diff --git a/.circleci/config.yml b/.circleci/config.yml index 2cf3d68..8680f3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,15 +7,29 @@ jobs: steps: - checkout - run: npx prettier . --check + # Install dependencies + - run: npm ci + # TODO : do we need caching for dependencies? + # Build the application + - run: npm run build # TODO: check that style.css matches compiled tailwind + # Store the build artifacts to be used in the deploy job + - persist_to_workspace: + root: . + paths: + - dist + - scripts deploy: docker: - # specify the version you desire here - image: cimg/node:20.8.1 working_directory: ~/repo steps: - checkout + # Attach the workspace with the build artifacts + - attach_workspace: + at: . + # Deploy using the script - run: ./scripts/deploy.sh workflows: diff --git a/.gitignore b/.gitignore index d9e5e60..c962116 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ pnpm-debug.log* lerna-debug.log* node_modules +dist +dist-ssr *.local # Editor directories and files diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 009ba8f..0000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -public/style.css \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..79a552e --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + }, +); diff --git a/index.html b/index.html new file mode 100644 index 0000000..f1507ac --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + Darklang + + + +
+ + + diff --git a/package-lock.json b/package-lock.json index 9bd7e86..1d90c12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,363 +1,3086 @@ { "name": "darklang.com", + "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "darklang.com", + "version": "0.0.0", "dependencies": { - "@tailwindcss/line-clamp": "^0.4.4" + "@tailwindcss/vite": "^4.1.4", + "highlight.js": "^11.11.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.5.0", + "tailwindcss": "^4.1.4" }, "devDependencies": { - "prettier": "3.0.3", - "tailwindcss": "^3.2.7" + "@eslint/js": "^9.21.0", + "@types/node": "^22.15.3", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "prettier": "3.5.3", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", + "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", + "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", + "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-x64": "4.1.4", + "@tailwindcss/oxide-freebsd-x64": "4.1.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-x64-musl": "4.1.4", + "@tailwindcss/oxide-wasm32-wasi": "4.1.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", + "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", + "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", + "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", + "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@emnapi/wasi-threads": "^1.0.1", + "@napi-rs/wasm-runtime": "^0.2.8", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz", + "integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.4", + "@tailwindcss/oxide": "4.1.4", + "tailwindcss": "4.1.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001713", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", + "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.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/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "ms": "^2.1.3" }, "engines": { - "node": ">= 8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/electron-to-chromium": { + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">= 8" + "node": ">=10.13.0" } }, - "node_modules/@tailwindcss/line-clamp": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", - "integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==", - "peerDependencies": { - "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" } }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, "bin": { - "acorn": "bin/acorn" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" } }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/anymatch": { + "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">= 8" + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 8.10.0" + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, + "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, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.12.0" } }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" + "argparse": "^2.0.1" }, "bin": { - "detective": "bin/detective.js" + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=0.8.0" + "node": ">=6" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=8.6.0" + "node": ">=6" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "json-buffer": "3.0.1" } }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", "dependencies": { - "reusify": "^1.0.4" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "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==", + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "license": "MPL-2.0", "dependencies": { - "to-regex-range": "^5.0.1" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10.13.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "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==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.12.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -366,6 +3089,8 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -374,24 +3099,37 @@ "node": ">=8.6" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -399,55 +3137,126 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -462,117 +3271,32 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -583,10 +3307,21 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -600,67 +3335,145 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", "dependencies": { - "pify": "^2.3.0" + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz", + "integrity": "sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g==", + "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/react-router-dom": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.0.tgz", + "integrity": "sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "react-router": "7.5.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=20.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -675,73 +3488,112 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tailwindcss": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", - "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", - "dependencies": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.0.9", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.9" + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" } }, "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, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -749,25 +3601,238 @@ "node": ">=8.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, "engines": { - "node": ">=0.4" + "node": ">= 0.8.0" } }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">= 6" + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", + "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index 3ab997f..7c98ad4 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,35 @@ { - "devDependencies": { - "prettier": "3.0.3", - "tailwindcss": "^3.2.7" - }, + "name": "darklang.com", + "private": true, + "version": "0.0.0", + "type": "module", "scripts": { - "dev": "npx tailwindcss -i ./src/input.css -o ./public/style.css --watch" + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { - "@tailwindcss/line-clamp": "^0.4.4" + "@tailwindcss/vite": "^4.1.4", + "highlight.js": "^11.11.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.5.0", + "tailwindcss": "^4.1.4" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/node": "^22.15.3", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "prettier": "3.5.3", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" } } diff --git a/public/classic.html b/public/classic.html deleted file mode 100644 index 9b55b28..0000000 --- a/public/classic.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - Darklang Classic - - - - - - - -
-
- - - - - - - - -

- #FreePalestine. - Read our founder's - statement -

-
- - - -
- - -
-
-
-
- - - - - - - - - - - - - - - -
-
-

- Winding Down Dark-Classic -

-

- Dark-Classic is winding down to a limited-user mode, starting on - July 16, 2025.
- Email us to keep your canvas running. -

-
-
- -
-
- - -
- logo -
- - -
-

- Dark is a new - way of building serverless backends. Just code your backend, with no - infra, framework or deployment nightmares. Build - APIs, - CRUD apps, - internal tools and - bots - whatever your backend - needs. -

- -

- The classic version of darklang is still accessible but is currently - in maintenance mode, with no ongoing development. Darklang-next is the - next iteration of Dark, applicable to both the cloud runtime and to - local scripts and CLIs -

-
- - -
- - - - - - Internal Tools & Bots - - - Dark is ideal for quickly building slackbots and automating internal - tools. Receive webhooks live, call out to 3rd party APIs, store - data, and schedule jobs - while building no infrastructure. - -

- Read more about using dark with Slack -

-
-
- - - - REST APIs & Webhooks - - - Set up an API endpoint quickly enough to use it as a proof of - concept during a call. Immediately see the data from a webhook to - your endpoint. Call an external API using the HttpClient library and - see responses within the editor, or use workers to do them in the - background. Use the built-in package manager to make external API - calls really easily, and contribute your own API integrations. - - - - - - CRUD apps - - - Get a working CRUD application in less than ten minutes by setting - up a few API endpoints and a datastore. Build out the backend for a - web or mobile app, whether a simple HTML form or an entire product. - - -
-
- - -
- AnyBackend That - Requires... -
- - - - API Endpoints - - - - - Data Stores - - - - - Background Workers - - - - - Scheduled Jobs - - - - -
-

- Dark lets you build any backend that needs API endpoints, data stores, - background workers, scheduled jobs, and calling HTTP APIs. You just - write the code in Dark, and we'll manage the rest. -

-
- -
- -

see it in action!

-
-
-
- - -
-

Access Darklang-classic

- -
- Log in -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/classic/signup.html b/public/classic/signup.html deleted file mode 100644 index b4e5d40..0000000 --- a/public/classic/signup.html +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - Signup - - - - - - - - - - -
-
- logo -

- Signups to Dark-Classic have been disabled, as we're - winding down - this service.
- If you already have an account, - log in. -

-
-
- - - - - - - - diff --git a/public/img/wordmark-light-transparent@2x.png b/public/img/wordmark-light-transparent@2x.png deleted file mode 100644 index 0a5aea1..0000000 Binary files a/public/img/wordmark-light-transparent@2x.png and /dev/null differ diff --git a/public/index.html b/public/index.html deleted file mode 100644 index bfb2de0..0000000 --- a/public/index.html +++ /dev/null @@ -1,1543 +0,0 @@ - - - - - - Darklang - - - - - - - - - - - - - - - - -
-
- - - - - - - - -

- #FreePalestine. - Read our founder's - statement -

-
- - - -
- - -
-
-
-
- - - - - - - - - - - - - - - -
-
-

- Winding Down Dark-Classic -

-

- Dark-Classic is winding down to a limited-user mode, starting on - July 16, 2025.
- Email us to keep your canvas running. -

-
-
- -
-
- - -
-
- logo -
- -
-
-

- Just - code -

-

- no cruft: no build systems, no - null, no exception handling, no ORMs, no OOP, no inheritence - hierarchies, no async/await, no compilation, no dev environments, - no dependency hell, no packaging, no git, no github, - - no devops: no yaml, no config - files, no docker, no containers, no kubernetes, no ci/cd - pipelines, no terraform, no orchestrating, - - no infrastructure: no sql, no - nosql, no connection poolers, no sharding, no indexes, no servers, - no serverless, no networking, no load balancers, no 200 cloud - services, no kafka, no memcached, no unix, no OSes -

-
- -
-
-

Send me project updates

-
- - - -
-
-
-
-
- - -
-

- Darklang puts everything in one box, so you can build CLIs and cloud - apps with no bullshit, - just - code. -

-
-

- ️️️Also it's a really enjoyable language to use! -

-
- - -
-
-
-

- Looking for Darklang-classic? -

- -
-
- dark-classic -
-
-
- -
-
-
- In development - here’s how things will look shortly -
-
- -
- -
-
-              $ curl https://darklang.com/download | bash 
-              
-              Darklang installed in ~/.darklang/bin/darklang
-              
-              Add to PATH via .bashrc [y, n, ?]: y 
-              Added to .bashrc. 
-
-              Next you can: # Try the tutorial darklang tutorial # Run some code from the package manager darklang @paul.fizzbuzz 3 # Generate some code darklang prompt "Find ts scripts with more than 600 lines which use the commonjs format"# See available command line optionsdarklang help
-              
-            
-
-
- - -
- -
-                
-                $ darklang @paul.fizzbuzz 3 
-                1
-                2 
-                Fizz
-                
-              
-
- - -
-                
-                $ darklang deploy @paul.fizzbuzz /fizzbuzz 
-                Deployed to https://furry-squirrel-3562.darklang.io/fizzbuzz in 0.135s
-                 
-              
-
- - -
-                
-                $ curl -sSO https://furry-squirrel-3562.darklang.io/fizzbuzz/3 
-                1 
-                2 
-                Fizz
-                
-              
-
-
-
- -
- - -
-
-                $ darklang prompt "Find ts scripts with more than 600 lines which use commonjs format" 
-
-                We need your AI credentials, which will be stored locally in ~/darklang/secrets/
-
-                [1] Login to use Darklang AI service 
-                [2] Enter GitHub copilot credentials 
-                [3] Enter OpenAI (GPT 3.5/4) credentials 
-                [4] Use local model 
-
-                Enter [1-4]: 1 
-
-                Login or register [L, r]? L
-                Username: paul
-                Password: **************
-
-                Logged in.
-
-                Saved script in ./find-large-ts-cjs.dark in 43.8s
-
-                
-
- - -
-
-
- dark-file-logo -
-
-

find-large-ts-cjs.dark

-
-
- -
-
let findLargeTypescriptCommonJSFiles (path : String) =
-  Directory.traverse (fun path -> 
-    if not (List.oneOf (File.extension path) [".ts", ".mjs", ".cjs"]) then 
-      print $"Skipping - wrong file type: {path}" 
-    else 
-      let contents = File.readString path 
-      let lines = String.splitNewlines contents
-      if (List.length lines) <= 600 then 
-        print $"Skipping - too short: {path}" 
-      else 
-        let isCommonjs = 
-          lines |> List.any (fun line ->
-            line |> Regex.matches "/const .* = require(.*)/" 
-          ) 
-        if isCommonjs then 
-          print $"Found one: {path}" 
-    )
-    
-findLargeTypescriptCommonJSFiles "./"
-
-
-
-
-
- - -
- - - Functional language - - - Simple types using Records and - Enums - - Dynamic languages are great, allowing great flexibility. But now - that they've matured and projects have gotten larger, static - typing has been layered on top of very dynamic languages, and the - intersection is not pleasant. Functional static languages like - Darklang have simple Record and Enum types that can model nearly - everything with much less complexity. - - - - - Abstract Data Types made of Record - and Enums can model nearly anything, like in Rust, Elm, OCaml - and F# -
-// Record
-type Url = {
-  scheme : HttpScheme
-  domain : String
-  port : UInt16
-  path : String
-  query : Option<String>
-}
-
-// Enum (aka Variant, Sum Type, or Abstract Data Types)
-type UrlError = 
-  | InvalidScheme(String)
-  | EmptyDomain
-  | InvalidPort(Int64)
-  | Unparseable(msg:String, context:String)
-
-// Aliases are just shorthands
-type UrlParseResult = Result<Url, UrlError>
-
-
-
- - - We believe Object Oriented programming is a terrible way to - model programs, and inheritence is a terrible misfeature. - Languages like OCaml, Elm, and F# have shown that nearly all - problems can be modeled using ADTs. - -
-
-
- - - Option and - Result types instead of null and - exceptions - - I think it's widely accepted that - nullis a mistake, and that an - Optiontype (aka - Optional, - Maybe, etc) makes it significantly - easier to program. - - - - - We further believe that Exceptions — which can in most - languages be thrown at any point — makes it very frustrating - to know that a function actually works. There are no - exceptions in Darklang, and we use - Result types to manage error cases - - - ? and - !operators (similar to Rust, - TypeScript, and Clojure) provide ways to ergonomically handle - errors without being too annoying (see also Gradual Static - Typing) - - - We will admit we have some RuntimeErrors which cannot be - caught, but we are working to remove them almost entirely from - the language (see Gradual Static Typing for what we're - keeping) - - - - - - - Garbage Collected - - It can be fun to satisfy the borrow checker, or manage allocations - individually, but it can also be fun to just get working programs - immediately. We believe that run-time garbage collection is one of - the greatest programming language features, and we're all in - - - - - - Unicode First - - Languages created last millennium typically use Strings made of - bytes, or worse, UTF16 characters, or even worse, Unicode - Codepoints! Like Swift, we believe that - Characters should represent screen - readable characters, like 👨‍👩‍👦‍👦. All characters in Darklang represent - Extended Grapheme Clusters: one - character that you see on screen. Naturally, working on Unicode - Codepoints and normal bytes is also well - supported. - - - -
-
- - - - Runs Instantly - - - - Instantly run any package from - the CLI: - - darklang - @username.functionName - arg1 - arg2 - - - - - Darklang has a new model of sharing programs. Any function in the - package manager can be called directly from the command line (so - long as we can figure out how to coerce the command line arguments - correctly) - - - -
$ darklang @paul.fizzbuzz 10
-1
-2
-Fizz
-4
-Buzz
-Fizz
-7
-8
-Fizz
-Buzz 
- - - We designed this to allow you to share scripts with your team, - or the whole world. We support private packages - , with individual, team, or role-based access - controls. You can even have functions that run - server-side - without allowing the user to see credentials, for example for - providing customer support tools to your team. - - - Functions can also be run from the web, Slack, Discord, etc - - - - We're building safety features, to ensure that functions - can't just steal all your IDs and wallets and whatever. - Darklang asks for permissions when you call a function, and a - static analysis is run to collect all permissions needed. -
$ darklang @hacker.stealThings "Hi there"
-> @hacker.stealThings requires the following permissions
->   Makes Http requests to unknown domains
->   Reads any file
->   Executes any file
-> Continue? [N, y] y
-> These are unusual permissions. Are you sure [N, y] y
-Ha ha, you're own3d
-
-$ darklang @mycompany.internal.createMonthlyReport
-> @mycompany.internal.createMonthlyReport requires the following
-> permissions
->   Makes Http GET requests to stripe.com/api/ETC
->   Makes Http POST requests to drive.google.com
-> Continue? [N, y] y
-Report initiated and stored at https://drive.google.com/u/asj599b3/5288942sdfsdf3.pdf
-
-
-
-
- - - - Instantly run programs as you - write them - - - Darklang is designed for a really fast iterative loop - - - - - Single binary to install means no environments or containers - to set up. - Install it now! - Coming early 2025 - - - Packages are streamed from the package manager automatically - – no - npm install step - - - darklang is interpreted — no compilation step required, - programs run immediately - -
    - - We plan to add background compilation in the future, - combined with pre-compiled packages in the package manager - - -
- - - Gradual Static Typing allows running programs with incorrect - types so you don't need to fix all the types in your program - while getting one path working - - - Generate darklang code automatically using LLMs and GitHub - Copilot - -
-
-
-
-
- - - - Next-gen package - management - - Darklang has a rather unique package manager, where functions and - types are individually versioned and immutable, taking a lot of the - hassle out of package management. - - - - Only download the specific package items you use - - - Only upgrade the specific package items you use - - - Automated dependency upgrades, as we track - deprecation status, and know what functions are pure and safe to - update. - - - Different packages can rely on different versions of other - packages - - - Use multiple versions of the same package item at once: allows - testing new versions without having to change an entire package - version, lowering risk. - - - Share pre-release functions trivially, without - contributors needing to check out your git repo or set up - anything - - - The package manager functions as a source - repository - - - no need for uploads, releases or other synchronization. No git - or GitHub required (but you can sync to GitHub if you prefer). - - - - - - - - - - Gradual Static Typing - - Gradual Static Typing allows running incomplete programs so you don't - need to ensure everything type checks when you're getting one path - working - - - - While prototyping, just run code until you hit a type error - - - After prototyping, run the full type checker to gain confidence - your whole program works - - - - Full type-checking hints in VSCode or in LSP editors - - - - ! and - ? operators allow easy error handling - while you prototype - - - Automatic refactoring converts - ! into proper error handling - - - - - - - - - - - Async runtime - - - - Fully asynchronous runtime - - - Darklang has a fully asynchronous runtime, so making a Http call - or reading a file doesn't block the runtime. - - - - - - No - async / - await - - - Adding - async and - await keywords to every language was a - mistake. It exposes the complexity of concurrency and - multi-threading to languages which were originally designed for - simplicity. - - - - - - Concurrent and - parallel execution via - data-dependencies - - -

- When you make an async request, it first waits for any arguments - that are async, and starts when they're done. If another - function call needs to use the result, it will wait for it - before starting. -

-

- Since darklang values are immutable, there won't be any race - conditions from this. -

-
-
- - - - Powerful escape hatches - - - We provide powerful escape hatches if you need async ordering that - doesn't match the data dependencies of your program. - - -
-
- - - - Instant cloud - deployment - - Optional - - Instant cloud deployment of code (to our cloud or yours), with instant - creation of DBs, API endpoints, and worker queues, with no containers, - no cold starts, no orchestration, or other devops/cloud engineering - required - - - - - - Designed for GenAI - - Works with GitHub Copilot - - We redesigned the dark language and tooling to enable - GenAI-generated programs, including exposing language tools to GenAI - tools, allowing running partial and incomplete programs safely, and - ensuring access to significant context to GenAI tools - - - - - Build short CLI programs from prompts - - darklang - prompt "find all js files - which don't have a CSS file of the same name" - - - - Use any LLM: darklang's fine-tuned models , local - OSS models, commerical models via API, or using GitHub Copilot - - - - - Build vendor SDKs from prompts and OpenAPI docs - - - - - Build complex programs with darklang AI agents - - - - - - - Works with your - Editor - - VSCode Extension - LSP support for other editors - - Works with GitHub Copilot and other GenAI tools - - - We mention this because Darklang classic required using our - editor, and that's no longer the case. - - - - - - - - Open source - - Darklang is - open source - . - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/signup.html b/public/signup.html deleted file mode 100644 index 2b896a2..0000000 --- a/public/signup.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Signup - - - diff --git a/public/style.css b/public/style.css deleted file mode 100644 index 347d808..0000000 --- a/public/style.css +++ /dev/null @@ -1,2185 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=League+Gothic&display=swap"); - -@import url("https://fonts.googleapis.com/css2?family=Assistant:wght@700&display=swap"); - -@import url("https://fonts.googleapis.com/css2?family=Quicksand:wght@400;700&display=swap"); - -@import url("https://fonts.googleapis.com/css2?family=Assistant:wght@700&family=Fira+Code:wght@400;500;600&display=swap"); - -/* -! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after{ - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop{ - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.container{ - width: 100%; -} - -@media (min-width: 640px){ - .container{ - max-width: 640px; - } -} - -@media (min-width: 768px){ - .container{ - max-width: 768px; - } -} - -@media (min-width: 1024px){ - .container{ - max-width: 1024px; - } -} - -@media (min-width: 1280px){ - .container{ - max-width: 1280px; - } -} - -@media (min-width: 1536px){ - .container{ - max-width: 1536px; - } -} - -.static{ - position: static; -} - -.absolute{ - position: absolute; -} - -.relative{ - position: relative; -} - -.sticky{ - position: sticky; -} - -.-inset-full{ - top: -100%; - right: -100%; - bottom: -100%; - left: -100%; -} - -.-bottom-7{ - bottom: -1.75rem; -} - -.left-0{ - left: 0px; -} - -.right-4{ - right: 1rem; -} - -.top-0{ - top: 0px; -} - -.z-10{ - z-index: 10; -} - -.z-50{ - z-index: 50; -} - -.m-0{ - margin: 0px; -} - -.m-10{ - margin: 2.5rem; -} - -.m-2{ - margin: 0.5rem; -} - -.m-3{ - margin: 0.75rem; -} - -.m-4{ - margin: 1rem; -} - -.mx-12{ - margin-left: 3rem; - margin-right: 3rem; -} - -.mx-2{ - margin-left: 0.5rem; - margin-right: 0.5rem; -} - -.mx-3{ - margin-left: 0.75rem; - margin-right: 0.75rem; -} - -.mx-4{ - margin-left: 1rem; - margin-right: 1rem; -} - -.mx-auto{ - margin-left: auto; - margin-right: auto; -} - -.my-10{ - margin-top: 2.5rem; - margin-bottom: 2.5rem; -} - -.my-2{ - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - -.my-20{ - margin-top: 5rem; - margin-bottom: 5rem; -} - -.my-3{ - margin-top: 0.75rem; - margin-bottom: 0.75rem; -} - -.my-4{ - margin-top: 1rem; - margin-bottom: 1rem; -} - -.-ml-3{ - margin-left: -0.75rem; -} - -.mb-1{ - margin-bottom: 0.25rem; -} - -.mb-12{ - margin-bottom: 3rem; -} - -.mb-16{ - margin-bottom: 4rem; -} - -.mb-2{ - margin-bottom: 0.5rem; -} - -.mb-24{ - margin-bottom: 6rem; -} - -.mb-4{ - margin-bottom: 1rem; -} - -.mb-5{ - margin-bottom: 1.25rem; -} - -.mb-8{ - margin-bottom: 2rem; -} - -.ml-2{ - margin-left: 0.5rem; -} - -.ml-4{ - margin-left: 1rem; -} - -.ml-5{ - margin-left: 1.25rem; -} - -.mr-1{ - margin-right: 0.25rem; -} - -.mr-2{ - margin-right: 0.5rem; -} - -.mr-3{ - margin-right: 0.75rem; -} - -.mt-1{ - margin-top: 0.25rem; -} - -.mt-10{ - margin-top: 2.5rem; -} - -.mt-11{ - margin-top: 2.75rem; -} - -.mt-12{ - margin-top: 3rem; -} - -.mt-14{ - margin-top: 3.5rem; -} - -.mt-2{ - margin-top: 0.5rem; -} - -.mt-20{ - margin-top: 5rem; -} - -.mt-4{ - margin-top: 1rem; -} - -.mt-6{ - margin-top: 1.5rem; -} - -.mt-8{ - margin-top: 2rem; -} - -.box-border{ - box-sizing: border-box; -} - -.block{ - display: block; -} - -.inline-block{ - display: inline-block; -} - -.inline{ - display: inline; -} - -.flex{ - display: flex; -} - -.inline-flex{ - display: inline-flex; -} - -.grid{ - display: grid; -} - -.contents{ - display: contents; -} - -.hidden{ - display: none; -} - -.h-12{ - height: 3rem; -} - -.h-14{ - height: 3.5rem; -} - -.h-3{ - height: 0.75rem; -} - -.h-4{ - height: 1rem; -} - -.h-5{ - height: 1.25rem; -} - -.h-6{ - height: 1.5rem; -} - -.h-64{ - height: 16rem; -} - -.h-8{ - height: 2rem; -} - -.h-full{ - height: 100%; -} - -.w-1\/2{ - width: 50%; -} - -.w-3{ - width: 0.75rem; -} - -.w-4{ - width: 1rem; -} - -.w-5{ - width: 1.25rem; -} - -.w-6{ - width: 1.5rem; -} - -.w-8{ - width: 2rem; -} - -.w-fit{ - width: -moz-fit-content; - width: fit-content; -} - -.w-full{ - width: 100%; -} - -.max-w-6xl{ - max-width: 72rem; -} - -.max-w-xs{ - max-width: 20rem; -} - -.flex-1{ - flex: 1 1 0%; -} - -.rotate-180{ - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.-skew-x-12{ - --tw-skew-x: -12deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform{ - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -@keyframes ping{ - 75%, 100%{ - transform: scale(2); - opacity: 0; - } -} - -.animate-ping{ - animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; -} - -.cursor-pointer{ - cursor: pointer; -} - -.grid-cols-2{ - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.flex-row{ - flex-direction: row; -} - -.flex-col{ - flex-direction: column; -} - -.flex-wrap{ - flex-wrap: wrap; -} - -.items-center{ - align-items: center; -} - -.justify-center{ - justify-content: center; -} - -.justify-between{ - justify-content: space-between; -} - -.gap-3{ - gap: 0.75rem; -} - -.gap-y-2{ - row-gap: 0.5rem; -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]){ - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.overflow-hidden{ - overflow: hidden; -} - -.whitespace-nowrap{ - white-space: nowrap; -} - -.whitespace-pre-line{ - white-space: pre-line; -} - -.rounded{ - border-radius: 0.25rem; -} - -.rounded-2xl{ - border-radius: 1rem; -} - -.rounded-full{ - border-radius: 9999px; -} - -.rounded-lg{ - border-radius: 0.5rem; -} - -.rounded-md{ - border-radius: 0.375rem; -} - -.rounded-xl{ - border-radius: 0.75rem; -} - -.rounded-b-lg{ - border-bottom-right-radius: 0.5rem; - border-bottom-left-radius: 0.5rem; -} - -.border{ - border-width: 1px; -} - -.border-b{ - border-bottom-width: 1px; -} - -.border-l-4{ - border-left-width: 4px; -} - -.border-blue{ - --tw-border-opacity: 1; - border-color: rgb(116 122 185 / var(--tw-border-opacity)); -} - -.border-classic-purple{ - --tw-border-opacity: 1; - border-color: rgb(178 120 255 / var(--tw-border-opacity)); -} - -.border-gray-300{ - --tw-border-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-border-opacity)); -} - -.border-white\/20{ - border-color: rgb(255 255 255 / 0.2); -} - -.border-yellow-500{ - --tw-border-opacity: 1; - border-color: rgb(234 179 8 / var(--tw-border-opacity)); -} - -.border-b-blue\/25{ - border-bottom-color: rgb(116 122 185 / 0.25); -} - -.border-b-gray-600{ - --tw-border-opacity: 1; - border-bottom-color: rgb(75 85 99 / var(--tw-border-opacity)); -} - -.border-b-white\/10{ - border-bottom-color: rgb(255 255 255 / 0.1); -} - -.border-b-white\/20{ - border-bottom-color: rgb(255 255 255 / 0.2); -} - -.\!bg-code-background{ - --tw-bg-opacity: 1 !important; - background-color: rgb(245 244 241 / var(--tw-bg-opacity)) !important; -} - -.\!bg-dark-gray{ - --tw-bg-opacity: 1 !important; - background-color: rgb(47 47 47 / var(--tw-bg-opacity)) !important; -} - -.bg-\[\#19181D\]{ - --tw-bg-opacity: 1; - background-color: rgb(25 24 29 / var(--tw-bg-opacity)); -} - -.bg-\[\#1c1c1c\]{ - --tw-bg-opacity: 1; - background-color: rgb(28 28 28 / var(--tw-bg-opacity)); -} - -.bg-\[\#5FDD68\]{ - --tw-bg-opacity: 1; - background-color: rgb(95 221 104 / var(--tw-bg-opacity)); -} - -.bg-\[\#DD333F\]{ - --tw-bg-opacity: 1; - background-color: rgb(221 51 63 / var(--tw-bg-opacity)); -} - -.bg-\[\#E3D3FF\]{ - --tw-bg-opacity: 1; - background-color: rgb(227 211 255 / var(--tw-bg-opacity)); -} - -.bg-\[\#F5D76E\]{ - --tw-bg-opacity: 1; - background-color: rgb(245 215 110 / var(--tw-bg-opacity)); -} - -.bg-blue{ - --tw-bg-opacity: 1; - background-color: rgb(116 122 185 / var(--tw-bg-opacity)); -} - -.bg-classic-purple{ - --tw-bg-opacity: 1; - background-color: rgb(178 120 255 / var(--tw-bg-opacity)); -} - -.bg-cli-background{ - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity)); -} - -.bg-code-background{ - --tw-bg-opacity: 1; - background-color: rgb(245 244 241 / var(--tw-bg-opacity)); -} - -.bg-dark-gray{ - --tw-bg-opacity: 1; - background-color: rgb(47 47 47 / var(--tw-bg-opacity)); -} - -.bg-light-background{ - --tw-bg-opacity: 1; - background-color: rgb(250 250 250 / var(--tw-bg-opacity)); -} - -.bg-purple{ - --tw-bg-opacity: 1; - background-color: rgb(149 91 159 / var(--tw-bg-opacity)); -} - -.bg-white{ - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.bg-white\/70{ - background-color: rgb(255 255 255 / 0.7); -} - -.bg-gradient-to-r{ - background-image: linear-gradient(to right, var(--tw-gradient-stops)); -} - -.from-classic-purple{ - --tw-gradient-from: #B278FF; - --tw-gradient-to: rgb(178 120 255 / 0); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.from-purple{ - --tw-gradient-from: #955B9F; - --tw-gradient-to: rgb(149 91 159 / 0); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.from-transparent{ - --tw-gradient-from: transparent; - --tw-gradient-to: rgb(0 0 0 / 0); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.via-classic-purple\/80{ - --tw-gradient-to: rgb(178 120 255 / 0); - --tw-gradient-stops: var(--tw-gradient-from), rgb(178 120 255 / 0.8), var(--tw-gradient-to); -} - -.to-blue{ - --tw-gradient-to: #747AB9; -} - -.to-classic-pink\/80{ - --tw-gradient-to: rgb(245 111 240 / 0.8); -} - -.to-white{ - --tw-gradient-to: #fff; -} - -.bg-clip-text{ - -webkit-background-clip: text; - background-clip: text; -} - -.fill-current{ - fill: currentColor; -} - -.p-0{ - padding: 0px; -} - -.p-0\.5{ - padding: 0.125rem; -} - -.p-2{ - padding: 0.5rem; -} - -.p-4{ - padding: 1rem; -} - -.p-5{ - padding: 1.25rem; -} - -.p-6{ - padding: 1.5rem; -} - -.p-8{ - padding: 2rem; -} - -.px-1{ - padding-left: 0.25rem; - padding-right: 0.25rem; -} - -.px-1\.5{ - padding-left: 0.375rem; - padding-right: 0.375rem; -} - -.px-2{ - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.px-3{ - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-4{ - padding-left: 1rem; - padding-right: 1rem; -} - -.px-5{ - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.px-6{ - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.px-8{ - padding-left: 2rem; - padding-right: 2rem; -} - -.py-0{ - padding-top: 0px; - padding-bottom: 0px; -} - -.py-0\.5{ - padding-top: 0.125rem; - padding-bottom: 0.125rem; -} - -.py-1{ - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2{ - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-3{ - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - -.py-4{ - padding-top: 1rem; - padding-bottom: 1rem; -} - -.py-6{ - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.py-8{ - padding-top: 2rem; - padding-bottom: 2rem; -} - -.pb-11{ - padding-bottom: 2.75rem; -} - -.pb-4{ - padding-bottom: 1rem; -} - -.pl-11{ - padding-left: 2.75rem; -} - -.pl-3{ - padding-left: 0.75rem; -} - -.pr-14{ - padding-right: 3.5rem; -} - -.pr-2{ - padding-right: 0.5rem; -} - -.pr-4{ - padding-right: 1rem; -} - -.pt-1{ - padding-top: 0.25rem; -} - -.text-left{ - text-align: left; -} - -.text-center{ - text-align: center; -} - -.text-justify{ - text-align: justify; -} - -.font-FiraCode{ - font-family: Fira Code, monospace; -} - -.font-Quicksand{ - font-family: Quicksand, sans-serif; -} - -.font-body{ - font-family: Inter, sans-serif; -} - -.text-2xl{ - font-size: 1.5rem; - line-height: 2rem; -} - -.text-3xl{ - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-4xl{ - font-size: 2.25rem; - line-height: 2.5rem; -} - -.text-base{ - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg{ - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm{ - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl{ - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-xs{ - font-size: 0.75rem; - line-height: 1rem; -} - -.font-bold{ - font-weight: 700; -} - -.font-medium{ - font-weight: 500; -} - -.font-normal{ - font-weight: 400; -} - -.font-semibold{ - font-weight: 600; -} - -.italic{ - font-style: italic; -} - -.leading-6{ - line-height: 1.5rem; -} - -.\!text-gray-600{ - --tw-text-opacity: 1 !important; - color: rgb(75 85 99 / var(--tw-text-opacity)) !important; -} - -.\!text-white{ - --tw-text-opacity: 1 !important; - color: rgb(255 255 255 / var(--tw-text-opacity)) !important; -} - -.text-\[\#02A5F1\]{ - --tw-text-opacity: 1; - color: rgb(2 165 241 / var(--tw-text-opacity)); -} - -.text-\[\#949494\]{ - --tw-text-opacity: 1; - color: rgb(148 148 148 / var(--tw-text-opacity)); -} - -.text-\[\#DCDCDC\]{ - --tw-text-opacity: 1; - color: rgb(220 220 220 / var(--tw-text-opacity)); -} - -.text-\[\#a1b56c\]{ - --tw-text-opacity: 1; - color: rgb(161 181 108 / var(--tw-text-opacity)); -} - -.text-black{ - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); -} - -.text-blue{ - --tw-text-opacity: 1; - color: rgb(116 122 185 / var(--tw-text-opacity)); -} - -.text-classic-blue{ - --tw-text-opacity: 1; - color: rgb(134 193 185 / var(--tw-text-opacity)); -} - -.text-classic-brown{ - --tw-text-opacity: 1; - color: rgb(161 136 127 / var(--tw-text-opacity)); -} - -.text-classic-green{ - --tw-text-opacity: 1; - color: rgb(161 181 108 / var(--tw-text-opacity)); -} - -.text-classic-purple{ - --tw-text-opacity: 1; - color: rgb(178 120 255 / var(--tw-text-opacity)); -} - -.text-classic-yellow{ - --tw-text-opacity: 1; - color: rgb(230 189 129 / var(--tw-text-opacity)); -} - -.text-dark-gray{ - --tw-text-opacity: 1; - color: rgb(47 47 47 / var(--tw-text-opacity)); -} - -.text-dark-gray\/50{ - color: rgb(47 47 47 / 0.5); -} - -.text-gray-300{ - --tw-text-opacity: 1; - color: rgb(209 213 219 / var(--tw-text-opacity)); -} - -.text-gray-400{ - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-gray-500{ - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); -} - -.text-gray-600{ - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity)); -} - -.text-gray-800{ - --tw-text-opacity: 1; - color: rgb(31 41 55 / var(--tw-text-opacity)); -} - -.text-lime-400{ - --tw-text-opacity: 1; - color: rgb(163 230 53 / var(--tw-text-opacity)); -} - -.text-purple{ - --tw-text-opacity: 1; - color: rgb(149 91 159 / var(--tw-text-opacity)); -} - -.text-red-400{ - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity)); -} - -.text-transparent{ - color: transparent; -} - -.text-white{ - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.underline{ - text-decoration-line: underline; -} - -.line-through{ - text-decoration-line: line-through; -} - -.underline-offset-4{ - text-underline-offset: 4px; -} - -.opacity-40{ - opacity: 0.4; -} - -.opacity-75{ - opacity: 0.75; -} - -.shadow-2xl{ - --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); - --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-md{ - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.blur-lg{ - --tw-blur: blur(16px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.drop-shadow-md{ - --tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.transition{ - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-all{ - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-transform{ - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-200{ - transition-duration: 200ms; -} - -.duration-300{ - transition-duration: 300ms; -} - -.ease-in-out{ - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -.line-clamp-3{ - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; -} - -.hljs-title.hljs-title{ - --tw-text-opacity: 1; - color: rgb(153 27 27 / var(--tw-text-opacity)); -} - -@media (min-width: 768px){ - .md\:container{ - width: 100%; - } - - @media (min-width: 640px){ - .md\:container{ - max-width: 640px; - } - } - - @media (min-width: 768px){ - .md\:container{ - max-width: 768px; - } - } - - @media (min-width: 1024px){ - .md\:container{ - max-width: 1024px; - } - } - - @media (min-width: 1280px){ - .md\:container{ - max-width: 1280px; - } - } - - @media (min-width: 1536px){ - .md\:container{ - max-width: 1536px; - } - } -} - -@media (min-width: 1536px){ - .\32xl\:container{ - width: 100%; - } - - @media (min-width: 640px){ - .\32xl\:container{ - max-width: 640px; - } - } - - @media (min-width: 768px){ - .\32xl\:container{ - max-width: 768px; - } - } - - @media (min-width: 1024px){ - .\32xl\:container{ - max-width: 1024px; - } - } - - @media (min-width: 1280px){ - .\32xl\:container{ - max-width: 1280px; - } - } - - @media (min-width: 1536px){ - .\32xl\:container{ - max-width: 1536px; - } - } -} - -.hover\:-translate-y-1:hover{ - --tw-translate-y: -0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.hover\:bg-blue:hover{ - --tw-bg-opacity: 1; - background-color: rgb(116 122 185 / var(--tw-bg-opacity)); -} - -.hover\:bg-classic-purple:hover{ - --tw-bg-opacity: 1; - background-color: rgb(178 120 255 / var(--tw-bg-opacity)); -} - -.hover\:bg-purple:hover{ - --tw-bg-opacity: 1; - background-color: rgb(149 91 159 / var(--tw-bg-opacity)); -} - -.hover\:text-blue:hover{ - --tw-text-opacity: 1; - color: rgb(116 122 185 / var(--tw-text-opacity)); -} - -.hover\:text-classic-purple:hover{ - --tw-text-opacity: 1; - color: rgb(178 120 255 / var(--tw-text-opacity)); -} - -.hover\:text-purple:hover{ - --tw-text-opacity: 1; - color: rgb(149 91 159 / var(--tw-text-opacity)); -} - -.hover\:text-white:hover{ - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.hover\:underline:hover{ - text-decoration-line: underline; -} - -.focus\:ring-white:focus{ - --tw-ring-opacity: 1; - --tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity)); -} - -.group:hover .group-hover\:inset-1{ - top: 0.25rem; - right: 0.25rem; - bottom: 0.25rem; - left: 0.25rem; -} - -.group:hover .group-hover\:translate-x-1{ - --tw-translate-x: 0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -@keyframes shine{ - 100%{ - left: 125%; - } -} - -.group:hover .group-hover\:animate-shine{ - animation: shine 1s linear; -} - -.group:hover .group-hover\:opacity-75{ - opacity: 0.75; -} - -.group:hover .group-hover\:duration-200{ - transition-duration: 200ms; -} - -@media (min-width: 640px){ - .sm\:h-72{ - height: 18rem; - } -} - -@media (min-width: 768px){ - .md\:order-2{ - order: 2; - } - - .md\:m-0{ - margin: 0px; - } - - .md\:mx-0{ - margin-left: 0px; - margin-right: 0px; - } - - .md\:mx-2{ - margin-left: 0.5rem; - margin-right: 0.5rem; - } - - .md\:mx-32{ - margin-left: 8rem; - margin-right: 8rem; - } - - .md\:mx-auto{ - margin-left: auto; - margin-right: auto; - } - - .md\:mb-40{ - margin-bottom: 10rem; - } - - .md\:mr-0{ - margin-right: 0px; - } - - .md\:mr-6{ - margin-right: 1.5rem; - } - - .md\:mt-0{ - margin-top: 0px; - } - - .md\:mt-16{ - margin-top: 4rem; - } - - .md\:mt-28{ - margin-top: 7rem; - } - - .md\:mt-4{ - margin-top: 1rem; - } - - .md\:mt-44{ - margin-top: 11rem; - } - - .md\:mt-6{ - margin-top: 1.5rem; - } - - .md\:inline-block{ - display: inline-block; - } - - .md\:flex{ - display: flex; - } - - .md\:hidden{ - display: none; - } - - .md\:h-\[230px\]{ - height: 230px; - } - - .md\:w-1\/4{ - width: 25%; - } - - .md\:w-96{ - width: 24rem; - } - - .md\:w-\[700px\]{ - width: 700px; - } - - .md\:w-auto{ - width: auto; - } - - .md\:w-max{ - width: -moz-max-content; - width: max-content; - } - - .md\:max-w-2xl{ - max-width: 42rem; - } - - .md\:max-w-sm{ - max-width: 24rem; - } - - .md\:max-w-xl{ - max-width: 36rem; - } - - .md\:flex-row{ - flex-direction: row; - } - - .md\:flex-col{ - flex-direction: column; - } - - .md\:items-start{ - align-items: flex-start; - } - - .md\:items-center{ - align-items: center; - } - - .md\:justify-start{ - justify-content: flex-start; - } - - .md\:justify-end{ - justify-content: flex-end; - } - - .md\:justify-between{ - justify-content: space-between; - } - - .md\:justify-around{ - justify-content: space-around; - } - - .md\:space-x-8 > :not([hidden]) ~ :not([hidden]){ - --tw-space-x-reverse: 0; - margin-right: calc(2rem * var(--tw-space-x-reverse)); - margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); - } - - .md\:space-y-0 > :not([hidden]) ~ :not([hidden]){ - --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); - } - - .md\:rounded-2xl{ - border-radius: 1rem; - } - - .md\:border-0{ - border-width: 0px; - } - - .md\:border-none{ - border-style: none; - } - - .md\:bg-transparent{ - background-color: transparent; - } - - .md\:p-0{ - padding: 0px; - } - - .md\:p-8{ - padding: 2rem; - } - - .md\:px-16{ - padding-left: 4rem; - padding-right: 4rem; - } - - .md\:px-4{ - padding-left: 1rem; - padding-right: 1rem; - } - - .md\:px-8{ - padding-left: 2rem; - padding-right: 2rem; - } - - .md\:py-2{ - padding-top: 0.5rem; - padding-bottom: 0.5rem; - } - - .md\:pl-20{ - padding-left: 5rem; - } - - .md\:pr-7{ - padding-right: 1.75rem; - } - - .md\:pt-4{ - padding-top: 1rem; - } - - .md\:text-left{ - text-align: left; - } - - .md\:text-2xl{ - font-size: 1.5rem; - line-height: 2rem; - } - - .md\:text-4xl{ - font-size: 2.25rem; - line-height: 2.5rem; - } - - .md\:text-base{ - font-size: 1rem; - line-height: 1.5rem; - } - - .md\:text-lg{ - font-size: 1.125rem; - line-height: 1.75rem; - } - - .md\:text-sm{ - font-size: 0.875rem; - line-height: 1.25rem; - } - - .md\:text-xl{ - font-size: 1.25rem; - line-height: 1.75rem; - } -} - -@media (min-width: 1024px){ - .lg\:mx-28{ - margin-left: 7rem; - margin-right: 7rem; - } - - .lg\:mx-44{ - margin-left: 11rem; - margin-right: 11rem; - } - - .lg\:my-10{ - margin-top: 2.5rem; - margin-bottom: 2.5rem; - } - - .lg\:flex{ - display: flex; - } - - .lg\:grid-cols-1{ - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .lg\:grid-cols-2{ - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .lg\:grid-cols-4{ - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .lg\:rounded-lg{ - border-radius: 0.5rem; - } - - .lg\:p-6{ - padding: 1.5rem; - } - - .lg\:py-6{ - padding-top: 1.5rem; - padding-bottom: 1.5rem; - } - - .lg\:pt-6{ - padding-top: 1.5rem; - } - - .lg\:text-3xl{ - font-size: 1.875rem; - line-height: 2.25rem; - } - - .lg\:text-base{ - font-size: 1rem; - line-height: 1.5rem; - } -} - -@media (min-width: 1280px){ - .xl\:max-w-lg{ - max-width: 32rem; - } - - .xl\:flex-row{ - flex-direction: row; - } - - .xl\:text-\[28px\]{ - font-size: 28px; - } - - .xl\:text-xl{ - font-size: 1.25rem; - line-height: 1.75rem; - } -} - -@media (min-width: 1536px){ - .\32xl\:mx-44{ - margin-left: 11rem; - margin-right: 11rem; - } - - .\32xl\:mb-32{ - margin-bottom: 8rem; - } - - .\32xl\:mt-14{ - margin-top: 3.5rem; - } - - .\32xl\:mt-16{ - margin-top: 4rem; - } - - .\32xl\:mt-20{ - margin-top: 5rem; - } - - .\32xl\:mt-40{ - margin-top: 10rem; - } - - .\32xl\:max-w-3xl{ - max-width: 48rem; - } - - .\32xl\:max-w-4xl{ - max-width: 56rem; - } - - .\32xl\:max-w-screen-xl{ - max-width: 1280px; - } - - .\32xl\:max-w-xl{ - max-width: 36rem; - } - - .\32xl\:justify-center{ - justify-content: center; - } - - .\32xl\:p-0{ - padding: 0px; - } - - .\32xl\:text-2xl{ - font-size: 1.5rem; - line-height: 2rem; - } - - .\32xl\:text-4xl{ - font-size: 2.25rem; - line-height: 2.5rem; - } - - .\32xl\:text-base{ - font-size: 1rem; - line-height: 1.5rem; - } - - .\32xl\:text-lg{ - font-size: 1.125rem; - line-height: 1.75rem; - } - - .\32xl\:text-xl{ - font-size: 1.25rem; - line-height: 1.75rem; - } -} diff --git a/scripts/deploy.sh b/scripts/deploy.sh index abd76df..b2bb5ac 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -9,5 +9,4 @@ if [[ ! -f scripts/dark-cli-linux ]]; then fi -./scripts/dark-cli-linux public/ --canvas "${CANVAS}" --user "${USER}" --password "${PASSWORD}" - +./scripts/dark-cli-linux dist/ --canvas "${CANVAS}" --user "${USER}" --password "${PASSWORD}" diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..8f6b437 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,67 @@ +import { BrowserRouter, Routes, Route } from "react-router-dom"; + +import ScrollToTop from "./common/utils/ScrollToTop"; +import Layout from "./common/layout/Layout"; + +import Home from "./pages/Home"; + +import Classic from "./pages/Classic"; + +import Language from "./pages/Language" +import EditingPAge from "./pages/Editing"; +import CLI from "./pages/CLI"; +import Backends from "./pages/Backends"; +import AI from "./pages/AI" +import Distribution from "./pages/Distribution" +import Execution from "./pages/Execution" +import TraceDriven from "./pages/TraceDriven" +import TypeChecking from "./pages/TypeChecking" + +import Roadmap from "./pages/Roadmap"; +import Company from "./pages/Company"; +import GettingStarted from "./pages/GettingStarted"; +import ForX from "./pages/For" +import Cloud from "./pages/Cloud"; +// import Packages from "./pages/Packages"; +// import PackageDetail from "./pages/PackageDetail"; + +import NotFound from "./pages/NotFound"; +import Editing from "./pages/Home/Editing"; + +function App() { + return ( + + + + }> + } /> + + } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + } /> + {/* } /> */} + {/* } /> */} + + } /> + + + + ); +} + +export default App; diff --git a/src/assets/Cole_Thomas_The_Oxbow_-The_Connecticut_River_near_Northampton_1836-.jpeg b/src/assets/Cole_Thomas_The_Oxbow_-The_Connecticut_River_near_Northampton_1836-.jpeg new file mode 100644 index 0000000..ec7c055 Binary files /dev/null and b/src/assets/Cole_Thomas_The_Oxbow_-The_Connecticut_River_near_Northampton_1836-.jpeg differ diff --git a/src/assets/The_School_of_Athens__by_Raffaello_Sanzio_da_Urbino.jpeg b/src/assets/The_School_of_Athens__by_Raffaello_Sanzio_da_Urbino.jpeg new file mode 100644 index 0000000..f5c79d5 Binary files /dev/null and b/src/assets/The_School_of_Athens__by_Raffaello_Sanzio_da_Urbino.jpeg differ diff --git a/src/assets/Velazquez-The_Surrender_of_Breda.jpeg b/src/assets/Velazquez-The_Surrender_of_Breda.jpeg new file mode 100644 index 0000000..2d81d77 Binary files /dev/null and b/src/assets/Velazquez-The_Surrender_of_Breda.jpeg differ diff --git a/src/assets/cli.png b/src/assets/cli.png new file mode 100644 index 0000000..098fe7a Binary files /dev/null and b/src/assets/cli.png differ diff --git a/public/img/dark-classic.png b/src/assets/dark-classic-toplevel.png similarity index 100% rename from public/img/dark-classic.png rename to src/assets/dark-classic-toplevel.png diff --git a/public/img/darklang-classic.png b/src/assets/darklang-classic.png similarity index 100% rename from public/img/darklang-classic.png rename to src/assets/darklang-classic.png diff --git a/src/assets/darklang-cli-ascii.png b/src/assets/darklang-cli-ascii.png new file mode 100644 index 0000000..d178412 Binary files /dev/null and b/src/assets/darklang-cli-ascii.png differ diff --git a/public/img/wordmark-dark-transparent.png b/src/assets/darklang-logo-dbg.png similarity index 100% rename from public/img/wordmark-dark-transparent.png rename to src/assets/darklang-logo-dbg.png diff --git a/src/assets/darklang-logo-framed.png b/src/assets/darklang-logo-framed.png new file mode 100644 index 0000000..60ec9d7 Binary files /dev/null and b/src/assets/darklang-logo-framed.png differ diff --git a/src/assets/darklang-logo.png b/src/assets/darklang-logo.png new file mode 100644 index 0000000..3324e60 Binary files /dev/null and b/src/assets/darklang-logo.png differ diff --git a/src/assets/double-grid-square.png b/src/assets/double-grid-square.png new file mode 100644 index 0000000..d6ee1d3 Binary files /dev/null and b/src/assets/double-grid-square.png differ diff --git a/src/assets/double-grid.png b/src/assets/double-grid.png new file mode 100644 index 0000000..67af968 Binary files /dev/null and b/src/assets/double-grid.png differ diff --git a/src/assets/editing2.png b/src/assets/editing2.png new file mode 100644 index 0000000..0969604 Binary files /dev/null and b/src/assets/editing2.png differ diff --git a/src/assets/editing3.png b/src/assets/editing3.png new file mode 100644 index 0000000..465e0be Binary files /dev/null and b/src/assets/editing3.png differ diff --git a/src/assets/github-logo-white.png b/src/assets/github-logo-white.png new file mode 100644 index 0000000..fe7e299 Binary files /dev/null and b/src/assets/github-logo-white.png differ diff --git a/src/assets/github-logo.png b/src/assets/github-logo.png new file mode 100644 index 0000000..9490ffc Binary files /dev/null and b/src/assets/github-logo.png differ diff --git a/src/assets/grid.png b/src/assets/grid.png new file mode 100644 index 0000000..362e7e5 Binary files /dev/null and b/src/assets/grid.png differ diff --git a/src/assets/logo-ascii.png b/src/assets/logo-ascii.png new file mode 100644 index 0000000..ae130e0 Binary files /dev/null and b/src/assets/logo-ascii.png differ diff --git a/public/img/logo-dark-transparent.png b/src/assets/logo-dark-transparent.png similarity index 100% rename from public/img/logo-dark-transparent.png rename to src/assets/logo-dark-transparent.png diff --git a/src/assets/packageManager.png b/src/assets/packageManager.png new file mode 100644 index 0000000..f315286 Binary files /dev/null and b/src/assets/packageManager.png differ diff --git a/src/common/Banner.tsx b/src/common/Banner.tsx new file mode 100644 index 0000000..d643dad --- /dev/null +++ b/src/common/Banner.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +interface BannerProps { + text: string; +} + +const Banner: React.FC = ({ text }) => { + return ( +
+ + + + + {text}.{" "} + + See our roadmap → + + +
+ ); +}; + +export default Banner; diff --git a/src/common/layout/Footer.tsx b/src/common/layout/Footer.tsx new file mode 100644 index 0000000..cf9cf4b --- /dev/null +++ b/src/common/layout/Footer.tsx @@ -0,0 +1,308 @@ +import { Link } from "react-router-dom"; + +import darkLangLogo from "~/assets/darklang-logo.png"; +import darkLangLogoDark from "~/assets/darklang-logo-dbg.png"; + +// Define valid page types +type PageName = + | "home" + | "classic" + | "roadmap" + | "cli" + | "editing" + | string; + +interface FooterProps { + currentPage: PageName; +} + +const Footer = ({ currentPage }: FooterProps) => { + // Define background colors for different pages + const bgColors: Record = { + home: "bg-white", + "classic": "bg-dark text-white", + roadmap: "bg-white", + cli: "bg-dark text-white", + editing: "bg-dark text-white", + packages: "bg-dark text-white", + // Default for any other page (like NotFound) + default: "bg-white", + }; + + // Get the color for the current page, or use default if not defined + // any page starting with "packages" should use dark background + const bgColor = currentPage.startsWith('packages') + ? bgColors.packages + : (bgColors[currentPage] || bgColors.default); + // Determine if we're using a dark background + const isDarkBg = bgColor.includes("bg-dark"); + + const logoSrc = isDarkBg ? darkLangLogoDark : darkLangLogo; + + return ( + + ); +}; + +export default Footer; diff --git a/src/common/layout/Header.tsx b/src/common/layout/Header.tsx new file mode 100644 index 0000000..2b203fa --- /dev/null +++ b/src/common/layout/Header.tsx @@ -0,0 +1,407 @@ +/** + * Header Component + */ + +import { Link } from "react-router-dom"; +import { useState } from "react"; +import darkLangLogo from "~/assets/darklang-logo.png"; +import darkLangLogoDark from "~/assets/darklang-logo-dbg.png"; +import githubLogo from "~/assets/github-logo.png"; +import githubLogoWhite from "~/assets/github-logo-white.png"; +import Dropdown from "../ui/Dropdown"; + +type PageName = "home" | "classic" | "roadmap" | "cli" | string; + +interface HeaderProps { + currentPage: PageName; +} + +const Header = ({ currentPage }: HeaderProps) => { + // Define background colors for different pages + const bgColors: Record = { + home: "bg-white", + "classic": "bg-dark text-white", + roadmap: "bg-white", + cli: "bg-dark text-white", + editing: "bg-dark text-white", + packages: "bg-dark text-white", + // Default for any other page (like NotFound) + default: "bg-white", + }; + + // Get the color for the current page, or use default if not defined + // any page starting with "packages" should use dark background + const bgColor = currentPage.startsWith('packages') + ? bgColors.packages + : (bgColors[currentPage] || bgColors.default); + + const isDarkBg = bgColor.includes("bg-dark"); + + const logoSrc = isDarkBg ? darkLangLogoDark : darkLangLogo; + const githubLogoSrc = isDarkBg ? githubLogoWhite : githubLogo; + + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + + const toggleMobileMenu = () => { + setIsMobileMenuOpen(!isMobileMenuOpen); + }; + + return ( +
+
+ + Darklang Logo + + +
+ + + + + + + GitHub + + + {currentPage === "classic" ? ( + + Log in + + ) : ( + <> +
+ + + )} +
+ {/* Burger Menu Button (mobile only) */} + +
+ + {/* Mobile Menu */} + {isMobileMenuOpen && ( +
+ +
+ )} +
+ ); +}; + +export default Header; diff --git a/src/common/layout/Layout.tsx b/src/common/layout/Layout.tsx new file mode 100644 index 0000000..0c00860 --- /dev/null +++ b/src/common/layout/Layout.tsx @@ -0,0 +1,33 @@ +import { Outlet, useLocation } from "react-router-dom"; + +import Header from "./Header"; +import Footer from "./Footer"; +import Banner from "../Banner"; + +const Layout = () => { + // Get current location to determine the active page + const location = useLocation(); + const currentPage = location.pathname.substring(1) || "home"; + + const bgClass = (currentPage === "cli" || currentPage === "packages") ? "bg-dark" : "bg-transparent"; + + return ( +
+
+ { + currentPage.startsWith('classic') || currentPage.startsWith('packages/') + ? <> + : + } + + {!currentPage.startsWith('packages/') &&
} +
+
+ +
+ {!currentPage.startsWith('packages/') &&
} +
+ ); +}; + +export default Layout; diff --git a/src/common/ui/BlogPostCard/BlogPostCard.tsx b/src/common/ui/BlogPostCard/BlogPostCard.tsx new file mode 100644 index 0000000..fd3d131 --- /dev/null +++ b/src/common/ui/BlogPostCard/BlogPostCard.tsx @@ -0,0 +1,45 @@ +import React from "react"; + +interface BlogPostCardProps { + author: string; + date: string; + title: string; + excerpt: string; + imageUrl: string; + slug: string; +} + +export const BlogPostCard: React.FC = ({ + author, + date, + title, + excerpt, + imageUrl, + slug, +}) => { + return ( +
+ +
+
+ By {author} + {date} +
+

{title}

+

+ {excerpt} +

+
+
+ {title} +
+
+
+ ); +}; + +export default BlogPostCard; diff --git a/src/common/ui/BlogPostCard/index.ts b/src/common/ui/BlogPostCard/index.ts new file mode 100644 index 0000000..9c69e81 --- /dev/null +++ b/src/common/ui/BlogPostCard/index.ts @@ -0,0 +1,2 @@ +export { default } from "./BlogPostCard"; +export * from "./BlogPostCard"; diff --git a/src/common/ui/Button.tsx b/src/common/ui/Button.tsx new file mode 100644 index 0000000..d66a6e6 --- /dev/null +++ b/src/common/ui/Button.tsx @@ -0,0 +1,44 @@ +/** + * Button Component + * A reusable button component with different variants and sizes. + */ + +import React from "react"; + +type ButtonProps = { + variant?: "primary" | "secondary" | "outline"; + size?: "sm" | "md" | "lg"; + children: React.ReactNode; +} & React.ButtonHTMLAttributes; + +const Button = ({ + variant = "primary", + size = "md", + children, + className = "", + ...props +}: ButtonProps) => { + let baseClasses = "rounded font-medium transition-colors focus:outline-none"; + + const variantClasses = { + primary: "bg-blue-600 text-white hover:bg-blue-700", + secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300", + outline: "border bg-transparent hover:bg-gray-100", + }; + + const sizeClasses = { + sm: "px-2 py-1 text-sm", + md: "px-4 py-2", + lg: "px-6 py-3", + }; + + const buttonClasses = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`; + + return ( + + ); +}; + +export default Button; diff --git a/src/common/ui/CTASection/index.tsx b/src/common/ui/CTASection/index.tsx new file mode 100644 index 0000000..8875528 --- /dev/null +++ b/src/common/ui/CTASection/index.tsx @@ -0,0 +1,41 @@ +/** + * CallToAction component + */ + +import React, { ReactNode } from "react"; + +interface CTASectionProps { + children: ReactNode; + className?: string; +} + +const CTASection: React.FC = ({ + children, + className = "", +}) => { + return ( +
+ {/* Relative container to position the intersecting corner lines and content */} +
+ {/* Full horizontal lines that cross the entire container */} +
+
+ +
+
+ + {/* Full vertical lines that cross the entire container */} +
+
+ +
+
+ + {/* Content */} +
{children}
+
+
+ ); +}; + +export default CTASection; diff --git a/src/common/ui/Card/TraceCard.tsx b/src/common/ui/Card/TraceCard.tsx new file mode 100644 index 0000000..476a619 --- /dev/null +++ b/src/common/ui/Card/TraceCard.tsx @@ -0,0 +1,97 @@ +/** + * TraceCard Component + * A card component for displaying trace information + */ + +import React from "react"; + +interface TraceCardProps { + title: string; + icon?: React.ReactNode; + description: string; + traceData?: string; + className?: string; + color?: "blue" | "purple" | "magenta" | "orange" | "gray" | "taupe"; +} + +const TraceCard: React.FC = ({ + title, + icon, + description, + traceData, + className = "", + color = "blue", +}) => { + // Map colors to tailwind classes + const colorMap = { + blue: { + title: "text-blue-500", + icon: "bg-blue-100", + code: "bg-blue-50", + }, + purple: { + title: "text-blue-lbg", + icon: "bg-indigo-100", + code: "bg-indigo-50", + }, + magenta: { + title: "text-purple-lbg", + icon: "bg-purple-lbg/10", + code: "bg-purple-lbg/10", + }, + orange: { + title: "text-tan", + icon: "bg-orange-100", + code: "bg-orange-50", + }, + taupe: { + title: "text-taupe", + icon: "bg-taupe/30", + code: "bg-taupe/30", + }, + gray: { + title: "text-gray-500", + icon: "bg-gray-200", + code: "bg-gray-50", + }, + }; + + const colorClasses = colorMap[color]; + + return ( +
+
+ {icon && ( +
+ {icon} +
+ )} +

+ {title} +

+
+ + + +
{description}
+ + {traceData && ( +
+
+            {traceData}
+          
+
+ )} +
+ ); +}; + +export default TraceCard; diff --git a/src/common/ui/Card/index.ts b/src/common/ui/Card/index.ts new file mode 100644 index 0000000..9c5f42f --- /dev/null +++ b/src/common/ui/Card/index.ts @@ -0,0 +1 @@ +export { default as TraceCard } from "./TraceCard"; diff --git a/src/common/ui/CodeDisplay.tsx b/src/common/ui/CodeDisplay.tsx new file mode 100644 index 0000000..fe58ae6 --- /dev/null +++ b/src/common/ui/CodeDisplay.tsx @@ -0,0 +1,51 @@ +import React, { useState, useEffect } from "react"; +import hljs from "highlight.js/lib/core"; +import "highlight.js/styles/xcode.min.css"; +import fsharp from "highlight.js/lib/languages/fsharp"; + +hljs.registerLanguage("fsharp", fsharp); + +interface CodeDisplayProps { + code: string; + language?: string; + showLineNumbers?: boolean; +} + +const CodeDisplay: React.FC = ({ + code, + language = "fsharp", + showLineNumbers = true, +}) => { + const [highlightedCode, setHighlightedCode] = useState(""); + + useEffect(() => { + const highlighted = hljs.highlight(code, { language }).value; + + const codeLines = highlighted.split("\n"); + + const codeWithLineNumbers = codeLines + .map((line, index) => { + const lineNumber = index + 1; + return `
${showLineNumbers + ? `${lineNumber}` + : "" + }${line || " "}
`; + }) + .join(""); + + setHighlightedCode(codeWithLineNumbers); + }, [code, language, showLineNumbers]); + + return ( +
+
+        
+      
+
+ ); +}; + +export default CodeDisplay; diff --git a/src/common/ui/CodeEditor/CompletionMenu.tsx b/src/common/ui/CodeEditor/CompletionMenu.tsx new file mode 100644 index 0000000..3010d50 --- /dev/null +++ b/src/common/ui/CodeEditor/CompletionMenu.tsx @@ -0,0 +1,43 @@ +/** + * CompletionMenu Component + * A dropdown menu for code completion suggestions + */ + +import React from "react"; + +interface CompletionItem { + label: string; + description?: string; +} + +interface CompletionMenuProps { + items: CompletionItem[]; +} + +const CompletionMenu: React.FC = ({ items }) => { + return ( +
+
    + {items.map((item, index) => ( +
  • +
    + {item.label} + {item.description && ( + + {item.description} + + )} +
    +
  • + ))} +
+
+ ); +}; + +export default CompletionMenu; diff --git a/src/common/ui/CodeEditor/index.tsx b/src/common/ui/CodeEditor/index.tsx new file mode 100644 index 0000000..599ae6e --- /dev/null +++ b/src/common/ui/CodeEditor/index.tsx @@ -0,0 +1,218 @@ +/** + * CodeEditor Component + */ + +import React, { useState, useEffect } from "react"; +import CodeDisplay from "../CodeDisplay"; +import hljs from "highlight.js/lib/core"; +import "highlight.js/styles/vs2015.min.css"; +import fsharp from "highlight.js/lib/languages/fsharp"; + +hljs.registerLanguage("fsharp", fsharp); + +interface CodeEditorProps { + code: string; + language?: string; + showCompletion?: boolean; + showHover?: boolean; + showDiagnostics?: boolean; + showGoToDef?: boolean; +} + +const CodeEditor: React.FC = ({ + code, + language = "fsharp", + showCompletion = false, + showHover = false, + showDiagnostics = false, + showGoToDef = false, +}) => { + const [highlightedCode, setHighlightedCode] = useState< + Array<{ html: string }> + >([]); + + // Create line numbers + const lines = code.split("\n"); + const lineNumbers = Array.from( + { length: Math.max(7, lines.length) }, + (_, i) => i + 1, + ); + + useEffect(() => { + const processedLines = lines.map(line => { + if (line.trim() === "") return { html: " " }; + const highlighted = hljs.highlight(line, { language }).value; + return { html: highlighted }; + }); + + setHighlightedCode(processedLines); + }, [code, language]); + + return ( +
+ {/* Editor top bar with dots */} +
+
+
+
+
+
+
+ + {/* Code area */} +
+
+ {/* Line numbers */} +
+ {lineNumbers.map(num => ( +
+ {num} +
+ ))} +
+ + {/* Actual code with syntax highlighting */} +
+ {highlightedCode.map((line, index) => ( +
+ +
+ ))} + {renderEmptyLines(Math.max(0, 7 - lines.length))} +
+
+ + {/* Code completion dropdown */} + {showCompletion && ( +
+
    +
  • +
    + empty +
    +
  • +
  • +
    + head +
    +
  • +
  • +
    + tail +
    +
  • +
  • +
    + map +
    +
  • +
  • +
    + filter +
    +
  • +
  • +
    + length +
    +
  • +
  • +
    + sort +
    +
  • +
  • +
    + fold +
    +
  • +
  • +
    + merge +
    +
  • +
+
+ )} + + {/* Hover info */} + {showHover && ( +
+
+
List.empty
+
+ Create an empty list of any type +
+
a' list
+
+
+ )} + + {/* Diagnostics error */} + {showDiagnostics && ( +
+
+
+
Error
+
+ Cannot use List without completing the method call +
+
+
+ )} + + {/* Go to definition - Popup with function definition */} + {showGoToDef && ( + <> + {/* Underline the function name */} +
+ + {/* Small popup showing function definition */} +
+
+
+ {/* Popup header */} +
+
+
+
+
+
+
+ darklang/packages/stdlib/List.dark +
+
+ + {/* Code content */} + +
+
+ + )} +
+
+ ); +}; + +// Render empty lines to fill space +const renderEmptyLines = (count: number) => { + return Array.from({ length: count }, (_, i) => ( +
+   +
+ )); +}; + +export default CodeEditor; diff --git a/src/common/ui/Dropdown.tsx b/src/common/ui/Dropdown.tsx new file mode 100644 index 0000000..935df79 --- /dev/null +++ b/src/common/ui/Dropdown.tsx @@ -0,0 +1,87 @@ +import React, { useState, useRef, useEffect } from "react"; +import { Link } from "react-router-dom"; + +interface DropdownProps { + label: string; + labelColor?: string; + items: { + text: string; + href: string; + target?: string; + }[]; +} + +const Dropdown: React.FC = ({ label, labelColor, items }) => { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + const toggleDropdown = () => { + setIsOpen(!isOpen); + }; + + // Close dropdown when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + return ( +
+ + + {isOpen && ( +
+
+ {items.map((item, index) => ( + + setIsOpen(false)} + > + {item.text} + + {index < items.length - 1 && ( +
+ )} +
+ ))} +
+
+ )} +
+ ); +}; + +export default Dropdown; diff --git a/src/common/ui/Icons/FeatureIcons.tsx b/src/common/ui/Icons/FeatureIcons.tsx new file mode 100644 index 0000000..0eb4928 --- /dev/null +++ b/src/common/ui/Icons/FeatureIcons.tsx @@ -0,0 +1,105 @@ +/** + * FeatureIcons + * Icons for feature cards and other UI elements + */ + +import React from "react"; + +interface IconProps { + className?: string; +} + +export const HttpIcon: React.FC = () => ( + + + +); + +export const WorkerIcon: React.FC = () => ( + + + + + + +); + +export const CronIcon: React.FC = () => ( + + + + +); + +export const FnIcon: React.FC = () => ( + + + +); + +export const CliIcon: React.FC = () => ( + + + +); + +export const MiscIcon: React.FC = () => ( + + + + + +); diff --git a/src/common/ui/Icons/index.ts b/src/common/ui/Icons/index.ts new file mode 100644 index 0000000..23d4ad5 --- /dev/null +++ b/src/common/ui/Icons/index.ts @@ -0,0 +1 @@ +export * from "./FeatureIcons"; diff --git a/src/common/ui/SectionTitle.tsx b/src/common/ui/SectionTitle.tsx new file mode 100644 index 0000000..c28fae9 --- /dev/null +++ b/src/common/ui/SectionTitle.tsx @@ -0,0 +1,65 @@ +/** + * SectionTitle Component + * A reusable component for section titles + */ + +import React, { ReactNode } from "react"; + +interface SectionTitleProps { + subtitle?: string; + children: ReactNode; + description?: string; + align?: "left" | "center" | "right"; + className?: string; + maxWidth?: string; + textColor?: string; + subtitleStyle?: "text" | "button"; +} + +const SectionTitle: React.FC = ({ + subtitle, + children, + description, + align = "left", + className = "", + maxWidth = "max-w-6xl", + textColor, + subtitleStyle = "text", +}) => { + const textAlignment = { + left: "text-left", + center: "text-center", + right: "text-right", + }[align]; + + return ( +
+ {subtitle && + (subtitleStyle === "button" ? ( +
+ +
+ ) : ( +
+ {subtitle} +
+ ))} +

+ {children} +

+ {description && ( +

+ {description} +

+ )} +
+ ); +}; + +export default SectionTitle; diff --git a/src/common/ui/Terminal.tsx b/src/common/ui/Terminal.tsx new file mode 100644 index 0000000..45c18ff --- /dev/null +++ b/src/common/ui/Terminal.tsx @@ -0,0 +1,30 @@ +/** + * Terminal Component + * Renders a terminal-like UI with customizable content + */ + +import React, { ReactNode } from "react"; + +interface TerminalProps { + children: ReactNode; + className?: string; +} + +const Terminal: React.FC = ({ children, className = "" }) => { + return ( +
+
+
+
+
+
+
+
+
+ {children} +
+
+ ); +}; + +export default Terminal; diff --git a/src/common/utils/ScrollToTop.tsx b/src/common/utils/ScrollToTop.tsx new file mode 100644 index 0000000..46e6144 --- /dev/null +++ b/src/common/utils/ScrollToTop.tsx @@ -0,0 +1,14 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +function ScrollToTop() { + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return null; +} + +export default ScrollToTop; diff --git a/src/components/BreadcrumbNavigation.tsx b/src/components/BreadcrumbNavigation.tsx new file mode 100644 index 0000000..517db1c --- /dev/null +++ b/src/components/BreadcrumbNavigation.tsx @@ -0,0 +1,76 @@ +import React from 'react'; + +interface BreadcrumbItem { + name: string; + path: string | null; + isRoute: boolean; +} + +interface BreadcrumbNavigationProps { + fullName: string; + + onRouteClick: (path: string) => void; + onModuleClick: (modulePath: string) => void; + + className?: string; +} + +const BreadcrumbNavigation: React.FC = ({ + fullName, + onRouteClick, + onModuleClick, + className = '' +}) => { + const generateBreadcrumbs = (): BreadcrumbItem[] => { + const parts = fullName.split('.'); + return [ + { name: "Packages", path: "/packages", isRoute: true }, + ...parts.map((part: string, index: number) => { + const isLast = index === parts.length - 1; + if (isLast) { + // Current page - not clickable + return { name: part, path: null, isRoute: false }; + } else { + // Build the module path up to this point + const modulePath = parts.slice(0, index + 1).join('.'); + return { name: part, path: modulePath, isRoute: false }; + } + }) + ]; + }; + + const breadcrumbs = generateBreadcrumbs(); + + const handleCrumbClick = (crumb: BreadcrumbItem) => { + if (!crumb.path) return; + + if (crumb.isRoute) { + onRouteClick(crumb.path); + } else { + onModuleClick(crumb.path); + } + }; + + return ( + + ); +}; + +export default BreadcrumbNavigation; \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..0b06ac0 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1 @@ +export { default as BreadcrumbNavigation } from './BreadcrumbNavigation'; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..f19246f --- /dev/null +++ b/src/index.css @@ -0,0 +1,75 @@ +@import "tailwindcss"; + +body { + font-family: "Quicksand", sans-serif; + overflow-x: hidden; +} + +.code-with-line-numbers { + font-family: monospace; + white-space: pre; +} + +.code-line { + display: flex; + width: 100%; + min-height: 1.2em; +} + +.line-number { + display: inline-block; + width: 2rem; + text-align: right; + padding-right: 1rem; + margin-right: 1rem; + color: #888; + /* border-right: 1px solid #ddd; */ +} + +.line-content { + flex: 1; + white-space: pre-wrap; + line-height: 1.3; +} + +@theme { + /* Classic editor colors */ + --color-dark: #2f2f2f; + --color-dark-black: #1e1e1e; + --color-gray-custom: #484848; + --color-gray-dark: #8b8888; + --color-gray-light: #a8a8a8; + --color-rust: #bf6360; + --color-taupe: #a1887f; + --color-sand: #f7ca88; + --color-mint: #86c1b9; + --color-rose: #d5839d; + --color-olive: #a1b56c; + --color-tan: #dc9656; + --color-lavender: #c7abcd; + + /* Darklang colors */ + --color-black-custom: #2f2f2f; + /* --color-white-custom: #F8F8F8; */ + --color-white-custom: #ffffff; + + /* Define purple and blue with their variations */ + --color-purple-dbg: #8f5ea1; + --color-purple-lbg: #95589f; + /*lighter purple*/ + --color-purple-secondry: #a76bba; + + --color-blue-dbg: #6d74c5; + --color-blue-lbg: #747ab9; + + --color-classic-green: #a1b56c; + --color-classic-brown: #a1887f; + --color-classic-yellow: #e6bd81; + --color-classic-blue: #86c1b9; + --color-classic-purple: #b278ff; + --color-classic-pink: #f56ff0; + + --font-caveat: Caveat, cursive; + --font-sans: Quicksand, sans-serif; + --font-code: Fira Code, monospace; +} \ No newline at end of file diff --git a/src/input.css b/src/input.css deleted file mode 100644 index 45d5052..0000000 --- a/src/input.css +++ /dev/null @@ -1,11 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=League+Gothic&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Assistant:wght@700&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Quicksand:wght@400;700&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Assistant:wght@700&family=Fira+Code:wght@400;500;600&display=swap"); -@tailwind base; -@tailwind components; -@tailwind utilities; - -.hljs-title.hljs-title { - @apply text-red-800; -} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..abf3265 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +import "./index.css"; +import App from "./App.tsx"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/src/pages/AI/index.tsx b/src/pages/AI/index.tsx new file mode 100644 index 0000000..62ae2ed --- /dev/null +++ b/src/pages/AI/index.tsx @@ -0,0 +1,35 @@ +import React from "react"; + +const AI: React.FC = () => { + return ( +
+## Generally
+
+## For your development
+- very generous parser
+- import/export to other languages using AI
+- support copilot
+- redesigned to support AI
+- bring-your-own-model
+
+
+## For your users
+
+
+- (bullets from "designed for GenAI")
+- "built for AI"
+- build vendor SDKs automagically
+- prompt using your AI, or ours
+- build AI models in darklang
+- agents
+- packages for integrating with OpenAI or other models
+- support for local whatever
+- future: native support for vector DBs?
+- alternative to langchain
+	- prompt pipelines, etc
+
+	
+ ); +}; + +export default AI; diff --git a/src/pages/Backends/index.tsx b/src/pages/Backends/index.tsx new file mode 100644 index 0000000..e9719f0 --- /dev/null +++ b/src/pages/Backends/index.tsx @@ -0,0 +1,72 @@ +import React from "react"; + +const Backends: React.FC = () => { + return ( +
+
+        talk about features
+        http handlers
+        CRONs
+        workers
+        etc.
+
+        talk about our cloud offerings
+        reference things in dark-classic
+
+        talk about eventual migration from -classic to -next
+      
+ +
+ +

+ Darklang is a programming language and platform designed to simplify backend development by removing much of the complexity associated with traditional backend infrastructure. Its backend features are tightly integrated into a single environment, and built to streamline the creation of cloud-native applications without managing infrastructure, deployments, or containers. +

+ +
+

Darklang’s backend features—HTTP handlers, scheduled jobs (CRONs), background workers, and datastores are designed for rapid development and prototyping, with a focus on simplicity and instant deployment.

+ +
+

+ Datastores: + - Datastores in Darklang are key-value stores used for persistent data storage, integrated seamlessly with handlers and workers. + - when you set up a database in dark, you just add the database, like you click a button or a keyboard shortcut, no requiring a server from somewhere to put it on, no going to another server, no config, no orm it is written in the same language as the rest of your code + - when you make a query, you query in the dark + + talk about operations? + - (DB.set, DB.get...) + - (DB.query...) +

+ +
+ +

+ HTTP Handlers: + - HTTP handlers in Darklang are the core mechanism for handling incoming HTTP requests, serving as the entry point for API endpoints and web applications. + - when setting up a HTTP request you're not setting up a HTTP server, you're just add an end point that is directly connected to the code and you write them in the same place. There's no spinning up servers + - Darklang is designed for interacting with 3rd party APIs over HTTP. The `HttpClient` module has a set of functions for calling out to other HTTP services and APIs +

+
+ +

+ Scheduled jobs: + - CRON jobs in Darklang are scheduled tasks that run on a predefined schedule, similar to Unix cron, ideal for periodic tasks like report generation or data cleanup. + - Crons run automatically once per interval +

+ +
+

+ Background Workers: + - Workers in Darklang handle asynchronous background tasks, processing messages from a queue, making them ideal for tasks like API calls, batch processing, or report generation. + - Darklang supports doing work asynchronously outside the context of an HTTP handler using a **Worker**. Each worker has a queue of messages, which are processed loosely in-order, executing the code within the Worker once for each message. Messages are created by calling `emit` from any other code, and can contain arbitrary event data. + + ADD Example use case? +

+
+ +
+ + + ); +}; + +export default Backends; diff --git a/src/pages/CLI/index.tsx b/src/pages/CLI/index.tsx new file mode 100644 index 0000000..92b44c7 --- /dev/null +++ b/src/pages/CLI/index.tsx @@ -0,0 +1,399 @@ +import React from "react"; + +import cliImage from "~/assets/cli.png"; +import doubleGridImage from "~/assets/double-grid.png"; +import gridImage from "~/assets/grid.png"; +import logoAscii from "~/assets/logo-ascii.png"; +import cliAscii from "~/assets/darklang-cli-ascii.png"; + +import Terminal from "../../common/ui/Terminal.tsx"; +// import Button from "../../common/ui/Button.tsx"; +// import CTASection from "../../common/ui/CTASection/index.tsx"; + +const CLIPage: React.FC = () => { + return ( +
+ {/* ASCII Art Header Section - Hidden on Mobile */} +
+
+ Darklang CLI ASCII +
+
+ + {/* CLI Platform Section */} +
+
+
+
+ $ darklang platform | +
+

+ Darklang's CLI is fully cross-platform, seamlessly running on + macOS, Linux, and Windows for a consistent development experience + everywhere +

+ {/* */} +
+
+ Darklang CLI Terminal +
+
+
+ + {/* Get Started Section */} + {/*
+
+ $ darklang get started | +
+
*/} + + {/* Bash Complexities Section */} +
+
+ $ darklang as an alternative to bash, python, etc. | +
+ +
+
+

+ Darklang CLI is a better replacement for traditional file-based + scripts, such as in bash, python, lua, js, etc. +

+ +

+ bash is super hard to read, using weird variable names. While lots + of us can read and write bash scripts, since there are few + experts, it's not a great language. +

+ +
    +
  • + - + + lack of a package manager means the generated code has to use + cli tools, which each have different interfaces, which may not + be installed, and are often opaque + +
  • + +
  • + - + + different versions of the tools might be installed with subtly + different behaviour (esp gnu vs bsd) + +
  • + +
  • + - + + lack of real types and functions (which are a mess in bash) + contributes to these problems + +
  • +
+ +

Python scripts can be just as messy, often requiring you to spin up virtual environments for every project just to avoid dependency conflicts

+
+
+ grid +
+
+
+ + {/* Darklang for Scripts Section */} +
+
+ $ darklang for your scripts | +
+
+ grid +
+

+ Darklang is used as a better language for scripts: +

+ +
    +
  • + - + + Static types help ensure correctness + +
  • + +
  • + - + + Immutable values make code easier to understand and verify + +
  • + +
  • + - + + Built-in package manager + +
  • + +
  • + + + without an npm install step + +
  • + +
  • + + + versioned immutable functions and packages + +
  • + +
  • + - + + Easy to take a script and move it to the cloud + +
  • + +
  • + - + + Easy to use traces + +
  • + +
  • + - + + Easy to test, and be sure it's working + +
  • +
+
+
+
+ + {/* Darklang Examples Section */} +
+
+ $ darklang examples | +
+
+ {/* Left Terminal */} + +
+ $ + curl + https://darklang.com/download + | + bash +
+ +
+ Darklang installed in{" "} + ~/.darklang/bin/darklang +
+ +
+ Add to + PATH + via .bashrc + [y, n, ?]: + y +
+ +
+ + Added to .bashrc. +
+ +
Next you can:
+

# Try the tutorial

+

darklang tutorial

+

+ # Run some code from the package manager darklang +

+

@paul.fizzbuzz 3

+

# Generate some code

+

+ darklang prompt{" "} + + "Find ts scripts with more than 600 lines which use the commonjs + format" + +

+

+ # See available command line options +

+

darklang help

+
+ + {/* Right Column */} +
+ +
+ $ + darklang + @paul.fizzbuzz 3 +
+
1
+
2
+
Fizz
+
+ + +
+ $ + darklang + + deploy @paul.fizzbuzz /fizzbuzz + +
+ +
+ {" "} + Deployed to{" "} + https://furry-squirrel-3562.darklang.io/fizzbuzz +
+
+ in + 0.135s +
+
+ + +
+ $ curl -sSO + + https://furry-squirrel-3562.darklang.io/fizzbuzz/3 + +
+
1
+
2
+
Fizz
+
+
+
+ logo-ascii +
+ + {/* Darklang Commands Section */} +
+
+ $ darklang commands | +
+ +
+
+
+ dark help +
+
+ Show this help message and exit +
+
+ +
+
+ dark [function name] +
+
+ Run a function in the package manager i.e. `dark + @Darklang.Stdlib.Bool.not true` +
+
+ +
+
+ dark run [script path] +
+
+ Run a .dark script i.e. `dark ./my-script.dark` +
+
+ +
+
+ dark install +
+
+ Install the darklang CLI so it's available globally in your + terminal +
+
+ +
+
+ dark http +
+
+ Lists both local and cloud handlers +
+
+ +
+
+ dark dbs +
+
+ Lists both local and cloud dbs +
+
+
+ + {/*
+ +
*/} +
+ + {/* +
+

+ Getting Started with Darklang CLI +

+

+ Write your first script in Darklang today and have it running in + minutes +

+ +
+
*/} +
+ ); +}; + +export default CLIPage; diff --git a/src/pages/Classic/index.tsx b/src/pages/Classic/index.tsx new file mode 100644 index 0000000..27f5acd --- /dev/null +++ b/src/pages/Classic/index.tsx @@ -0,0 +1,189 @@ +import darklangClassic from "~/assets/darklang-classic.png"; +import darkClassicToplevel from "~/assets/dark-classic-toplevel.png"; + +// TODO talk about how things will eventually be migrated to "-next" + +const About = () => { + return ( +
+
+ {/* Logo section */} +
+ Darklang Classic +
+ + {/* Main content - First Section */} +
+ + {/* Code Editor Image Section */} +
+ Darklang Editor Interface +
+ + {/* Key Capabilities */} +
+
+ {/* Internal Tools & Bots */} +
+

+ Internal Tools + & + Bots +

+

+ Dark is ideal for quickly building slackbots and automating + internal tools. Receive webhooks live, call out to 3rd party + APIs, store data, and schedule jobs - while building no + infrastructure. +

+ + Read more about using dark with Slack + +
+ + {/* REST APIs & Webhooks */} +
+

+ REST APIs + & + Webhooks +

+

+ Set up an API endpoint quickly enough to use it as a proof of + concept during a call. Immediately see the data from a webhook + to your endpoint. Call an external API using the HttpClient + library and see responses within the editor, or use workers to + do them in the background. Use the built-in package manager to + make external API calls really easily, and contribute your own + API integrations. +

+
+ + {/* CRUD apps */} +
+

+ CRUD + apps +

+

+ Get a working CRUD application in less than ten minutes by + setting up a few API endpoints and a datastore. Build out the + backend for a web or mobile app, whether a simple HTML form or + an entire product. +

+
+
+
+ + {/* Any Backend Section */} +
+

+ Any + Backend + That Requires... +

+ +
+ {/* Feature Card 1 */} +
+

+ API + Endpoints +

+
+ + {/* Feature Card 2 */} +
+

+ Data + Stores +

+
+ + {/* Feature Card 3 */} +
+

+ Background + Workers +

+
+ + {/* Feature Card 4 */} +
+

+ Scheduled + Jobs +

+
+
+ +

+ Dark lets you build any backend that needs API endpoints, data + stores, background workers, scheduled jobs, and calling HTTP APIs. + You just write the code in Dark, and we'll manage the rest. +

+ + + +
+
+
+ ); +}; + +export default About; diff --git a/src/pages/Cloud/index.tsx b/src/pages/Cloud/index.tsx new file mode 100644 index 0000000..20ad743 --- /dev/null +++ b/src/pages/Cloud/index.tsx @@ -0,0 +1,22 @@ + + + +import React from "react"; + +const Cloud: React.FC = () => { + return ( +
+

the big host

+
    +
  • hosting of your code (git)
  • +
  • hosting of your deployed apps (CLI, backends, etc)
  • +
  • in-browser CLI access
  • +
  • in-browser editor
  • +
  • hosted MCP servers, LSP servers
  • +
  • CDN help
  • +
+
+ ); +}; + +export default Cloud; diff --git a/src/pages/Company/Sustainability.tsx b/src/pages/Company/Sustainability.tsx new file mode 100644 index 0000000..85e4bfc --- /dev/null +++ b/src/pages/Company/Sustainability.tsx @@ -0,0 +1,13 @@ +
+    Licensing
+    Cloud hosting
+    plz give us money
+
+    burndown chart?
+
+    {
+                  //   text: "Sponsor us",
+                  //   href: "https://github.com/sponsors/darklang",
+                  //   target: "_blank",
+                  // },
+
\ No newline at end of file diff --git a/src/pages/Company/index.tsx b/src/pages/Company/index.tsx new file mode 100644 index 0000000..2dc24f8 --- /dev/null +++ b/src/pages/Company/index.tsx @@ -0,0 +1,46 @@ +import React from "react"; + +const Company: React.FC = () => { + return ( +
+Darklang has been around in various form since xxxxxx.
+
+## Vision
+
+## Mission
+
+## Funding, Sustainability, Licensing, and Business Model
+
+In order to work on our mission, towards our vision, we must be sustainable.
+
+[sponsor, licensing]
+
+(if you're interested in giving us $, ...)
+
+## Branding
+
+(link to another page)
+
+## Roadmap
+
+- [...]
+
+
+## Are we hiring?
+
+No, sorry, not currently. We're limited on funding.
+
+
+## History
+...
+
+    Licensing
+    Cloud hosting
+    plz give us money
+
+    burndown chart?
+
+ ); +}; + +export default Company; diff --git a/src/pages/Distribution/index.tsx b/src/pages/Distribution/index.tsx new file mode 100644 index 0000000..3e553c0 --- /dev/null +++ b/src/pages/Distribution/index.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +const Distribution: React.FC = () => { + return ( +
+
+        package management
+        source control
+        building artifacts for end users
+      
+

+ TODO: move this somewhere else +
+ Darklang is a cloud-native programming language and platform designed to simplify the development of serverless backends by introducing a "deployless" model. This approach aims to remove the complexities of traditional software deployment, allowing developers to focus on writing code rather than managing infrastructure, builds, or deployment pipelines. + + In traditional software development, deploying code involves multiple steps: writing code, committing it to a repository (e.g., via Git), creating pull requests, building assets, pushing containers to registries, and orchestrating deployments (e.g., via Kubernetes). Darklang eliminates roughly 60% of these steps by integrating the language, editor, and infrastructure into a cohesive platform. The result is a deployment process that takes approximately 50 milliseconds, enabling rapid iteration and testing in production. + + + The deployless model is analogous to serverless computing: just as serverless abstracts away server management, deployless abstracts away deployment management so that you don’t have to think about it + + Developers don’t need to configure build systems, containers, Kubernetes, or CI/CD pipelines. The platform handles scaling, monitoring, and optimization + + Feature flags allow developers to test code in production without exposing it to users, and precise access controls enhance security + + No Infrastructure Management: Developers avoid configuring servers, databases, or networking components. + + Developers spend less time on DevOps tasks, focusing on application logic +

+
+ ); +}; + +export default Distribution; \ No newline at end of file diff --git a/src/pages/Editing/index.tsx b/src/pages/Editing/index.tsx new file mode 100644 index 0000000..c798535 --- /dev/null +++ b/src/pages/Editing/index.tsx @@ -0,0 +1,210 @@ +import React from "react"; + +import doubleGridSquare from "~/assets/double-grid-square.png"; +import darklangLogoFramed from "~/assets/darklang-logo-framed.png"; + +// import Button from "../../common/ui/Button"; +import CodeEditor from "../../common/ui/CodeEditor"; +// import CTASection from "../../common/ui/CTASection"; + +const Editing: React.FC = () => { + return ( +
+
+
+ Grid Pattern +
+ +
+
+
+
+ Darklang Logo +
+
+ +
+

Multi-Editor Support

+

+ Darklang's language server is designed to work across multiple + editing environments, providing a consistent experience + regardless of your preferred editor. +

+ {/*
+ + +
*/} +
+
+
+
+ + {/* LSP Server Section */} +
+
Darklang LSP Server
+ +

+ Our language server is fully written in Darklang and follows the + Language Server Protocol (LSP), providing features like + autocompletion, real-time error checking, and hover documentation. It + is currently integrated with a lightweight VS Code extension, and we + plan to support more editors like Vim, Rider, and Sublime in the + future. +

+
+ + {/* Basic Features Section */} +
+
Basic Features
+ +
+ {/* Code Completion */} +
+

+ Code completion +

+ +
+ + {/* Syntax Highlighting */} +
+

+ Syntax highlighting +

+ +
+ + {/* Diagnostics */} +
+

+ Diagnostics +

+ +
+
+ +
+ {/* Hover */} +
+

+ Hover +

+ +
+ + {/* Go to Definition */} +
+

+ Go to definition +

+ +
+
+
+ + {/* Customizing & Creating Language Servers Section */} +
+
+ Customizing & Creating Language Servers +
+ +
+

+ Many of our language tools are fully accessible to users. Just like + forking and editing your own code, you can fork the language server + to change how features like onHover, autocompletion, or diagnostics + work. +

+ +

+ It's also easy to create your own language servers, whether for + testing new ideas or developing specialized tools, building internal Darklang features, or your own independent projects. +

+
+
+ + {/* Extension Section */} +
+
VSCode Extension
+ +
+

+ We are currently focused on building a VS Code extension because of + its rich API ecosystem and powerful FileSystemProvider (FSP) + capabilities. Unlike traditional development where code exists as + plain text files on disk, Darklang's code is stored in a package + manager, making VS Code's FSP particularly valuable for our + implementation. +

+
+
+ +
+
Future Editor Support
+ +
+

+ While VS Code is our starting point, we're actively planning support + for additional editors including Vim, Sublime Text, and Rider, as well as enabling a fully integrated AI-powered terminal-based editing experience. +

+
+
+ + {/* Getting Started Section */} + {/* +
+

Getting Started

+

+ Try the VS Code extension or use it instantly on{" "} + + editor.darklang.com + + ! +

+
+ +
+
+
*/} +
+ ); +}; + +export default Editing; diff --git a/src/pages/Execution/index.tsx b/src/pages/Execution/index.tsx new file mode 100644 index 0000000..4496557 --- /dev/null +++ b/src/pages/Execution/index.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +const Execution: React.FC = () => { + return ( +
+    interpeted
+
+    TODO: compiler stuff
+    runtime typechecking [link to TC page]
+
+    rough metrics of perf.
+
+    link to github issues
+
+ ); +}; + +export default Execution; diff --git a/src/pages/For/index.tsx b/src/pages/For/index.tsx new file mode 100644 index 0000000..01c80cd --- /dev/null +++ b/src/pages/For/index.tsx @@ -0,0 +1,39 @@ +import React from "react"; + +const For: React.FC = () => { + return ( +
+I'm thinking to have a bunch of pages like
+darklang.com/for/web-developers
+with dedicated content for groups of folks who we think might appreciate Darklang.
+
+here are some ideas
+
+darklang.com/for/fsharp-developers
+darklang.com/for/home-automation
+darklang.com/for/eInk-devices
+darklang.com/for/web-developers
+darklang.com/for/people-with-money (letter asking them to invest etc?)
+darklang.com/for/people-who-want-money (entrepreneurs? scripters? people with servers?)
+darklang.com/for/security-nerds
+darklang.com/for/unix-users
+darklang.com/for/accessibility
+darklang.com/for/AI-developers
+darklang.com/for/lazy-people
+darklang.com/for/folks-who-collect-domains
+darklang.com/for/python-developers
+darklang.com/for/web-scrapers (the web is ridden with flies, you can hardly see through. scrape the web and provide) also good for folks who are into those browser extensions... something monkey? that remove ads and such
+darklang.com/for/privacy
+darklang.com/for/local-first
+darklang.com/for/small-businesses (SSG; wordpress alt.; affordable software solutions and contracting)
+
+
+
+
+in each of these, include expandable section for context for those outside the know
+
+
+ ); +}; + +export default For; diff --git a/src/pages/GettingStarted/index.tsx b/src/pages/GettingStarted/index.tsx new file mode 100644 index 0000000..da2b88b --- /dev/null +++ b/src/pages/GettingStarted/index.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +const GettingStarted: React.FC = () => { + return ( +
+    Not sure if we should point to devcontainer yet
+    , or just say "wait a bit"
+    , or something else.
+
+ ); +}; + +export default GettingStarted; diff --git a/src/pages/Home/AsyncRuntime.tsx b/src/pages/Home/AsyncRuntime.tsx new file mode 100644 index 0000000..09d254a --- /dev/null +++ b/src/pages/Home/AsyncRuntime.tsx @@ -0,0 +1,122 @@ +import React from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +interface FeatureCardProps { + title: React.ReactNode; + description: React.ReactNode; + className?: string; +} + +const FeatureCard: React.FC = ({ + title, + description, + className = "", +}) => ( +
+
+
+
+
+
+

{title}

+
+
{description}
+
+); + +const AsyncRuntime = () => { + return ( +
+
+ + Async Runtime + + +
+ {/* Left side */} +
+ + No async / await + + } + description={ +

+ Adding async and await keywords to every language was a + mistake. It exposes the complexity of concurrency and + multi-threading to languages which were originally designed + for simplicity. +

+ } + /> +
+ + {/* Right column card */} +
+ + Concurrent and{" "} + parallel execution + via data-dependencies + + } + description={ +
+

+ When you make an async request, it first waits for any + arguments that are async, and starts when they're done. If + another function call needs to use the result, it will wait + for it before starting. +

+

+ Since darklang values are immutable, there won't be any race + conditions from this. +

+
+ } + /> +
+ + {/* Second row cards */} +
+ + Fully asynchronous{" "} + runtime + + } + description={ +

+ Darklang has a fully asynchronous runtime, so making a Http + call or reading a file doesn't block the runtime. +

+ } + /> + + + Powerful{" "} + escape hatches + + } + description={ +

+ We provide powerful escape hatches if you need async ordering + that doesn't match the data dependencies of your program. +

+ } + /> +
+
+
+
+ ); +}; + +export default AsyncRuntime; diff --git a/src/pages/Home/BackendFeatures.tsx b/src/pages/Home/BackendFeatures.tsx new file mode 100644 index 0000000..1f23f2a --- /dev/null +++ b/src/pages/Home/BackendFeatures.tsx @@ -0,0 +1,367 @@ +import React, { useState, useRef, useEffect } from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; +import CodeDisplay from "../../common/ui/CodeDisplay"; + +interface FeatureButtonProps { + icon: React.ReactNode; + isMonoFont?: boolean; + label: string; + isActive: boolean; + hasDropdown?: boolean; + onClick: () => void; + subOptions?: { label: string; value: string }[]; + onSubOptionClick?: (value: string) => void; +} + +const FeatureButton: React.FC = ({ + icon, + isMonoFont = false, + label, + isActive, + hasDropdown = false, + onClick, + subOptions = [], + onSubOptionClick, +}) => { + const [showDropdown, setShowDropdown] = useState(false); + const dropdownRef = useRef(null); + + const handleClick = () => { + if (hasDropdown) { + setShowDropdown(!showDropdown); + } else { + onClick(); + } + }; + + // Handle clicking outside of dropdown + useEffect(() => { + const handleOutsideClick = (event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setShowDropdown(false); + } + }; + + if (showDropdown) { + document.addEventListener("mousedown", handleOutsideClick); + } + + return () => { + document.removeEventListener("mousedown", handleOutsideClick); + }; + }, [showDropdown]); + + return ( +
+
+ + {icon} + + {label} + {hasDropdown && } +
+ + {showDropdown && hasDropdown && ( +
+ {subOptions.map(option => ( +
{ + if (onSubOptionClick) { + onSubOptionClick(option.value); + setShowDropdown(false); + } + }} + > + {option.label} +
+ ))} +
+ )} +
+ ); +}; + +// Code examples for each feature +const codeExamples = { + httpHandlerGET: `[] +let _handler _req = + let products = Stdlib.DB.getAll ProductsDB + + let body = + products + |> Builtin.jsonSerialize + |> Stdlib.String.toBytes + + Stdlib.Http.responseWithHeaders + body + 200L + [("Content-Type", "application/json")]`, + + httpHandlerPOST: `[] +let _handler req = + // Parse the request body + let body = + req.body + |> Stdlib.String.fromBytes + |> Builtin.jsonDeserialize<{ + name: String + price: Float + description: String + category: String + }> + + // Create a new product + let newProduct = { + id = Stdlib.Uuid.generate() + name = body.name + price = body.price + description = body.description + category = body.category + createdAt = Stdlib.Date.now() + indexed = false + } + + // Save to database using the UUID as key + Stdlib.DB.set newProduct newProduct.id ProductsDB + + // Emit product to background worker + emit { productId = newProduct.id } "product-indexer" + + // Return the created product + let responseBody = + newProduct + |> Builtin.jsonSerialize + |> Stdlib.String.toBytes + + Stdlib.Http.responseWithHeaders + responseBody + 201L + [("Content-Type", "application/json")]`, + + httpHandlerDELETE: `[] +let _handler req = + let productId = req.pathParams["id"] |> Stdlib.Uuid.parse |> Builtin.unwrap + + match Stdlib.DB.get productId ProductsDB with + | Some product -> + Stdlib.DB.delete productId ProductsDB + + Builtin.printline $"Product {productId} deleted" + + Stdlib.Http.response + (Stdlib.String.toBytes "") + 204L + + | None -> + // Product not found + Stdlib.Http.responseWithHeaders + Stdlib.String.toBytes "Product not found" + 404L + [("Content-Type", "application/json")]`, + + dataStores: `// Define our product type +type Product = { + id: Stdlib.Uuid.UUID + name: String + price: Float + description: String + category: String + createdAt: Date + indexed: Bool +} + +// Create the database +[] +type ProductsDB = Product`, + + scheduledJobs: `[] +let _handler () = + // Count total products + let products = Stdlib.DB.getAll ProductsDB + let totalProducts = Stdlib.List.length products + + // Send email report + Stdlib.Email.send { + to = "admin@example.com" + subject = "Daily Product Report" + body = $"Total Products: {totalProducts}" + } + + Builtin.log "Daily report sent successfully"`, + + backgroundWorkers: `// Define a worker to process products asynchronously +[] +let _handler = + // Access the event data (comes from emit) + let productId = event.productId + + // Get the product from database + match Stdlib.DB.get productId ProductsDB with + | Some product -> + // Log start of processing + Builtin.log $"Processing product {product.id}: {product.name}" + + // Update the product status + let updatedProduct = { product with indexed = true } + Stdlib.DB.set updatedProduct product.id ProductsDB + + // Log completion + Builtin.log $"Product {product.id} marked as indexed" + + | None -> + Builtin.log $"Product {productId} not found" +`, +}; + +const BackendFeatures: React.FC = () => { + const [selectedFeature, setSelectedFeature] = + useState("httpHandlerGET"); + + // HTTP handler dropdown options + const httpHandlerOptions = [ + { label: "GET /products", value: "httpHandlerGET" }, + { label: "POST /products", value: "httpHandlerPOST" }, + { label: "DELETE /products/:id", value: "httpHandlerDELETE" }, + ]; + + // Get the code for the currently selected feature + const currentCode = codeExamples[selectedFeature]; + + return ( +
+
+
+ + Build a complete backend with Darklang + + +

+ Darklang lets you easily develop backend applications, seamlessly deployable to the cloud. + You can build tiny applications to connect two services, or large scale applications with tens of thousands of users. + Code is written in + collaboration with AI and is instantly and safely deployed on our + hosted platform or yours, so you can focus on writing code while we + handle the rest +

+ +

+ You can build any backend that needs: +

+
+ +
+
+ setSelectedFeature("httpHandlerGET")} + subOptions={httpHandlerOptions} + onSubOptionClick={value => + setSelectedFeature(value as keyof typeof codeExamples) + } + /> + + + + + + + } + label="Data stores" + isActive={selectedFeature === "dataStores"} + onClick={() => setSelectedFeature("dataStores")} + /> + + setSelectedFeature("scheduledJobs")} + /> + + + + + } + label="Background workers" + isActive={selectedFeature === "backgroundWorkers"} + onClick={() => setSelectedFeature("backgroundWorkers")} + /> +
+ +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ ); +}; + +export default BackendFeatures; diff --git a/src/pages/Home/BlogPosts.tsx b/src/pages/Home/BlogPosts.tsx new file mode 100644 index 0000000..bef480b --- /dev/null +++ b/src/pages/Home/BlogPosts.tsx @@ -0,0 +1,51 @@ +import React from "react"; + +import { BlogPostCard } from "../../common/ui/BlogPostCard"; +import SectionTitle from "../../common/ui/SectionTitle"; + +interface BlogPost { + id: string; + author: string; + date: string; + title: string; + excerpt: string; + imageUrl: string; + slug: string; +} + +interface BlogPostsProps { + posts: BlogPost[]; +} + +export const BlogPosts: React.FC = ({ posts }) => { + return ( +
+
+ + Recent Blog Posts + + +
+ {posts.map(post => ( + + ))} +
+
+
+ ); +}; + +export default BlogPosts; diff --git a/src/pages/Home/BlogPostsExample.tsx b/src/pages/Home/BlogPostsExample.tsx new file mode 100644 index 0000000..31741b5 --- /dev/null +++ b/src/pages/Home/BlogPostsExample.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +import BlogPosts from "./BlogPosts"; + +// Mock data for blog posts +const mockPosts = [ + { + id: "1", + author: "Stachu Korick", + date: "16 Jun 2025", + title: "First Steps of Darklang Inc.", + excerpt: + "Darklang Inc is the new steward of the Darklang language, whose team has been working on Darklang for several years. In this post, we'll cover three big changes...", + imageUrl: "src/assets/Cole_Thomas_The_Oxbow_-The_Connecticut_River_near_Northampton_1836-.jpeg", + slug: "https://blog.darklang.com/first-steps-of-darklang-inc/", + }, + { + id: "2", + author: "Paul Biggar", + date: "16 Jun 2025", + title: "Goodbye Dark Inc. - Hello Darklang Inc.", + excerpt: + "Dark Inc has officially run out of money. Dark Inc is the company we founded in 2017 to build Darklang...", + imageUrl: "src/assets/Velazquez-The_Surrender_of_Breda.jpeg", + slug: "https://blog.darklang.com/goodbye-dark-inc-welcome-darklang-inc/", + }, + { + id: "3", + author: "Stachu Korick", + date: "16 Jun 2025", + title: "Darklang Goes Open Source", + excerpt: + "As part of shutting down Dark Inc. and forming Darklang Inc., we've finally open-sourced all of our repositories. Our source code is now under the Apache License 2.0....", + imageUrl: "src/assets/The_School_of_Athens__by_Raffaello_Sanzio_da_Urbino.jpeg", + slug: "https://blog.darklang.com/darklang-goes-open-source/", + }, +]; + +export const BlogPostsExample: React.FC = () => { + return ; +}; + +export default BlogPostsExample; diff --git a/src/pages/Home/CLI.tsx b/src/pages/Home/CLI.tsx new file mode 100644 index 0000000..f20a341 --- /dev/null +++ b/src/pages/Home/CLI.tsx @@ -0,0 +1,157 @@ +import React from "react"; + +import cliImage from "~/assets/cli.png"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +const CLI: React.FC = () => { + return ( +
+
+ + {" "} + A CLI Runtime to Replace your Bash

and Python Scripts +
+ +
+ {/* Left side - Text content */} +
+
+
+ + + +
+

+ Darklang's CLI is a better alternative to Bash, combining its + power with the simplicity and safety of modern programming + languages. Enabling you to write scripts without confusing + errors or dependency issues. +

+
+ +
+
+ {/* Feature 1 */} +
+
+ + + + +
+
+

+ Type-safe by design +

+

+ Using static types to help ensure correctness +

+
+
+ + {/* Feature 2 */} +
+
+ + + +
+
+

+ Built-in Packages +

+

+ No Npm install, no dependency headaches +

+
+
+ + {/* Feature 3 */} +
+
+ + + +
+
+

+ Cross-platform +

+

+ Running on macOS, Linux, and Windows +

+
+
+
+
+ + + Discover Darklang CLI features and how it solves scripting + problems → + +
+ + {/* Right side */} +
+
+ Darklang CLI interface +
+
+
+
+
+ ); +}; + +export default CLI; diff --git a/src/pages/Home/DeploylessCloud.tsx b/src/pages/Home/DeploylessCloud.tsx new file mode 100644 index 0000000..58bd46f --- /dev/null +++ b/src/pages/Home/DeploylessCloud.tsx @@ -0,0 +1,109 @@ +import React from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +type FeatureCardProps = { + icon: React.ReactNode; + title: string; + description: string; +}; + +const FeatureCard: React.FC = ({ + icon, + title, + description, +}) => { + return ( +
+
{icon}
+
+

{title}

+

{description}

+
+
+ ); +}; + +const LightningIcon = ({ color = "text-purple-lbg" }: { color?: string }) => ( + + + +); + +const DeploylessCloud: React.FC = () => { + + return ( +
+
+ + Deployless, Infraless Cloud Apps + + + + +
+
+

Deployless

+ } + title="Write code and it's immediately available" + description="No build step, no wait time, no deployment pipeline your code is live as soon as you type." + /> + + } + title="Feature flags for controlled rollouts" + description="Control exactly when and for whom new features go live. Test in production safely with instant rollback capability" + /> + + } + title="Development/Production Parity" + description="Your development environment matches the production environment, so you can test with confidence." + /> + + } + title="Integrated code review and testing" + description="Review code, run tests, and collaborate seamlessly in one place" + /> +
+ +
+

Zero setup infrastructure

+ } + title="Instant infrastructure creation" + description="Language-native HTTP handlers, Databases, CRONs and queues, without thinking about servers, containers, or deployment" + /> + + } + title="Simplified Architecture" + description="No need to worry about connection poolers, sharding, indexes, load balancers, cloud services, or system administration—everything runs seamlessly in the background" + /> +
+
+
+
+ ); +}; + +export default DeploylessCloud; diff --git a/src/pages/Home/DesignedForGenAI.tsx b/src/pages/Home/DesignedForGenAI.tsx new file mode 100644 index 0000000..98555c2 --- /dev/null +++ b/src/pages/Home/DesignedForGenAI.tsx @@ -0,0 +1,161 @@ +import React from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +const DesignedForGenAI: React.FC = () => { + return ( +
+
+ + Designed for Generative AI + + +
+ {/* First Card */} +
+
+ Works with your existing AI dev tools{" "} +
+
    +
  • + - + Real-time, context-aware code suggestions for Dark apps +
  • +
  • + - + Super-tight feedback loop to speed up AI-powered development and testing +
  • +
  • + - + Safely run partial or incomplete code +
  • +
  • + - + Seamlessly integrates with Claude Code, GitHub Copilot, and type-ahead agents +
  • +
  • + - + Share custom context with GenAI tools using LLM.txt +
  • +
+
+ + {/* Second Card */} +
+
+ Build short CLI programs{" "} + from prompts +
+
+ dark prompt "find all js files which don't have a CSS file of the + same name" +
+
+ + {/* Third Card */} +
+ +
+ Use any Language Model +
+
    +
  • + - + darklang's fine-tuned models +
  • +
  • + - + local OSS models +
  • +
  • + - + commercial models via API +
  • +
+
+ + {/* Fourth Card */} +
+ +
+ Build vendor SDKs from + prompts and OpenAPI docs +
+
    +
  • + - + Transform technical API documentation into usable code libraries +
  • +
  • + - + Turn technical documentation into developer-friendly toolkits +
  • +
  • + - + Update SDKs automatically when APIs change +
  • +
+
+ + {/* Fifth Card */} +
+ +
+ Build complex programs{" "} + with darklang AI agents +
+
+ + Create next-level applications by leveraging darklang's AI + agents to handle complex workflows. These agents can analyze + requirements, suggest implementation strategies, generate code, + and help troubleshoot issues—all while maintaining code quality + and adhering to best practices. + +
+
+ + {/* MCP Card */} +
+ +
+ + Create, Run, and Share MCP servers + +
+ +
    +
  • + - + + Allow AI models to access external tools/resources, execute + code, and interact with various services + +
  • +
  • + - + Create custom MCP servers for your systems within a minute +
  • +
  • + - + Quickly run MCP servers from the command line, and easily share them with coworkers +
  • +
  • + - + Ready-to-use MCP servers for Darklang internal development and your own projects +
  • +
+
+
+
+
+ ); +}; + +export default DesignedForGenAI; diff --git a/src/pages/Home/DevelopmentSteps.tsx b/src/pages/Home/DevelopmentSteps.tsx new file mode 100644 index 0000000..6d4e8d3 --- /dev/null +++ b/src/pages/Home/DevelopmentSteps.tsx @@ -0,0 +1,615 @@ +import { useState, useEffect } from "react"; + +const DevelopmentSteps = () => { + // Animation state for the node side + const [activeNodeStep, setActiveNodeStep] = useState(0); + const [activeSubSteps, setActiveSubSteps] = useState<{ + [key: string]: number; + }>({ + setup: 1, // Start with 2 substeps completed in Setup + }); + const [completedNodeSteps, setCompletedNodeSteps] = useState([]); + const [expandedSteps, setExpandedSteps] = useState([]); + + // Animation state for the dark side + const [darkCompletedSteps, setDarkCompletedSteps] = useState(0); // Start with none completed + + // The complete list of node.js steps with their substeps + const nodeSteps = [ + { + id: "setup", + title: "Setup", + substeps: [ + { id: "install-nodejs", title: "Install Node.js and NPM" }, + { id: "create-project", title: "Create a new Node.js project" }, + { id: "npm-init", title: "Initialize project with npm init" }, + ], + }, + { + id: "development", + title: "Development", + substeps: [ + { id: "write-code", title: "Write application code" }, + { id: "env-vars", title: "Set up environment variables" }, + { id: "dependencies", title: "Install required dependencies" }, + { id: "env-vars2", title: "Set up environment variables" }, + { + id: "config-files", + title: "Create configuration files (.gitignore, package.json, etc)", + }, + ], + }, + { + id: "database", + title: "Database Setup", + substeps: [ + { id: "choose-db", title: "Choose a database system" }, + { id: "setup-db", title: "Set up database" }, + { id: "create-schema", title: "Create database schema" }, + { id: "config-connection", title: "Configure database connection" }, + { id: "setup-orm", title: "Set up ORM" }, + ], + }, + { + id: "version-control", + title: "Version Control", + substeps: [ + { id: "init-git", title: "Initialize Git repository" }, + { id: "commit-push", title: "Create branch, commit, and push code" }, + ], + }, + { + id: "testing", + title: "Testing", + substeps: [ + { id: "unit-tests", title: "Write unit tests" }, + { id: "integration-tests", title: "Write integration tests" }, + { id: "test-env", title: "Set up test environment" }, + { id: "run-tests", title: "Run tests locally" }, + ], + }, + { + id: "infrastructure", + title: "Infrastructure", + substeps: [ + { id: "choose-hosting", title: "Choose a hosting provider" }, + { + id: "create-account", + title: "Create an account with the hosting provider", + }, + { id: "register-domain", title: "Register a domain name (optional)" }, + { id: "prod-db", title: "Set up production database" }, + ], + }, + { + id: "deployment", + title: "Deployment", + substeps: [ + { id: "build-process", title: "Configure Build process" }, + { + id: "env-hosting", + title: "Setup environment variables on hosting platform", + }, + { id: "deploy-app", title: "Deploy application to hosting" }, + { id: "setup-cicd", title: "Set up CI/CD" }, + { id: "test-deployed", title: "Test deployed application" }, + { id: "launch", title: "Launch application to users" }, + ], + }, + ]; + + // Dark app CLI script steps + const darkScriptSteps = [ + { + id: "get-cli", + title: "Open your favourite editor", + description: "e.g. On VSCode download our extension or Go to editor.darklang.com or get the CLI from https://github.com/darklang/dark/releases", + }, + { + id: "write-code", + title: "Write code", + notice: "Ready to go—no setup needed", + description: "write CLI scripts or create functions, types, DBs, HTTP handlers, Crons, Workers, etc.", + }, + { + id: "debug-code", + title: "Debug code", + notice: "nothing to setup", + description: "using traces", + }, + { + id: "run-code", + title: "Run code", + description: + "As soon as functions/types/constants are created they are accessible publicly (or privately) in the package manager", + }, + { + id: "share-code", + title: "Share code", + optional: true, + description: + "Run $ darklang share @user/myModule.myFnName or click on the Share button to instantly generate a shareable link for your code", + }, + { + id: "deploy-code", + title: "Deploy code", + optional: true, + description: + "Run $ darklang deploy @user/myModule.myFnName to deploy your code to our cloud or yours", + }, + ]; + + // Use CLI script steps only + const darkSteps = darkScriptSteps; + + // Stats + const nodeStats = { + timeSpent: "12mins 34s", + dependencyIssues: "5 errors", + packagesInstalled: "67 packages", + }; + + const darkStats = { + timeSpent: "1mins 23s", + dependencyIssues: "0 errors", + packagesInstalled: "0 packages", + }; + + // Animation effect for node steps + useEffect(() => { + let animationTimer: ReturnType; + + const progressAnimation = () => { + // Get current step info + const currentStepIndex = activeNodeStep; + + if (currentStepIndex >= nodeSteps.length) { + // Animation has finished all steps + return; + } + + const currentStep = nodeSteps[currentStepIndex]; + const currentStepId = currentStep.id; + const substepCount = currentStep.substeps.length; + const currentSubStepIndex = activeSubSteps[currentStepId] ?? -1; + + if (currentSubStepIndex < substepCount - 1) { + // Move to next substep + setActiveSubSteps(prev => ({ + ...prev, + [currentStepId]: currentSubStepIndex + 1, + })); + } else { + // Current step is complete, add to completed steps + setCompletedNodeSteps(prev => [...prev, currentStepIndex]); + + // Move to next main step + const nextStepIndex = currentStepIndex + 1; + + if (nextStepIndex < nodeSteps.length) { + setTimeout(() => { + setActiveNodeStep(nextStepIndex); + + // Pre-initialize substeps for the next step + setActiveSubSteps(prev => ({ + ...prev, + [nodeSteps[nextStepIndex].id]: -1, + })); + }, 400); // Slight delay before moving to next step + } + } + }; + + // Start the animation and run it every 700ms - slower for Node.js side + animationTimer = setInterval(progressAnimation, 700); + + return () => { + if (animationTimer) { + clearInterval(animationTimer); + } + }; + }, [activeNodeStep, activeSubSteps, nodeSteps]); + + // Handle the final step completion (when the deployment step is done) + useEffect(() => { + // If the last step is in the completed steps, we should mark all as completed + if (completedNodeSteps.includes(nodeSteps.length - 1)) { + setActiveNodeStep(nodeSteps.length); // Move past the last step + } + }, [completedNodeSteps, nodeSteps.length]); + + // Animation effect for Dark steps + useEffect(() => { + if (darkCompletedSteps < darkSteps.length) { + const darkInterval = setInterval(() => { + setDarkCompletedSteps(prev => + prev < darkSteps.length ? prev + 1 : prev, + ); + }, 1200); // Keep this at the faster speed + + return () => clearInterval(darkInterval); + } + }, [darkCompletedSteps, darkSteps.length]); + + // Helper to determine if a step is complete + const isNodeStepComplete = (index: number) => { + return completedNodeSteps.includes(index) || index < activeNodeStep; + }; + + // Helper to determine if a step is active + const isNodeStepActive = (index: number) => { + return index === activeNodeStep && activeNodeStep < nodeSteps.length; + }; + + // Helper to determine if a substep is complete + const isSubStepComplete = (stepIndex: number, subStepIndex: number) => { + if (isNodeStepComplete(stepIndex)) return true; + + const stepId = nodeSteps[stepIndex].id; + const currentSubStep = activeSubSteps[stepId] || -1; + + return stepIndex === activeNodeStep && subStepIndex <= currentSubStep; + }; + + // Helper to determine if a Dark step is complete + const isDarkStepComplete = (index: number) => { + return index < darkCompletedSteps; + }; + + // Handle step click for expansion + const handleStepClick = (stepIndex: number) => { + setExpandedSteps(prev => { + if (prev.includes(stepIndex)) { + return prev.filter(i => i !== stepIndex); + } else { + return [...prev, stepIndex]; + } + }); + }; + + // Helper to determine if substeps should be shown + const shouldShowSubsteps = (stepIndex: number) => { + return isNodeStepActive(stepIndex) || expandedSteps.includes(stepIndex); + }; + + return ( +
+
+

+ Get started in no time +

+ +
+ {/* Node.js App */} +
+

Building a Node app

+ + {/* Stats */} +
+
+
+ + + +
+
+
+ Time Spent on Setup +
+
+ {nodeStats.timeSpent} +
+
+
+ +
+
+ + + +
+
+
+ Dependency Issues +
+
+ {nodeStats.dependencyIssues} +
+
+
+ +
+
+ + + +
+
+
+ Packages Installed +
+
+ {nodeStats.packagesInstalled} +
+
+
+
+ + {/* Steps */} +
+ {/* Vertical line */} +
+ + {/* All steps */} +
+ {nodeSteps.map((step, stepIndex) => ( +
+ {/* Main step with circle */} +
handleStepClick(stepIndex)} + > +
+ {(isNodeStepComplete(stepIndex) || + isNodeStepActive(stepIndex)) && ( + + + + )} +
+
+ {step.title} +
+
+ + {/* Show substeps based on animation or user interaction */} + {shouldShowSubsteps(stepIndex) && ( +
+ {step.substeps.map((substep, subIndex) => { + const isCompleted = isSubStepComplete( + stepIndex, + subIndex, + ); + const isInProgress = + stepIndex === activeNodeStep && + subIndex === (activeSubSteps[step.id] || -1) + 1; + + return ( +
+ {/* Substep line */} +
+ + {/* Substep with circle */} +
+
+ {/* This creates the partial fill effect */} + {isCompleted && ( +
+ )} +
+
+ {substep.title} +
+
+
+ ); + })} +
+ )} +
+ ))} +
+
+
+ + {/* Dark App */} +
+

Building a Dark app

+ + {/* Stats */} +
+
+
+ + + +
+
+
+ Time Spent on Setup +
+
+ {darkStats.timeSpent} +
+
+
+ +
+
+ + + +
+
+
+ Dependency Issues +
+
+ {darkStats.dependencyIssues} +
+
+
+ +
+
+ + + +
+
+
+ Packages Installed +
+
+ {darkStats.packagesInstalled} +
+
+
+
+ + {/* Steps */} +
+ {/* Vertical line */} +
+ + {/* All steps */} +
+ {darkSteps.map((step, index) => ( +
+ {/* Main step with circle */} +
+
+ {isDarkStepComplete(index) && ( + + + + )} +
+ +
+
+ + {step.title} + + {step.optional && ( + + Optional + + )} + {step.notice && ( + + {step.notice} + + )} +
+ + {step.description && ( +
+ {step.description} +
+ )} +
+
+
+ ))} +
+
+
+
+
+
+ ); +}; + +export default DevelopmentSteps; diff --git a/src/pages/Home/Editing.tsx b/src/pages/Home/Editing.tsx new file mode 100644 index 0000000..122c2e4 --- /dev/null +++ b/src/pages/Home/Editing.tsx @@ -0,0 +1,88 @@ +import React from "react"; +import editingImg from "~/assets/editing3.png"; +import SectionTitle from "../../common/ui/SectionTitle"; +const Editing: React.FC = () => { + const features = [ + { + id: 1, + description: + "Darklang has a Language Server that provides real-time feedback, AI-powered completions, instant diagnostics, and easy access to language features. It wraps a lightweight VS Code extension for seamless integration and an intuitive coding experience.", + }, + { + id: 2, + description: + "Built entirely in Darklang, the language server follows the Language Server Protocol (LSP), making it easy to expand support to more editors so you can use Darklang in your favourite development environment.", + }, + { + id: 3, + description: + "The Language Server is designed to be flexible, so users can configure it to fit their needs", + }, + ]; + return ( +
+
+
+
+
+ + Powerful, Familiar, and Extensible editing + +

+ Edit code in your preferred environment—whether it's your local + editor, a browser-based IDE, or directly in the CLI. This is + made possible by Darklang's Language Server: +

+
+ {features.map(feature => ( +
+
+
+ + + +
+
+
+

+ {feature.description} +

+
+
+ ))} + + +
+
+ Darklang Editor Environment +
+
+
+
+ ); +}; +export default Editing; \ No newline at end of file diff --git a/src/pages/Home/GradualStaticTyping.tsx b/src/pages/Home/GradualStaticTyping.tsx new file mode 100644 index 0000000..af5099a --- /dev/null +++ b/src/pages/Home/GradualStaticTyping.tsx @@ -0,0 +1,71 @@ +import React from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +type FeaturePoint = { + text: string | React.ReactNode; +}; + +const GradualStaticTyping: React.FC = () => { + const featurePoints: FeaturePoint[] = [ + { text: "While prototyping, just run code until you hit a type error" }, + { + text: "After prototyping, run the full type checker to gain confidence your whole program works", + }, + { + text: ( + <> + ! and ? operators allow easy error handling while you prototype + + ), + }, + { + text: ( + <> + Automatic refactoring converts ! into proper error handling + + ) + }, + { text: "Full type-checking hints in VSCode or in LSP editors" }, + ]; + + return ( +
+
+ + Gradual Static Typing + + +

+ Gradual Static Typing allows running incomplete programs so you don't + need to ensure everything type checks when you're getting one path + working +

+ +
+ {/* Vertical line */} +
+ + {/* Feature points */} + {featurePoints.map((point, index) => ( +
+ {/* Circle marker */} +
+
+
+ + {/* Content */} +
+

+ {point.text} +

+
+
+ ))} +
+
+
+ ); +}; + +export default GradualStaticTyping; diff --git a/src/pages/Home/Hero.tsx b/src/pages/Home/Hero.tsx new file mode 100644 index 0000000..2b65963 --- /dev/null +++ b/src/pages/Home/Hero.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +// import Button from "../../common/ui/Button"; + +const Hero: React.FC = () => { + return ( +
+
+

+ Build above the cloud +

+ +

+ Darklang is an integrated language, framework, runtime, and editor for + building software— CLI scripts and REPLs, cloud apps, and more +

+ +

+ Write simple code that runs + everywhere. No setup.{" "} + Just code. +

+ + {/* */} + + {/* GIF placeholder */} + {/*
*/} +
+
+ ); +}; + +export default Hero; diff --git a/src/pages/Home/LanguageFeatures.tsx b/src/pages/Home/LanguageFeatures.tsx new file mode 100644 index 0000000..b13f5ac --- /dev/null +++ b/src/pages/Home/LanguageFeatures.tsx @@ -0,0 +1,220 @@ +import React, { useState } from "react"; + +import SectionTitle from "../../common/ui/SectionTitle"; +import CodeDisplay from "../../common/ui/CodeDisplay"; + +interface CodeTabProps { + isActive: boolean; + label: string; + onClick: () => void; +} + +interface FeatureItemProps { + children: React.ReactNode; +} + +interface TabData { + id: string; + label: string; + code: string; +} + +const CodeTab: React.FC = ({ isActive, label, onClick }) => ( + +); + +const FeatureItem: React.FC = ({ children }) => ( +
+
+ + + + +
+
{children}
+
+); + +// Main Component +const LanguageFeatures: React.FC = () => { + // Tab data + const tabs: TabData[] = [ + { + id: "functional", + label: "Functional Style", + code: `// Simple types +type UserAction = + | Click of String + | Type of String + | Submit + +// Match patterns +let handleAction (action:UserAction) : String = + match action with + | Click button -> $"Button '{button}' clicked." + | Type input -> $"User typed: '{input}'" + | Submit -> "Form submitted!" + +let actions = [Click "Login"; Type "Hello"; Submit] + +// First-class functions +let logs = actions |> Stdlib.List.map handleAction + +// Immutable and composable +logs |> List.iter printLine`, + }, + { + id: "records", + label: "Records & Enums", + code: `// Record +type Url = { + scheme : HttpScheme + domain : String + port : UInt16 + path : String + query : Option +} + +// Enum (aka Variant, Sum Type, or Abstract Data Types) +type UrlError = +| InvalidScheme(String) +| EmptyDomain +| InvalidPort(Int64) +| Unparseable(msg:String, context:String) + +// Aliases are just shorthands +type UrlParseResult = Result`, + }, + { + id: "option", + label: "Option & Result types", + code: `// Option type instead of null +type Option<'v> = +| Some of 'v +| None + +let findUser (users: List) (id: int) : Option = + users |> List.tryFind (fun user → user.Id = id) + +// Result type instead of exceptions +type Result<'Ok, 'Err> = +| Ok of 'Ok +| Error of 'Err + +let findUser +(users: List) +(id: int) +: Result = + let user = users |> List.tryFind (fun user → user.Id = id) + match user with + | Some user → Ok user + | None → Error NotFound`, + }, + { + id: "unicode", + label: "Unicode-First", + code: `// All characters in Darklang represent Extended Grapheme +// Clusters. + +// Characters represent what you see on screen +String.length "hello" // 5 +String.length "👨‍👩‍👧‍👦" // 1`, + }, + ]; + + const features = [ + "Built-in immutability and strong type system", + "Garbage-collected", + "Records and Enums for straightforward data modeling", + "Option and Result types keep errors explicit", + "Unicode-first", + ]; + + const [activeTab, setActiveTab] = useState(tabs[0].id); + + // Get current tab code + const getCurrentTabCode = () => { + const tab = tabs.find(tab => tab.id === activeTab); + return tab ? tab.code : ""; + }; + + return ( +
+
+
+ {/* Left side */} +
+ + Functional, Composable, and Fun to use + + +

+ Darklang is a statically-typed functional language built for + simplicity and composability. No null, no unexpected exceptions + —just predictable code that's easy to write, read, and maintain. +

+ +
+ {features.map((feature, index) => ( + + {feature} + + ))} +
+
+ + {/* Right side */} +
+
+ {/* Tabs navigation */} +
+ {tabs.map(tab => ( + setActiveTab(tab.id)} + /> + ))} +
+ + {/* Code display area */} +
+
+ +
+
+
+
+
+
+
+ ); +}; + +export default LanguageFeatures; diff --git a/src/pages/Home/Newsletter.tsx b/src/pages/Home/Newsletter.tsx new file mode 100644 index 0000000..9f566ad --- /dev/null +++ b/src/pages/Home/Newsletter.tsx @@ -0,0 +1,146 @@ +import React, { useState, useRef } from "react"; + +import Button from "../../common/ui/Button"; + +interface NewsletterProps { + className?: string; +} + +const Newsletter: React.FC = () => { + const [error, setError] = useState(""); + const [isSubmitted, setIsSubmitted] = useState(false); + const formRef = useRef(null); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + // Reset error state + setError(""); + + const emailInput = document.getElementById( + "newsletterEmail", + ) as HTMLInputElement; + if (!emailInput?.value) { + setError("Please enter your email address"); + return; + } + + try { + const response = await fetch( + "https://ops-corpsite.builtwithdark.com/signup", + { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: emailInput.value, + }), + }, + ); + + if (response.status === 200) { + setIsSubmitted(true); + } else { + setError("Error adding to waitlist"); + } + } catch (err) { + setError("Failed to subscribe. Please try again."); + console.error("Subscription error:", err); + } + }; + + return ( +
+
+
+
+

+ Send me project updates +

+

+ Get notified about new features, updates, Bug Fixes, project + milestones, and more +

+
+ + {!isSubmitted ? ( +
+
+
+ + {error && ( + + {error} + + )} +
+ +
+ +
+ + + + Monthly updates, no spam, unsubscribe anytime +
+
+ ) : ( +
+ Added to waitlist! + + + +
+ )} +
+
+
+ ); +}; + +export default Newsletter; diff --git a/src/pages/Home/PackageManager.tsx b/src/pages/Home/PackageManager.tsx new file mode 100644 index 0000000..4b4306b --- /dev/null +++ b/src/pages/Home/PackageManager.tsx @@ -0,0 +1,127 @@ +import React from "react"; + +import packageManagerImg from "~/assets/packageManager.png"; + +import SectionTitle from "../../common/ui/SectionTitle"; + +const PackageManager: React.FC = () => { + const features = [ + { + id: 1, + title: "No Install Step", + description: + "Built-in package management without npm-style installation processes or Python's painful package compatibility issues.", + }, + { + id: 2, + title: "Immutable", + description: + "Updates never overwrite existing code but create new versions with unique identifiers.", + }, + { + id: 3, + title: "No More Downloading the Internet", + description: + "Only download and upgrade the specific package items you use.", + }, + { + id: 4, + title: "Source-Controlled Package Management", + description: "The package manager functions as a source repository.", + }, + { + id: 5, + title: "Smart Dependency Management", + description: + "Automated dependency upgrades, as we track deprecation status, and know what functions are pure and safe to update.", + }, + { + id: 6, + title: "Version Flexibility", + description: + "Different packages can rely on different versions of other packages.", + }, + { + id: 7, + title: "Parallel Versioning", + description: + "Use multiple versions of the same package item at once: allows testing new versions without having to change an entire package version, lowering risk.", + }, + { + id: 8, + title: "Effortless Pre-Release Sharing", + description: + "Share pre-release functions trivially, without contributors needing to check out your git repo or set up anything.", + }, + { + id: 9, + title: "Zero Overhead Workflow", + description: + "No need for uploads, releases or other synchronization. No git or GitHub required (but you can sync to GitHub if you prefer).", + }, + ]; + + return ( +
+
+ + Next-gen Package Manager + +
+

+ Darklang has a rather unique package manager built directly into the + runtime, where functions and types are individually versioned and + immutable, taking a lot of the hassle out of package management. +

+
+
+
+ {features.map(feature => ( +
+
+
+ + + + +
+
+
+

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+ +
+ Package Manager Architecture +
+
+
+
+ ); +}; + +export default PackageManager; diff --git a/src/pages/Home/TraceDrivenDevelopment.tsx b/src/pages/Home/TraceDrivenDevelopment.tsx new file mode 100644 index 0000000..adfbf83 --- /dev/null +++ b/src/pages/Home/TraceDrivenDevelopment.tsx @@ -0,0 +1,185 @@ +import React from "react"; + +import { + HttpIcon, + WorkerIcon, + CronIcon, + CliIcon, + FnIcon, + MiscIcon, +} from "../../common/ui/Icons"; +import SectionTitle from "../../common/ui/SectionTitle"; +import { TraceCard } from "../../common/ui/Card"; + +const httpTraceData = `Made less than a minute ago +request: { + body: { + info: "testinfo", + }, + fullBody:"{\\\"info\\\":\\\"testinfo\\\"}", + headers: { + accept: "*/*", + content-length: "37", + ... + } +}`; + +const fnCallTraceData = `Made 4 minutes ago +getUserPaintings(userId) +▸ args: +"u-573829" +▸ returns: +[ +{ "id": "p-001", "title": "Mountain Sunset", "created": "2024-11-05" }, +{ "id": "p-002", "title": "Ocean Waves", "created": "2025-12-01" } +] +▸ duration: 47ms`; + +const cliTraceData = `Made 3 minutes ago +$ npm run deploy --env=staging +stdout: +Deploying to staging environment... +✓ Building application (2.3s) +✓ Running tests (1.7s) +✓ Uploading assets (4.2s) +✓ Deployment complete (8.7s) +exit: 0`; + +const workerTraceData = `Made 2 minutes ago +WORKER: media-processor +TASK_ID: wrk_8a72c9d3 +STATUS: completed +DURATION: 3.8s +RESULT: { +"processedFiles": 7, +"compressionRatio": 2.24 +}`; + +const cronTraceData = `Made 25 minutes ago +JOB_ID: cron_93f721a5 +NAME: data-sync-production +SCHEDULE: */15 * * * * +STATUS: success +DURATION: 42.3s +RESULT: { "records_processed": 8427 }`; + +const miscTraceData = `Create custom trace collectors +Made 17 minutes ago +EVENT: user.onboarding.completed +context: { +userId: "usr_27b391fe", +timestamp: "2025-04-18T15:28:13Z" +} +data: { +stepsCompleted: 4, +totalDuration: 462 +}`; + +const TraceDrivenDevelopment: React.FC = () => { + return ( +
+
+
+ {/* Left Side Content */} +
+ + Development with Real Data + + +

+ The best way to debug and refactor code is with the help of{" "} + + real user data + + . As your code executes, whether{" "} + + locally + {" "} + or in the{" "} + + cloud + + , traces are captured and made available in your development + workflow, making it easier to refactor code and debug issues. +

+ +

+ Traces are captured centrally in the package manager, stored + locally first, and securely available in your editing environment + - you control when and if they sync. +

+ + + See how traces work with real examples → + +
+ + {/* Right Side Stacked Cards*/} +
+
+ {/* Left column of cards */} + +
+ } + description="Capture and analyze HTTP requests and responses in real-time. Debug API integrations with complete request data." + traceData={httpTraceData} + color="blue" + > + + } + description="Inspect function inputs, outputs, and performance metrics. Debug complex operations with detailed traces." + traceData={fnCallTraceData} + color="magenta" + > + + } + description="Track CLI operations with detailed input and output records. Monitor script execution, environment variables, and command results for easy debugging." + traceData={cliTraceData} + color="purple" + > +
+ + {/* Right column of cards */} +
+ } + description="Track and monitor background workers and their execution. Observe task processing in real-time." + traceData={workerTraceData} + color="taupe" + > + + } + description="Monitor scheduled tasks and their execution results. Ensure automation runs correctly with real data." + traceData={cronTraceData} + color="orange" + > + + } + description="Customize your own trace types and visualizations. Monitor any aspect of your application with flexible data collection." + traceData={miscTraceData} + color="gray" + > +
+
+
+
+
+
+ ); +}; + +export default TraceDrivenDevelopment; diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx new file mode 100644 index 0000000..d7fb087 --- /dev/null +++ b/src/pages/Home/index.tsx @@ -0,0 +1,37 @@ +import Hero from "./Hero"; +import DevelopmentSteps from "./DevelopmentSteps"; +import BackendFeatures from "./BackendFeatures"; +import LanguageFeatures from "./LanguageFeatures"; +import AsyncRuntime from "./AsyncRuntime"; +import GradualStaticTyping from "./GradualStaticTyping"; +import PackageManager from "./PackageManager"; +import TraceDrivenDevelopment from "./TraceDrivenDevelopment"; +import Editing from "./Editing"; +import CLI from "./CLI"; +import DeploylessCloud from "./DeploylessCloud"; +import DesignedForGenAI from "./DesignedForGenAI"; +import Newsletter from "./Newsletter"; +import BlogPostsExample from "./BlogPostsExample"; + +const Home = () => { + return ( + <> + + + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/src/pages/Language/index.tsx b/src/pages/Language/index.tsx new file mode 100644 index 0000000..023649e --- /dev/null +++ b/src/pages/Language/index.tsx @@ -0,0 +1,70 @@ +import React from "react"; + +const Language: React.FC = () => { + return ( + +
+
+        it's an ML-style language yay
+      
+ +

+ Darklang is a statically-typed, functional/imperative hybrid programming language designed primarily for building cloud-based backend services. Its functional aspects draw inspiration from languages like OCaml, Elm, and Rust, emphasizing simplicity, type safety, and productivity while avoiding complex functional programming concepts like monads or category theory. +

+ +

+ Immutability: + Darklang embraces immutability, a core functional programming principle. Values in Darklang are immutable by default, eliminating race conditions in concurrent execution and making programs easier to reason about. +

+ +

+ No Exceptions, Use of Results and Options: + Instead of exceptions, which can complicate reasoning in functional programming, Darklang uses Result and Option types to handle errors and optional values. This approach, inspired by languages like Rust and OCaml, ensures explicit error handling and avoids issues associated with nulls or unchecked exceptions. +

+ +

+ First-Class Functions and Pipelines: + Darklang supports first-class functions, enabling functions to be passed as arguments, returned from other functions, and assigned to variables, a standard feature in functional languages. + It makes heavy use of pipelines (similar to F# or Elm), allowing developers to chain function calls in a readable, declarative way. For example, operations like List::map are used to transform collections functionally +

+ +

+ Implicit Returns: + Like many functional languages (e.g., Elm or Haskell), Darklang uses implicit returns, where the last expression in a function is automatically its return value. This reduces boilerplate and aligns with functional programming’s focus on expressions over statements. +

+ +

+ Simple Type System with Records and Enums: + Darklang employs a straightforward type system based on Records and Enums (also known as variants or sum types), similar to Rust, Elm, or OCaml. These types are expressive enough to model complex data structures while remaining simpler than object-oriented class hierarchies. + For example, a Url type might be defined as a record with fields like scheme, domain, and port, while a UrlError enum could represent possible errors like InvalidScheme or EmptyDomain. This approach avoids the complexity of inheritance and aligns with functional programming’s preference for algebraic data types. +

+ +

+ Functional/Imperative Hybrid: + While primarily functional, Darklang is described as a functional/imperative hybrid, drawing from OCaml, Elm, and Rust rather than purely functional languages like Haskell. This hybrid nature makes it accessible to developers familiar with imperative or object-oriented languages, avoiding esoteric functional concepts like monads, lenses, or combinators. + The language prioritizes developer productivity by allowing imperative-style code where needed, but its functional core ensures type safety and immutability. +

+ +

+ Asynchronous Programming Without await: + Darklang’s runtime is fully asynchronous, but it avoids the explicit await keywords common in languages like JavaScript or TypeScript. Instead, it uses data dependencies to manage concurrency, a functional approach that ensures operations wait for their inputs without exposing threading complexity. + This design eliminates race conditions (due to immutability) and simplifies concurrent programming, aligning with functional programming’s emphasis on declarative concurrency models. +

+ +

+ Unicode-Aware Strings: + Darklang’s string handling is designed for modern applications, using Extended Grapheme Clusters to represent characters (e.g., emojis like 👨‍👩‍👦‍👦 are treated as single characters). This functional approach to string representation avoids the pitfalls of byte-based or UTF-16 strings in older languages, ensuring predictable behavior in text processing. +

+ +

+ Reliability: Static typing and immutability ensure that compiled code is robust, a critical feature for cloud backends. + Concurrency: Immutability and data-driven async execution simplify concurrent programming, a key requirement for scalable backend services. + Productivity: Features like pipelines, implicit returns, and gradual typing make functional programming accessible and efficient, aligning with Darklang’s goal of simplifying backend development. +

+ +
+ + ); +}; + +export default Language; diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx new file mode 100644 index 0000000..832eac3 --- /dev/null +++ b/src/pages/NotFound.tsx @@ -0,0 +1,31 @@ +import { Link } from "react-router-dom"; + +const NotFound = () => { + return ( +
+

404

+

+ Page Not Found +

+

+ The page you're looking for doesn't exist or has been moved. +

+
+ + Go Home + + + Visit Docs + +
+
+ ); +}; + +export default NotFound; diff --git a/src/pages/PackageDetail/PackageDetailSidebar.tsx b/src/pages/PackageDetail/PackageDetailSidebar.tsx new file mode 100644 index 0000000..21709b2 --- /dev/null +++ b/src/pages/PackageDetail/PackageDetailSidebar.tsx @@ -0,0 +1,530 @@ +import React, { useCallback, useMemo, useEffect, useRef } from "react"; +import { BreadcrumbNavigation } from '../../components'; +import { useDarkPackagesApi } from './darkPackagesApi'; +import { PackageData } from './types'; +import { SelectedItem } from './components/types'; + +interface PackageStats { + functions: number; + types: number; + constants: number; + submodules: number; +} + +interface SidebarPackage { + path: string; + name: string; + stats: PackageStats; +} + +interface SubmoduleData { + name: string; + fullName: string; +} + +interface ProcessedModuleData { + functions: string[]; + types: string[]; + constants: string[]; + submodules: SubmoduleData[]; +} + +interface SidebarState { + packages: SidebarPackage[]; + expandedItems: Set; + itemsData: Record; + loadingStates: Record; + selectedPackage: string; + selectedItem: SelectedItem | null; + searchQuery: string; + searchLoading: boolean; + activeTab: string; +} + +interface SidebarActions { + onNavigate: (path: string) => void; + onPackageClick: (packagePath: string) => void; + onItemToggle: (packagePath: string) => void; + onItemClick: (itemType: 'function' | 'type' | 'constant', itemName: string, packagePath: string, event?: React.MouseEvent) => void; + onSubmoduleClick: (submoduleFullName: string, event?: React.MouseEvent) => void; + onSearchChange: (value: string) => void; + onFetchItemData: (moduleName: string) => void; + updateState: { + setSearchLoading: (loading: boolean) => void; + setExpandedItems: (items: Set) => void; + setItemsData: (data: Record | ((prev: Record) => Record)) => void; + setPackages: (packages: SidebarPackage[] | ((prev: SidebarPackage[]) => SidebarPackage[])) => void; + setActiveTab: (tab: string) => void; + }; +} + +interface SidebarProps { + packageData: PackageData | null; + state: SidebarState; + actions: SidebarActions; +} + +export const PackageDetailSidebar: React.FC = ({ + packageData, + state, + actions +}) => { + const api = useDarkPackagesApi(); + const lastSearchQuery = useRef(""); + + const searchInAllPackages = useCallback(async (query: string) => { + if (!query.trim()) return; + + actions.updateState.setSearchLoading(true); + const packagesToExpand = new Set(); + + try { + const searchPromises = state.packages.map(async (pkg: SidebarPackage) => { + // Check existing data first + if (state.itemsData[pkg.path]) { + const hasMatches = searchInPackageData(state.itemsData[pkg.path], query); + if (hasMatches) packagesToExpand.add(pkg.path); + return { pkg, hasMatches }; + } + + // Fetch data for new packages + try { + const processedData: ProcessedModuleData = await api.getProcessedModuleData(pkg.path); + + // Update data + actions.updateState.setItemsData((prev: Record) => ({ + ...prev, + [pkg.path]: processedData + })); + + // Update package stats + actions.updateState.setPackages(prevPkgs => prevPkgs.map((p: SidebarPackage) => + p.path === pkg.path + ? { ...p, stats: { ...p.stats, submodules: processedData.submodules.length } } + : p + )); + + const hasMatches = searchInPackageData(processedData, query); + if (hasMatches) packagesToExpand.add(pkg.path); + + return { pkg, hasMatches }; + } catch (err) { + console.error(`Error searching in package ${pkg.path}:`, err); + return { pkg, hasMatches: false }; + } + }); + + await Promise.all(searchPromises); + + // Expand packages with matches + const newExpandedItems = new Set([...state.expandedItems, ...packagesToExpand]); + actions.updateState.setExpandedItems(newExpandedItems); + + } finally { + actions.updateState.setSearchLoading(false); + } + }, [state.packages, state.itemsData, api, actions.updateState]); + + // Helper function to search within package data + const searchInPackageData = useCallback((packageData: ProcessedModuleData, query: string): boolean => { + const queryLower = query.toLowerCase(); + return ( + packageData.functions?.some((fn: string) => fn.toLowerCase().includes(queryLower)) || + packageData.types?.some((type: string) => type.toLowerCase().includes(queryLower)) || + packageData.constants?.some((constant: string) => constant.toLowerCase().includes(queryLower)) || + packageData.submodules?.some((submod: SubmoduleData) => submod.name.toLowerCase().includes(queryLower)) + ); + }, []); + + // Debounced search effect + useEffect(() => { + if (state.searchQuery !== lastSearchQuery.current) { + lastSearchQuery.current = state.searchQuery; + + if (state.searchQuery.trim()) { + const timer = setTimeout(() => { + searchInAllPackages(state.searchQuery); + }, 300); + return () => clearTimeout(timer); + } else { + // Clear search - preserve selected package expansion + if (state.selectedPackage) { + actions.updateState.setExpandedItems(new Set([state.selectedPackage])); + } else { + actions.updateState.setExpandedItems(new Set()); + } + actions.updateState.setSearchLoading(false); + } + } + }, [state.searchQuery, searchInAllPackages, state.selectedPackage, actions.updateState]); + + // Auto-expand packages with matches + useEffect(() => { + if (state.searchQuery.trim()) { + const query = state.searchQuery.toLowerCase(); + const currentExpanded = new Set(state.expandedItems); + let hasNewMatches = false; + + state.packages.forEach((pkg: SidebarPackage) => { + const pkgData = state.itemsData[pkg.path]; + if (pkgData && !currentExpanded.has(pkg.path)) { + const hasMatches = searchInPackageData(pkgData, query); + if (hasMatches) { + currentExpanded.add(pkg.path); + hasNewMatches = true; + } + } + }); + + if (hasNewMatches) { + actions.updateState.setExpandedItems(currentExpanded); + } + } + }, [state.itemsData, state.searchQuery, state.packages, state.expandedItems, actions.updateState, searchInPackageData]); + + // Auto-switch tabs based on content + useEffect(() => { + if (state.selectedPackage && state.itemsData[state.selectedPackage]) { + const currentPackageData = state.itemsData[state.selectedPackage]; + + if (state.activeTab === "Functions" && + (!currentPackageData.functions || currentPackageData.functions.length === 0) && + currentPackageData.submodules && currentPackageData.submodules.length > 0) { + actions.updateState.setActiveTab("Submodules"); + } + } + }, [state.itemsData, state.selectedPackage, state.activeTab, actions.updateState]); + + // Filter items based on search + const getFilteredItems = useCallback((packagePath: string, items: string[]): string[] => { + if (!items || !Array.isArray(items) || !state.searchQuery.trim()) { + return items || []; + } + + const query = state.searchQuery.toLowerCase(); + const pkgInfo = state.packages.find((pkg: SidebarPackage) => pkg.path === packagePath); + const packageName = pkgInfo?.name || ''; + + // If package name matches, show all items + if (packageName.toLowerCase().includes(query)) { + return items; + } + + // Filter items by name + return items.filter((item: string) => item && item.toLowerCase().includes(query)); + }, [state.searchQuery, state.packages]); + + // Filter packages based on search + const filteredPackages = useMemo((): SidebarPackage[] => { + if (!state.searchQuery.trim()) { + return state.packages; + } + + const query = state.searchQuery.toLowerCase(); + return state.packages.filter((pkg: SidebarPackage) => { + if (pkg.name.toLowerCase().includes(query)) { + return true; + } + + const pkgData = state.itemsData[pkg.path]; + return pkgData ? searchInPackageData(pkgData, query) : false; + }); + }, [state.packages, state.itemsData, state.searchQuery, searchInPackageData]); + + if (!packageData) { + return ( +
+
+
Loading...
+
+
+ ); + } + + return ( +
+
+

+ {packageData.isRootModule ? packageData.fullName : packageData.fullName.split('.').slice(0, -1).join('.')} +

+ + + +
+ actions.onSearchChange(e.target.value)} + placeholder="Search submodules, functions, types, and constants..." + className="w-full py-5 text-sm text-white placeholder-gray-400 focus:outline-none text-ellipsis bg-transparent" + disabled={state.searchLoading} + /> +
+ {state.searchQuery && ( + + )} + {state.searchLoading ? ( +
+ + + +
+ ) : ( + + + + + + + + )} +
+
+ {state.searchLoading && ( +
+ Searching all packages... +
+ )} +
+ +
+
+
+ + + +
+
+ +
+ {state.searchQuery.trim() && filteredPackages.length === 0 ? ( +
+

No results found for "{state.searchQuery}"

+

Try different keywords

+
+ ) : ( +
+ {filteredPackages.map((pkg: SidebarPackage) => ( + + ))} +
+ )} +
+
+
+ ); +}; + +interface PackageItemProps { + pkg: SidebarPackage; + state: SidebarState; + actions: SidebarActions; + getFilteredItems: (packagePath: string, items: string[]) => string[]; +} + +const PackageItem: React.FC = ({ + pkg, + state, + actions, + getFilteredItems +}) => { + const isExpanded = state.expandedItems.has(pkg.path); + const isSelected = state.selectedPackage === pkg.path; + const isLoading = state.loadingStates[pkg.path]; + const packageData = state.itemsData[pkg.path]; + + return ( +
+
+
+
{ + const target = e.target as Element; + const isInsideDropdown = target.closest('.dropdown-content'); + if (isInsideDropdown) return; + actions.onPackageClick(pkg.path); + actions.onItemToggle(pkg.path); + }} + > +
+ + + + + +
+
+
{pkg.name}
+
Lorem ipsum dolor sit amet consectetur.
+
+ {renderStatBadge(pkg.stats.functions, 'Functions', undefined, isSelected)} + {renderStatBadge(pkg.stats.types, 'Types', undefined, isSelected)} + {renderStatBadge(pkg.stats.constants, 'Constants', 'Constant', isSelected)} + {renderStatBadge(pkg.stats.submodules, 'Submodules', 'Submodule', isSelected)} +
+
+
+ +
{ + e.stopPropagation(); + actions.onItemToggle(pkg.path); + }} + > + + + +
+
+
+ + {isExpanded && ( +
+ {isLoading ? ( +
+
Loading functions, types, and constants...
+
+ ) : packageData ? ( + + ) : ( +
+
No data loaded yet
+ +
+ )} +
+ )} +
+ ); +}; + +// Helper function for stat badges +const renderStatBadge = (count: number, pluralLabel: string, singularLabel?: string, isSelected?: boolean) => { + const label = count === 1 && singularLabel ? singularLabel : pluralLabel; + return ( +
+ {count || 0} {label} +
+ ); +}; + +interface PackageDropdownContentProps { + packageData: ProcessedModuleData; + packagePath: string; + state: SidebarState; + actions: SidebarActions; + getFilteredItems: (packagePath: string, items: string[]) => string[]; +} + +const PackageDropdownContent: React.FC = ({ + packageData, + packagePath, + state, + actions, + getFilteredItems +}) => { + const submodules = packageData.submodules || []; + const filteredSubmodules = state.searchQuery.trim() + ? submodules.filter((submod: SubmoduleData) => submod.name.toLowerCase().includes(state.searchQuery.toLowerCase())) + : submodules; + + const filteredFunctions = getFilteredItems(packagePath, packageData.functions || []); + const filteredTypes = getFilteredItems(packagePath, packageData.types || []); + const filteredConstants = getFilteredItems(packagePath, packageData.constants || []); + + const hasAnyItems = ( + filteredSubmodules.length > 0 || + filteredFunctions.length > 0 || + filteredTypes.length > 0 || + filteredConstants.length > 0 + ); + + if (!hasAnyItems) { + return ( +
+
No submodules, functions, types, or constants found
+ {state.searchQuery && ( +
Try clearing the search filter
+ )} +
+ ); + } + + const sections = [ + { title: 'Submodules', items: filteredSubmodules, icon: '📦', color: '', onClick: actions.onSubmoduleClick, isSubmodule: true }, + { title: 'Functions', items: filteredFunctions.map(name => ({ name })), icon: 'f', color: 'text-blue-dbg', onClick: (name: string, e?: React.MouseEvent) => actions.onItemClick('function', name, packagePath, e) }, + { title: 'Types', items: filteredTypes.map(name => ({ name })), icon: 'T', color: 'text-purple-400', onClick: (name: string, e?: React.MouseEvent) => actions.onItemClick('type', name, packagePath, e) }, + { title: 'Constants', items: filteredConstants.map(name => ({ name })), icon: '◊', color: 'text-yellow-400', onClick: (name: string, e?: React.MouseEvent) => actions.onItemClick('constant', name, packagePath, e) } + ]; + + return ( + <> + {sections.map(section => { + if (section.items.length === 0) return null; + + return ( +
+

+ {section.title} ({section.items.length}) +

+
+ {section.items.map((item: any) => ( +
section.isSubmodule ? section.onClick(item.fullName, e) : section.onClick(item.name, e)} + > + {section.icon} + {item.name} +
+ ))} +
+
+ ); + })} + + ); +}; \ No newline at end of file diff --git a/src/pages/PackageDetail/components/ActionButton.tsx b/src/pages/PackageDetail/components/ActionButton.tsx new file mode 100644 index 0000000..3ceebcc --- /dev/null +++ b/src/pages/PackageDetail/components/ActionButton.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +interface ActionButtonProps { + text: string; + variant?: 'primary' | 'secondary'; + onClick?: () => void; + className?: string; +} + +const ActionButton: React.FC = ({ text, variant = 'secondary', onClick, className = '' }) => { + const baseStyles = + 'px-3 py-1.5 rounded text-xs font-medium transition-colors border'; + + const primary = 'bg-[#343333] hover:bg-purple-dbg text-white border-purple-dbg'; + const secondary = 'bg-[#343333] hover:bg-gray-custom text-gray-300 border-purple-dbg'; + + return ( + + ); +}; + +export default ActionButton; diff --git a/src/pages/PackageDetail/components/PackageHeader.tsx b/src/pages/PackageDetail/components/PackageHeader.tsx new file mode 100644 index 0000000..7c1cfc0 --- /dev/null +++ b/src/pages/PackageDetail/components/PackageHeader.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { PackageData } from '../types'; +import TabNavigation from './TabNavigation'; +import { SidebarPackage, SelectedItem } from './types'; + +interface PackageHeaderProps { + packageData: PackageData; + selectedPackage: string; + sidebarItemsData: Record; + sidebarPackages: SidebarPackage[]; + activeTab: string; + selectedItem: SelectedItem | null; + onTabChange: (tab: string) => void; + onFetchSidebarItemData: (packageName: string) => void; +} + +const PackageHeader: React.FC = ({ + packageData, + selectedPackage, + sidebarItemsData, + sidebarPackages, + activeTab, + selectedItem, + onTabChange, + onFetchSidebarItemData +}) => { + const currentSidebarData = sidebarItemsData[selectedPackage]; + const functionsCount = currentSidebarData?.functions?.length || packageData?.functions || packageData?.totalFunctions || 0; + const typesCount = currentSidebarData?.types?.length || packageData?.types || packageData?.totalTypes || 0; + const constantsCount = currentSidebarData?.constants?.length || packageData?.constants || packageData?.totalConstants || 0; + const submodulesCount = currentSidebarData?.submodules?.length || packageData?.subModules?.length || 0; + + return ( +
+ {/* Package Header */} +
+

{packageData.name}

+

{packageData.description}

+ + {/* Stats */} +
+
+ + {functionsCount} Functions + +
+
+ + {typesCount} Types + +
+
+ + {constantsCount} Constant{constantsCount !== 1 ? 's' : ''} + +
+ {(submodulesCount > 0 || packageData?.isRootModule) && ( +
+ + {submodulesCount} {packageData?.isRootModule ? 'Modules' : 'Submodules'} + +
+ )} +
+ + {/* Tabs */} + +
+
+ ); +}; + +export default PackageHeader; \ No newline at end of file diff --git a/src/pages/PackageDetail/components/TabContent.tsx b/src/pages/PackageDetail/components/TabContent.tsx new file mode 100644 index 0000000..4662509 --- /dev/null +++ b/src/pages/PackageDetail/components/TabContent.tsx @@ -0,0 +1,183 @@ +import React from 'react'; +import ActionButton from './ActionButton'; +import { SelectedItem } from './types'; + +interface TabItem { + name: string; + signature?: string; + description: string; +} + +interface TabContentProps { + type: 'functions' | 'types' | 'constants'; + selectedPackage: string; + sidebarItemsData: Record; + sidebarLoadingStates: Record; + searchQuery: string; + selectedItem: SelectedItem | null; + onFetchItemData: (packageName: string) => void; + onSearchClear: () => void; +} + +const TabContent: React.FC = ({ + type, + selectedPackage, + sidebarItemsData, + sidebarLoadingStates, + searchQuery, + selectedItem, + onFetchItemData, + onSearchClear +}) => { + const currentSidebarData = sidebarItemsData[selectedPackage]; + const isLoading = sidebarLoadingStates[selectedPackage] || false; + + // Get the appropriate data based on type + const getItemData = (): TabItem[] => { + if (!currentSidebarData) return []; + + switch (type) { + case 'functions': + return currentSidebarData.fullFunctionData || []; + case 'types': + return currentSidebarData.fullTypeData || []; + case 'constants': + return currentSidebarData.fullConstantData || []; + default: + return []; + } + }; + + const rawData = getItemData(); + + // Filter items based on search query + const filteredItems = rawData.filter((item: TabItem) => { + if (!searchQuery.trim()) return true; + const query = searchQuery.toLowerCase(); + + const name = item.name?.toLowerCase() || ''; + const description = item.description?.toLowerCase() || ''; + const signature = item.signature?.toLowerCase() || ''; + + return name.includes(query) || description.includes(query) || signature.includes(query); + }); + + // Get display configuration based on type + const getDisplayConfig = () => { + switch (type) { + case 'functions': + return { + singular: 'function', + plural: 'functions', + color: 'text-blue-dbg', + emptyMessage: 'No functions found for this module.' + }; + case 'types': + return { + singular: 'type', + plural: 'types', + color: 'text-purple-dbg', + emptyMessage: 'No types found for this module.' + }; + case 'constants': + return { + singular: 'constant', + plural: 'constants', + color: 'text-sand', + emptyMessage: 'No constants found for this module.' + }; + default: + return { + singular: 'item', + plural: 'items', + color: 'text-gray-400', + emptyMessage: 'No items found.' + }; + } + }; + + const config = getDisplayConfig(); + + // Handle loading state + if (isLoading) { + return ( +
+
Loading {config.plural}...
+
Fetching {config.singular} information
+
+ ); + } + + // Handle case where we need to fetch data + if (!currentSidebarData && selectedPackage) { + onFetchItemData(selectedPackage); + return ( +
+
Loading {config.plural}...
+
Fetching {config.singular} information
+
+ ); + } + + // Handle empty state + if (filteredItems.length === 0) { + return ( +
+ {searchQuery ? ( + <> +

No {config.plural} found matching "{searchQuery}"

+ + + ) : ( +

{config.emptyMessage}

+ )} +
+ ); + } + + return ( +
+ {searchQuery && ( +
+ Showing {filteredItems.length} of {rawData.length} {config.plural} +
+ )} + + {filteredItems.map((item: TabItem, index: number) => ( +
+
+

+ {type === 'functions' ? item.signature : item.name} +

+

+ {item.description} +

+
+ +
+ + + +
+
+ ))} +
+ ); +}; + +export default TabContent; \ No newline at end of file diff --git a/src/pages/PackageDetail/components/TabNavigation.tsx b/src/pages/PackageDetail/components/TabNavigation.tsx new file mode 100644 index 0000000..90f882e --- /dev/null +++ b/src/pages/PackageDetail/components/TabNavigation.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { PackageData } from '../types'; +import { SidebarPackage, SelectedItem } from './types'; + +interface TabNavigationProps { + packageData: PackageData; + selectedPackage: string; + sidebarItemsData: Record; + sidebarPackages: SidebarPackage[]; + activeTab: string; + selectedItem: SelectedItem | null; + onTabChange: (tab: string) => void; + onFetchSidebarItemData: (packageName: string) => void; +} + +const TabNavigation: React.FC = ({ + packageData, + selectedPackage, + sidebarItemsData, + sidebarPackages, + activeTab, + selectedItem, + onTabChange, + onFetchSidebarItemData +}) => { + const handleTabClick = (tab: string) => { + onTabChange(tab); + + const tabToTypeMap = { + Functions: 'function', + Types: 'type', + Constants: 'constant' + }; + + if (selectedItem && selectedItem.type !== tabToTypeMap[tab as keyof typeof tabToTypeMap]) { + window.history.replaceState(null, '', window.location.pathname + window.location.search); + } + }; + + const handleSubmodulesTabClick = () => { + onTabChange("Submodules"); + window.history.replaceState(null, '', window.location.pathname + window.location.search); + + const currentPackageData = sidebarItemsData[selectedPackage]; + if (!currentPackageData && selectedPackage) { + onFetchSidebarItemData(selectedPackage); + } + }; + + const currentSidebarData = sidebarItemsData[selectedPackage]; + const hasSubmodulesFromSidebar = currentSidebarData && currentSidebarData.submodules && currentSidebarData.submodules.length > 0; + const currentSidebarPackage = sidebarPackages.find(pkg => pkg.path === selectedPackage); + const hasSubmodulesFromStats = currentSidebarPackage && currentSidebarPackage.stats.submodules > 0; + + const hasRootModuleSubmodules = packageData.isRootModule && packageData.subModules && packageData.subModules.length > 0; + const hasSubmodules = hasSubmodulesFromSidebar || hasSubmodulesFromStats || hasRootModuleSubmodules; + + return ( +
+
+ {hasSubmodules && ( + + )} + + {["Functions", "Types", "Constants"].map((tab) => ( + + ))} +
+
+ ); +}; + +export default TabNavigation; \ No newline at end of file diff --git a/src/pages/PackageDetail/components/index.ts b/src/pages/PackageDetail/components/index.ts new file mode 100644 index 0000000..b6d5e1f --- /dev/null +++ b/src/pages/PackageDetail/components/index.ts @@ -0,0 +1,4 @@ +export { default as PackageHeader } from './PackageHeader'; +export { default as TabNavigation } from './TabNavigation'; +export { default as TabContent } from './TabContent'; +export * from './types'; diff --git a/src/pages/PackageDetail/components/types.ts b/src/pages/PackageDetail/components/types.ts new file mode 100644 index 0000000..8a9a35d --- /dev/null +++ b/src/pages/PackageDetail/components/types.ts @@ -0,0 +1,15 @@ +export interface SidebarPackage { + name: string; + path: string; + stats: { + functions: number; + types: number; + constants: number; + submodules: number; + }; +} + +export interface SelectedItem { + type: 'function' | 'type' | 'constant' | null; + name: string; +} \ No newline at end of file diff --git a/src/pages/PackageDetail/darkPackagesApi.ts b/src/pages/PackageDetail/darkPackagesApi.ts new file mode 100644 index 0000000..06d14a9 --- /dev/null +++ b/src/pages/PackageDetail/darkPackagesApi.ts @@ -0,0 +1,625 @@ +import { SubModule, PackageData } from './types'; + +// API Response Types +export interface ApiFunction { + name?: { name: string }; + parameters?: Array<{ name: string; typ: any }>; + returnType?: any; + description?: string; +} + +export interface ApiType { + name?: { name: string }; + description?: string; +} + +export interface ApiConstant { + name?: { name: string }; + description?: string; +} + +export interface ApiResponse { + fns?: ApiFunction[]; + types?: ApiType[]; + constants?: ApiConstant[]; + submodules?: string[][]; +} + +// Processed Data Types +export interface ProcessedSubmodule { + name: string; + fullName: string; +} + +export interface ProcessedFunction { + name: string; + signature: string; + description: string; +} + +export interface ProcessedType { + name: string; + description: string; +} + +export interface ProcessedConstant { + name: string; + description: string; +} + +export interface ProcessedItemData { + functions: string[]; + types: string[]; + constants: string[]; + submodules: ProcessedSubmodule[]; + fullFunctionData?: ProcessedFunction[]; + fullTypeData?: ProcessedType[]; + fullConstantData?: ProcessedConstant[]; +} + +export interface TypeDefinition { + declaration: any; + deprecated: any; + description: string; + id: string; + name: { + modules: string[]; + name: string; + owner: string; + }; +} + +export interface ModuleCounts { + functions: number; + types: number; + constants: number; +} + +const PRIMITIVE_TYPE_MAP = { + TBool: 'Bool', + TString: 'String', + TChar: 'Char', + TInt64: 'Int64', + TUInt64: 'UInt64', + TInt8: 'Int8', + TUInt8: 'UInt8', + TInt16: 'Int16', + TUInt16: 'UInt16', + TInt32: 'Int32', + TUInt32: 'UInt32', + TFloat: 'Float', + TUnit: 'Unit', + TDateTime: 'DateTime', + TUuid: 'Uuid', + TBytes: 'Bytes' +} as const; + +const UNIT_TYPES = new Set(['Unit', 'unit', 'undefined']); +const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +class DarkPackagesApi { + private readonly baseUrl = 'http://dark-packages.dlio.localhost:11001/search'; + private readonly typeUrl = 'http://dark-packages.dlio.localhost:11001/type/get'; + private readonly typeCache = new Map(); + + private async fetchData(url: string): Promise { + try { + const response = await fetch(url); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); + } + return await response.json(); + } catch (error) { + console.error('API Fetch Error:', error); + throw error; + } + } + + private buildSearchUrl(moduleName: string, entityTypes: string, searchDepth = 'direct'): string { + return `${this.baseUrl}?modules=${moduleName}&searchDepth=${searchDepth}&entityTypes=${entityTypes}`; + } + + private isTypeId(value: string): boolean { + return UUID_REGEX.test(value); + } + + private async fetchTypeDefinition(typeId: string): Promise { + try { + const response = await fetch(`${this.typeUrl}/${typeId}`); + return response.ok ? await response.json() : null; + } catch (error) { + console.error(`Error fetching type definition for ${typeId}:`, error); + return null; + } + } + + private getTypeNameFromDefinition(typeDef: TypeDefinition): string { + return typeDef.name.name; + } + + private async resolveCachedOrFetchType(typeId: string): Promise { + if (this.typeCache.has(typeId)) { + return this.typeCache.get(typeId)!; + } + + const typeDef = await this.fetchTypeDefinition(typeId); + if (typeDef) { + const typeName = this.getTypeNameFromDefinition(typeDef); + this.typeCache.set(typeId, typeName); + return typeName; + } + + return typeId; + } + + private async processPrimitiveType(typeObj: any): Promise { + for (const [key, value] of Object.entries(PRIMITIVE_TYPE_MAP)) { + if (typeObj[key]) return value; + } + return null; + } + + private async processCollectionType(typeObj: any): Promise { + if (typeObj.TList) { + const innerType = await this.getTypeString(typeObj.TList[0]); + return `List<${innerType}>`; + } + if (typeObj.TDict) { + const innerType = await this.getTypeString(typeObj.TDict[0]); + return `Dict<${innerType}>`; + } + return null; + } + + private async processCustomType(typeObj: any): Promise { + if (!typeObj.TCustomType) return null; + + const packageInfo = typeObj.TCustomType[0]?.Ok?.[0]?.Package; + if (packageInfo && Array.isArray(packageInfo) && packageInfo.length > 0) { + const typeId = packageInfo[0]; + + if (typeof typeId === 'string' && this.isTypeId(typeId)) { + const baseTypeName = await this.resolveCachedOrFetchType(typeId); + const typeArgs = typeObj.TCustomType[1] || []; + + if (typeArgs.length > 0) { + const argStrings = await Promise.all( + typeArgs.map((arg: any) => this.getTypeString(arg)) + ); + return `${baseTypeName}<${argStrings.join(', ')}>`; + } + return baseTypeName; + } + } + + // Fallback - handle non-UUID custom types + const typeName = typeObj.TCustomType[0]?.Ok?.[0]?.Package || 'CustomType'; + const typeArgs = typeObj.TCustomType[1] || []; + if (typeArgs.length > 0) { + const argStrings = await Promise.all( + typeArgs.map((arg: any) => this.getTypeString(arg)) + ); + return `${typeName}<${argStrings.join(', ')}>`; + } + return typeName; + } + + private async getTypeString(typeObj: any): Promise { + if (!typeObj) return 'Unknown'; + + // Handle primitive types + const primitiveType = await this.processPrimitiveType(typeObj); + if (primitiveType) return primitiveType; + + // Handle collection types + const collectionType = await this.processCollectionType(typeObj); + if (collectionType) return collectionType; + + // Handle type variables + if (typeObj.TVariable) { + const varName = typeObj.TVariable[0] || typeObj.TVariable; + return `'${varName}`; + } + + // Handle function types + if (typeObj.TFn) { + const params = typeObj.TFn[0] || []; + const returnType = typeObj.TFn[1]; + + if (params.length === 0) { + const retType = await this.getTypeString(returnType); + return `() -> ${retType}`; + } + + const paramTypes = await Promise.all( + params.map((param: any) => this.getTypeString(param)) + ); + const retType = await this.getTypeString(returnType); + + return `${paramTypes.join(' -> ')} -> ${retType}`; + } + + // Handle custom types + const customType = await this.processCustomType(typeObj); + if (customType) return customType; + + // Handle string type IDs + if (typeof typeObj === 'string') { + return this.isTypeId(typeObj) + ? await this.resolveCachedOrFetchType(typeObj) + : typeObj; + } + + // Handle arrays and objects with type IDs + if (Array.isArray(typeObj)) { + if (typeObj.length === 2 && typeof typeObj[0] === 'string' && this.isTypeId(typeObj[0])) { + const baseTypeName = await this.resolveCachedOrFetchType(typeObj[0]); + const typeArgs = typeObj[1]; + + if (typeArgs && Array.isArray(typeArgs) && typeArgs.length > 0) { + const argStrings = await Promise.all( + typeArgs.map((arg: any) => this.getTypeString(arg)) + ); + return `${baseTypeName}<${argStrings.join(', ')}>`; + } + return baseTypeName; + } + return typeObj.length > 0 ? await this.getTypeString(typeObj[0]) : 'Unknown'; + } + + // Handle objects with type ID keys + if (typeof typeObj === 'object' && typeObj !== null) { + const keys = Object.keys(typeObj); + + for (const key of keys) { + if (this.isTypeId(key)) { + const baseTypeName = await this.resolveCachedOrFetchType(key); + const value = typeObj[key]; + + if (value && Array.isArray(value) && value.length > 0) { + const argStrings = await Promise.all( + value.map((arg: any) => this.getTypeString(arg)) + ); + return `${baseTypeName}<${argStrings.join(', ')}>`; + } + return baseTypeName; + } + } + + // Handle single-key objects + if (keys.length === 1) { + const key = keys[0]; + const value = typeObj[key]; + + if (typeof value === 'string' && this.isTypeId(value)) { + return await this.resolveCachedOrFetchType(value); + } + + if (typeof value === 'object') { + return await this.getTypeString(value); + } + + return key; + } + + return keys[0] || 'Unknown'; + } + + return 'Unknown'; + } + + // Parameter Processing + private async processParameters(parameters: any[] = []): Promise<{ name: string; type: string }[]> { + const params = await Promise.all( + parameters.map(async (p: any) => { + const typeName = await this.getTypeString(p.typ); + return { + name: p.name, + type: typeName + }; + }) + ); + + return params; + } + + private async createFunctionSignature(fn: ApiFunction): Promise { + const name = fn.name?.name || 'Unknown'; + const params = await this.processParameters(fn.parameters); + + // Filter out unit parameters for display, but keep all function types + const displayParams = params.filter(p => !UNIT_TYPES.has(p.type)); + + const paramStr = displayParams.map(p => `${p.name}: ${p.type}`).join(', '); + const returnType = await this.getTypeString(fn.returnType); + + return `${name}(${paramStr}) : ${returnType}`; + } + + + async searchModuleData(moduleName: string): Promise { + if (!moduleName?.trim()) { + throw new Error('Module name is required'); + } + + const url = this.buildSearchUrl(moduleName, 'module,function,type,constant'); + return this.fetchData(url); + } + + async getModuleCounts(moduleName: string): Promise { + const url = this.buildSearchUrl(moduleName, 'function,type,constant'); + + try { + const data = await this.fetchData(url); + return { + functions: data.fns?.length || 0, + types: data.types?.length || 0, + constants: data.constants?.length || 0 + }; + } catch (error) { + console.error('getModuleCounts failed:', error); + throw error; + } + } + + async getSubmodules(moduleName: string): Promise { + const url = this.buildSearchUrl(moduleName, 'module'); + + try { + const data = await this.fetchData(url); + return data.submodules || []; + } catch (error) { + console.error('getSubmodules failed:', error); + throw error; + } + } + + async getSubmodulesCount(moduleName: string): Promise { + try { + const submodules = await this.getSubmodules(moduleName); + const parts = moduleName.split('.'); + return submodules.filter(subPath => + Array.isArray(subPath) && subPath.length === parts.length + 1 + ).length; + } catch (error) { + console.error(`Error fetching submodules count for ${moduleName}:`, error); + return 0; + } + } + + async processApiResponseToPackageData(moduleName: string, apiData: ApiResponse): Promise { + const parts = moduleName.split('.'); + const isRootModule = parts.length <= 2; + const displayName = parts[parts.length - 1] || moduleName; + + if (isRootModule) { + const submodules = (apiData.submodules || []).filter((subPath: string[]) => + Array.isArray(subPath) && subPath.length === parts.length + 1 + ); + + const processedSubModules = await Promise.all( + submodules.map(async (subPath: string[]) => { + const subModuleName = subPath[subPath.length - 1]; + const fullName = subPath.join('.'); + const counts = await this.getModuleCounts(fullName); + + let submodulesCount = 0; + try { + submodulesCount = await this.getSubmodulesCount(fullName); + } catch (err) { + console.error(`Error fetching submodules count for module card ${fullName}:`, err); + } + + return { + name: subModuleName, + fullName, + description: `${subModuleName} utilities and operations`, + functions: counts.functions, + types: counts.types, + constants: counts.constants, + submodules: submodulesCount + }; + }) + ); + + return { + name: displayName, + fullName: moduleName, + description: `${moduleName} provides core functionality and utilities.`, + isRootModule: true, + totalFunctions: apiData.fns?.length || 0, + totalTypes: apiData.types?.length || 0, + totalConstants: apiData.constants?.length || 0, + subModules: processedSubModules + }; + } + + const functionList = await Promise.all( + (apiData.fns || []).map(async (fn: ApiFunction) => ({ + name: fn.name?.name || 'Unknown', + signature: await this.createFunctionSignature(fn), + description: fn.description || 'No description available' + })) + ); + + const typeList = (apiData.types || []).map((type: ApiType) => ({ + name: type.name?.name || 'Unknown', + description: type.description || `Type definition for ${type.name?.name || 'Unknown'}` + })); + + const constantList = (apiData.constants || []).map((constant: ApiConstant) => ({ + name: constant.name?.name || 'Unknown', + description: constant.description || `Constant definition for ${constant.name?.name || 'Unknown'}` + })); + + return { + name: displayName, + fullName: moduleName, + description: `${displayName} utilities and operations`, + isRootModule: false, + functions: apiData.fns?.length || 0, + types: apiData.types?.length || 0, + constants: apiData.constants?.length || 0, + functionList, + typeList, + constantList + }; + } + + async processModuleData(moduleName: string, apiData: ApiResponse): Promise { + const parts = moduleName.split('.'); + + // Filter submodules to only include direct children + const submodules = (apiData.submodules || []).filter((subPath: string[]) => + Array.isArray(subPath) && subPath.length === parts.length + 1 + ); + + const processedSubmodules = submodules.map((subPath: string[]) => ({ + name: subPath[subPath.length - 1], + fullName: subPath.join('.') + })); + + + const fullFunctionData = await Promise.all( + (apiData.fns || []).map(async (fn: ApiFunction) => ({ + name: fn.name?.name || 'Unknown', + signature: await this.createFunctionSignature(fn), + description: fn.description || 'No description available' + })) + ); + + const fullTypeData = (apiData.types || []).map((type: ApiType) => ({ + name: type.name?.name || 'Unknown', + description: type.description || 'No description available' + })); + + const fullConstantData = (apiData.constants || []).map((constant: ApiConstant) => ({ + name: constant.name?.name || 'Unknown', + description: constant.description || 'No description available' + })); + + return { + functions: (apiData.fns || []).map((fn: ApiFunction) => fn.name?.name || 'Unknown'), + types: (apiData.types || []).map((type: ApiType) => type.name?.name || 'Unknown'), + constants: (apiData.constants || []).map((constant: ApiConstant) => constant.name?.name || 'Unknown'), + submodules: processedSubmodules, + fullFunctionData, + fullTypeData, + fullConstantData + }; + } + + + async fetchPackageDetails(moduleName: string): Promise { + const apiData = await this.searchModuleData(moduleName); + return this.processApiResponseToPackageData(moduleName, apiData); + } + + async getProcessedModuleData(moduleName: string): Promise { + const apiData = await this.searchModuleData(moduleName); + return this.processModuleData(moduleName, apiData); + } + + async fetchSiblingModules(parentModule: string): Promise> { + const processedData = await this.getProcessedModuleData(parentModule); + + return Promise.all( + processedData.submodules.map(async (submodule) => { + const counts = await this.getModuleCounts(submodule.fullName); + + let submodulesCount = 0; + try { + submodulesCount = await this.getSubmodulesCount(submodule.fullName); + } catch (err) { + console.error(`Error fetching submodules count for ${submodule.fullName}:`, err); + } + + return { + name: submodule.name, + path: submodule.fullName, + stats: { ...counts, submodules: submodulesCount } + }; + }) + ); + } + + async processInitialSidebarPackages(subModules: SubModule[]): Promise> { + return Promise.all( + subModules.map(async (sub: SubModule) => { + let submodulesCount = 0; + try { + submodulesCount = await this.getSubmodulesCount(sub.fullName); + } catch (err) { + console.error(`Error fetching initial submodules count for ${sub.fullName}:`, err); + } + + return { + name: `${sub.name}.dark`, + path: sub.fullName, + stats: { + functions: sub.functions, + types: sub.types, + constants: sub.constants, + submodules: submodulesCount + } + }; + }) + ); + } + + async searchInModules(moduleNames: string[], searchTerm?: string): Promise> { + const results = new Map(); + + const searchPromises = moduleNames.map(async (moduleName) => { + try { + const data = await this.getProcessedModuleData(moduleName); + + if (searchTerm) { + const query = searchTerm.toLowerCase(); + const filteredData: ProcessedItemData = { + ...data, + functions: data.functions.filter(fn => fn.toLowerCase().includes(query)), + types: data.types.filter(type => type.toLowerCase().includes(query)), + constants: data.constants.filter(constant => constant.toLowerCase().includes(query)), + submodules: data.submodules.filter(submod => submod.name.toLowerCase().includes(query)) + }; + results.set(moduleName, filteredData); + } else { + results.set(moduleName, data); + } + } catch (error) { + console.error(`Error searching in module ${moduleName}:`, error); + results.set(moduleName, { + functions: [], + types: [], + constants: [], + submodules: [] + }); + } + }); + + await Promise.all(searchPromises); + return results; + } + + async getBatchModuleCounts(moduleNames: string[]): Promise> { + const results = new Map(); + + const countPromises = moduleNames.map(async (moduleName) => { + try { + const counts = await this.getModuleCounts(moduleName); + results.set(moduleName, counts); + } catch (error) { + console.error(`Error fetching counts for ${moduleName}:`, error); + results.set(moduleName, { functions: 0, types: 0, constants: 0 }); + } + }); + + await Promise.all(countPromises); + return results; + } +} + +export const darkPackagesApi = new DarkPackagesApi(); +export const useDarkPackagesApi = () => darkPackagesApi; \ No newline at end of file diff --git a/src/pages/PackageDetail/index.tsx b/src/pages/PackageDetail/index.tsx new file mode 100644 index 0000000..74d9ad4 --- /dev/null +++ b/src/pages/PackageDetail/index.tsx @@ -0,0 +1,617 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { SubModule, PackageData } from './types'; +import { useDarkPackagesApi, ProcessedSubmodule } from './darkPackagesApi'; +import { PackageDetailSidebar } from './PackageDetailSidebar'; +import { PackageHeader, SidebarPackage, SelectedItem, TabContent } from './components'; + +const PackageDetail: React.FC = () => { + const { packageName } = useParams<{ packageName: string }>(); + const navigate = useNavigate(); + const api = useDarkPackagesApi(); + + // State for the currently selected package + const [selectedPackage, setSelectedPackage] = useState(""); + const [activeTab, setActiveTab] = useState("Functions"); + const [packageData, setPackageData] = useState(null); + const [initialLoading, setInitialLoading] = useState(false); + const [error, setError] = useState(null); + const [sidebarPackages, setSidebarPackages] = useState([]); + const [expandedSidebarItems, setExpandedSidebarItems] = useState>(new Set()); + const [sidebarItemsData, setSidebarItemsData] = useState>({}); + const [sidebarLoadingStates, setSidebarLoadingStates] = useState>({}); + const [searchLoading, setSearchLoading] = useState(false); + + // State for selected items (functions, types, constants) + const [selectedItem, setSelectedItem] = useState(null); + const [pendingItemSelection, setPendingItemSelection] = useState<{ + type: 'function' | 'type' | 'constant'; + name: string; + packagePath: string; + } | null>(null); + + // State for search functionality + const [searchQuery, setSearchQuery] = useState(""); + + // Memoized API functions to prevent unnecessary re-renders + const fetchSidebarItemData = useCallback(async (moduleName: string) => { + setSidebarLoadingStates(prev => ({ ...prev, [moduleName]: true })); + + try { + const processedData = await api.getProcessedModuleData(moduleName); + + setSidebarItemsData(prev => ({ + ...prev, + [moduleName]: processedData + })); + + setSidebarPackages(prev => prev.map(pkg => { + if (pkg.path === moduleName) { + return { + ...pkg, + stats: { + ...pkg.stats, + submodules: processedData.submodules.length + } + }; + } + return pkg; + })); + } catch (err) { + console.error(`Error fetching sidebar item data for ${moduleName}:`, err); + } finally { + setSidebarLoadingStates(prev => ({ ...prev, [moduleName]: false })); + } + }, [api]); + + // Search handler + const handleSearchChange = useCallback((value: string) => { + setSearchQuery(value); + }, []); + + + + const fetchSiblingModules = useCallback(async (parentModule: string) => { + try { + const processedSiblings = await api.fetchSiblingModules(parentModule); + setSidebarPackages(processedSiblings); + } catch (err) { + console.error('Error fetching sibling modules:', err); + } + }, [api]); + + // Main function to fetch package details + const fetchPackageDetails = useCallback(async (moduleName: string, isInitialLoad = false) => { + if (isInitialLoad) { + setInitialLoading(true); + } + setError(null); + + try { + const processedData = await api.fetchPackageDetails(moduleName); + + setPackageData(processedData); + + if (processedData.isRootModule && processedData.subModules && processedData.subModules.length > 0) { + setActiveTab("Submodules"); + } else { + setActiveTab("Functions"); + } + + if (processedData.isRootModule && processedData.subModules) { + const sidebarPackagesWithCounts = await api.processInitialSidebarPackages(processedData.subModules); + setSidebarPackages(sidebarPackagesWithCounts); + } else if (!processedData.isRootModule) { + const parts = moduleName.split('.'); + const parentModule = parts.slice(0, -1).join('.'); + fetchSiblingModules(parentModule); + } + + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to fetch package details'); + console.error('Error fetching package details:', err); + } finally { + if (isInitialLoad) { + setInitialLoading(false); + } + } + }, [fetchSiblingModules, api]); + + // Handler for sidebar clicks - updates both state and URL + const handleSidebarPackageClick = useCallback((packagePath: string) => { + if (selectedPackage !== packagePath) { + setSelectedPackage(packagePath); + + const encodedPackagePath = encodeURIComponent(packagePath); + navigate(`/packages/${encodedPackagePath}`, { replace: true }); + + setSelectedItem(null); + window.history.replaceState(null, '', window.location.pathname + window.location.search); + + setExpandedSidebarItems(new Set([packagePath])); + fetchSidebarItemData(packagePath); + } + }, [selectedPackage, navigate, fetchSidebarItemData]); + + // Separate handler for expand/collapse + const handleSidebarItemToggle = useCallback((packagePath: string) => { + setExpandedSidebarItems(prev => { + const wasExpanded = prev.has(packagePath); + + if (wasExpanded) { + const newExpanded = new Set(prev); + newExpanded.delete(packagePath); + return newExpanded; + } else { + const newExpanded = new Set([packagePath]); + fetchSidebarItemData(packagePath); + return newExpanded; + } + }); + }, [fetchSidebarItemData]); + + // Handler for clicking on functions, types, constants in sidebar + const handleItemClick = useCallback((itemType: 'function' | 'type' | 'constant', itemName: string, packagePath: string, event?: React.MouseEvent) => { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + if (packagePath === selectedPackage) { + setSelectedItem({ type: itemType, name: itemName }); + + const newUrl = `${window.location.pathname}${window.location.search}#${encodeURIComponent(itemName)}`; + window.history.replaceState(null, '', newUrl); + + const tabMap = { + function: 'Functions', + type: 'Types', + constant: 'Constants' + }; + setActiveTab(tabMap[itemType]); + + return; + } + + setPendingItemSelection({ type: itemType, name: itemName, packagePath }); + + const encodedPackageName = encodeURIComponent(packagePath); + const newUrl = `/packages/${encodedPackageName}#${encodeURIComponent(itemName)}`; + navigate(newUrl, { replace: true }); + + setSelectedPackage(packagePath); + setSelectedItem({ type: itemType, name: itemName }); + + setExpandedSidebarItems(new Set([packagePath])); + fetchSidebarItemData(packagePath); + + const tabMap = { + function: 'Functions', + type: 'Types', + constant: 'Constants' + }; + setActiveTab(tabMap[itemType]); + }, [navigate, fetchSidebarItemData, selectedPackage]); + + // Handler for clicking on submodules in sidebar + const handleSubmoduleClick = useCallback((submoduleFullName: string, event?: React.MouseEvent) => { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + if (selectedPackage !== submoduleFullName) { + setSelectedPackage(submoduleFullName); + + const encodedSubmoduleName = encodeURIComponent(submoduleFullName); + navigate(`/packages/${encodedSubmoduleName}`, { replace: true }); + + setSelectedItem(null); + window.history.replaceState(null, '', window.location.pathname + window.location.search); + + setExpandedSidebarItems(new Set([submoduleFullName])); + fetchSidebarItemData(submoduleFullName); + } + }, [selectedPackage, navigate, fetchSidebarItemData]); + + // Group sidebar props into organized objects + const sidebarState = { + packages: sidebarPackages, + expandedItems: expandedSidebarItems, + itemsData: sidebarItemsData, + loadingStates: sidebarLoadingStates, + selectedPackage, + selectedItem, + searchQuery, + searchLoading, + activeTab + }; + + const sidebarActions = { + onNavigate: navigate, + onPackageClick: handleSidebarPackageClick, + onItemToggle: handleSidebarItemToggle, + onItemClick: handleItemClick, + onSubmoduleClick: handleSubmoduleClick, + onSearchChange: handleSearchChange, + onFetchItemData: fetchSidebarItemData, + updateState: { + setSearchLoading, + setExpandedItems: setExpandedSidebarItems, + setItemsData: setSidebarItemsData, + setPackages: setSidebarPackages, + setActiveTab + } + }; + + // Initial load from URL parameter + useEffect(() => { + if (packageName) { + const decodedPackageName = decodeURIComponent(packageName); + setSelectedPackage(decodedPackageName); + fetchPackageDetails(decodedPackageName, true); + } + }, [packageName, fetchPackageDetails]); + + // Apply pending item selection after package data loads + useEffect(() => { + if (pendingItemSelection && packageData && packageData.fullName === pendingItemSelection.packagePath) { + setSelectedItem({ + type: pendingItemSelection.type, + name: pendingItemSelection.name + }); + setActiveTab({ + function: 'Functions', + type: 'Types', + constant: 'Constants' + }[pendingItemSelection.type]); + + setExpandedSidebarItems(new Set([pendingItemSelection.packagePath])); + fetchSidebarItemData(pendingItemSelection.packagePath); + + setPendingItemSelection(null); + } + }, [packageData, pendingItemSelection, fetchSidebarItemData]); + + // Handle URL fragment after package data loads + useEffect(() => { + const fragment = decodeURIComponent(window.location.hash.substring(1)); + if (fragment && packageData && !packageData.isRootModule) { + const functionMatch = packageData.functionList?.find(fn => fn.name === fragment); + const typeMatch = packageData.typeList?.find(type => type.name === fragment); + const constantMatch = packageData.constantList?.find(constant => constant.name === fragment); + + if (functionMatch) { + setSelectedItem({ type: 'function', name: fragment }); + setActiveTab('Functions'); + setExpandedSidebarItems(new Set([selectedPackage])); + if (selectedPackage) { + fetchSidebarItemData(selectedPackage); + } + } else if (typeMatch) { + setSelectedItem({ type: 'type', name: fragment }); + setActiveTab('Types'); + setExpandedSidebarItems(new Set([selectedPackage])); + if (selectedPackage) { + fetchSidebarItemData(selectedPackage); + } + } else if (constantMatch) { + setSelectedItem({ type: 'constant', name: fragment }); + setActiveTab('Constants'); + setExpandedSidebarItems(new Set([selectedPackage])); + if (selectedPackage) { + fetchSidebarItemData(selectedPackage); + } + } + } else if (!fragment) { + setSelectedItem(null); + if (selectedPackage) { + setExpandedSidebarItems(new Set([selectedPackage])); + } + } + }, [packageData, selectedPackage, fetchSidebarItemData]); + + // Fetch data when selected package changes + useEffect(() => { + if (selectedPackage) { + if (!packageData || packageData.fullName !== selectedPackage) { + fetchPackageDetails(selectedPackage, false); + } + } + }, [selectedPackage, fetchPackageDetails, packageData]); + + // Scroll to selected item when it changes + useEffect(() => { + if (selectedItem) { + const timer = setTimeout(() => { + const element = document.getElementById(`${selectedItem.type}-${selectedItem.name}`); + if (element) { + element.scrollIntoView({ + behavior: 'smooth', + block: 'center' + }); + } else { + setTimeout(() => { + const retryElement = document.getElementById(`${selectedItem.type}-${selectedItem.name}`); + if (retryElement) { + retryElement.scrollIntoView({ + behavior: 'smooth', + block: 'center' + }); + } + }, 500); + } + }, 200); + + return () => clearTimeout(timer); + } + }, [selectedItem, selectedPackage]); + + // Scroll to selected item in sidebar when sidebar data loads + useEffect(() => { + if (selectedItem && selectedPackage && sidebarItemsData[selectedPackage]) { + const timer = setTimeout(() => { + const sidebarElement = document.getElementById(`sidebar-${selectedItem.type}-${selectedItem.name}`); + if (sidebarElement) { + sidebarElement.scrollIntoView({ + behavior: 'smooth', + block: 'center' + }); + } + }, 300); + + return () => clearTimeout(timer); + } + }, [selectedItem, selectedPackage, sidebarItemsData]); + + if (initialLoading) { + return ( +
+
+
Loading package details...
+
Fetching module information and counts
+
+
+ ); + } + + if (error) { + return ( +
+
+
Error: {error}
+ +
+
+ ); + } + + if (!packageData) { + return ( +
+
Package not found
+
+ ); + } + + return ( +
+
+ + +
+ { + setActiveTab(tab); + const tabToTypeMap = { + Functions: 'function', + Types: 'type', + Constants: 'constant' + }; + + if (tab === 'Submodules') { + setSelectedItem(null); + window.history.replaceState(null, '', window.location.pathname + window.location.search); + } else if (selectedItem && selectedItem.type !== tabToTypeMap[tab as keyof typeof tabToTypeMap]) { + setSelectedItem(null); + window.history.replaceState(null, '', window.location.pathname + window.location.search); + } + }} + onFetchSidebarItemData={fetchSidebarItemData} + /> + +
+ {activeTab === "Submodules" && ( +
+ {(() => { + if (packageData.isRootModule && packageData.subModules) { + return ( + <> +
+

Available Submodules

+

Click on any submodule to explore its functions, types, and constants.

+
+ {packageData.subModules.map((subModule: SubModule, index: number) => ( +
handleSidebarPackageClick(subModule.fullName)} + > +
+
+
+ 📦 +

{subModule.name}

+
+

{subModule.description}

+
+ {subModule.functions} Functions + {subModule.types} Types + {subModule.constants} {subModule.constants === 1 ? 'Constant' : 'Constants'} + {(subModule.submodules && subModule.submodules > 0) ? ( + {subModule.submodules} {subModule.submodules === 1 ? 'Submodule' : 'Submodules'} + ) : null} +
+
+ Full path: {subModule.fullName} +
+
+
+ + + +
+
+
+ ))} + + ); + } else { + const currentPackageData = sidebarItemsData[selectedPackage]; + const submodules = currentPackageData?.submodules || []; + const filteredSubmodules = searchQuery.trim() + ? submodules.filter((sub: ProcessedSubmodule) => sub.name.toLowerCase().includes(searchQuery.toLowerCase())) + : submodules; + + const currentSidebarPackage = sidebarPackages.find(pkg => pkg.path === selectedPackage); + const hasSubmodulesFromStats = currentSidebarPackage && currentSidebarPackage.stats.submodules > 0; + const isLoading = sidebarLoadingStates[selectedPackage] || false; + + if (hasSubmodulesFromStats && !currentPackageData && !isLoading) { + if (selectedPackage) { + fetchSidebarItemData(selectedPackage); + } + } + + if (isLoading) { + return ( +
+
Loading submodules...
+
Fetching submodule information
+
+ ); + } + + return filteredSubmodules.length > 0 ? ( + <> +
+

Available Submodules

+

Click on any submodule to explore its functions, types, and constants.

+ {searchQuery && ( +
+ Showing {filteredSubmodules.length} of {submodules.length} submodules +
+ )} +
+ {filteredSubmodules.map((submodule: any, index: number) => ( +
handleSubmoduleClick(submodule.fullName)} + > +
+
+
+ 📦 +

{submodule.name}

+
+

{submodule.description}

+
+ Full path: {submodule.fullName} +
+
+
+ + + +
+
+
+ ))} + + ) : ( +
+ {searchQuery ? ( + <> +

No submodules found matching "{searchQuery}"

+ + + ) : ( + <> +

No submodules available.

+

This module doesn't have any submodules.

+ + )} +
+ ); + } + })()} +
+ )} + + {activeTab === "Functions" && ( + setSearchQuery('')} + /> + )} + + {activeTab === "Types" && ( + setSearchQuery('')} + /> + )} + + {activeTab === "Constants" && ( + setSearchQuery('')} + /> + )} +
+
+
+
+ ); +}; + +export default PackageDetail; \ No newline at end of file diff --git a/src/pages/PackageDetail/types.ts b/src/pages/PackageDetail/types.ts new file mode 100644 index 0000000..c2f491a --- /dev/null +++ b/src/pages/PackageDetail/types.ts @@ -0,0 +1,26 @@ +export interface SubModule { + name: string; + fullName: string; + description: string; + functions: number; + types: number; + constants: number; + submodules?: number; +} + +export interface PackageData { + name: string; + fullName: string; + description: string; + isRootModule: boolean; + totalFunctions?: number; + totalTypes?: number; + totalConstants?: number; + subModules?: SubModule[]; + functions?: number; + types?: number; + constants?: number; + functionList?: any[]; + typeList?: any[]; + constantList?: any[]; +} \ No newline at end of file diff --git a/src/pages/Packages/PackageCard.tsx b/src/pages/Packages/PackageCard.tsx new file mode 100644 index 0000000..a6a1163 --- /dev/null +++ b/src/pages/Packages/PackageCard.tsx @@ -0,0 +1,88 @@ +import React from "react"; +import { useNavigate } from "react-router-dom"; + +interface PackageCardProps { + name: string; + author: string; + description: string; + tags: string[]; + // stars: number; + // downloads: number; + logo?: string; + clickable?: boolean; + onCardClick?: () => void; +} + +const PackageCard: React.FC = ({ + name, + author, + description, + tags, + // stars, + // downloads, + logo, + clickable = false, + onCardClick +}) => { + const navigate = useNavigate(); + + // const formatNumber = (num: number): string => { + // return num >= 1000 ? (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : num.toString(); + // }; + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (clickable) { + onCardClick ? onCardClick() : navigate(`/packages/${encodeURIComponent(name)}`); + } + }; + + const cardClasses = ` + bg-[#242628] border border-[#383737] rounded-2xl p-6 + hover:border-[#4a4a4a] transition-colors + ${clickable ? 'cursor-pointer hover:bg-[#2a2c2e]' : ''} + `.trim(); + + return ( +
+
+ {logo && ( + {`${name} + )} +
+

{name}

+

{author}

+
+
+ +

{description}

+ +
+
+ {tags.map((tag, index) => ( + + {tag} + + ))} +
+ {/* +
+
+ + {formatNumber(stars)} +
+
+ 📦 + {formatNumber(downloads)} +
+
*/} +
+
+ ); +}; + +export default PackageCard; \ No newline at end of file diff --git a/src/pages/Packages/components/FilterBar.tsx b/src/pages/Packages/components/FilterBar.tsx new file mode 100644 index 0000000..85d80de --- /dev/null +++ b/src/pages/Packages/components/FilterBar.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +interface FilterBarProps { + activeFilter: string; + onFilterChange: (filter: string) => void; +} + +const FilterBar: React.FC = ({ activeFilter, onFilterChange }) => { + const filters = ["All", "Darklang"]; + + return ( +
+
+
+ {filters.map((filter) => ( + + ))} +
+
+
+ ); +}; + +export default FilterBar; \ No newline at end of file diff --git a/src/pages/Packages/components/Header.tsx b/src/pages/Packages/components/Header.tsx new file mode 100644 index 0000000..011081b --- /dev/null +++ b/src/pages/Packages/components/Header.tsx @@ -0,0 +1,165 @@ +import React, { useState, useEffect } from "react"; +import logoTransparent from "../../../assets/logo-dark-transparent.png"; + +const BookIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +const PackageSearchIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +const UploadIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +const ShareIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + + + + +); + +const PlusIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +const RefreshIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +interface ActionIcon { + name: string; + icon: React.ComponentType<{ className?: string }>; + angle: number; + description: string; +} + +const Header: React.FC = () => { + const actions: ActionIcon[] = [ + { name: 'learn', icon: BookIcon, angle: 170, description: 'Learn about packages' }, + { name: 'search', icon: PackageSearchIcon, angle: 145, description: 'Search for packages' }, + { name: 'create', icon: PlusIcon, angle: 110, description: 'Create new packages' }, + { name: 'update', icon: RefreshIcon, angle: 70, description: 'Update existing packages' }, + { name: 'publish', icon: UploadIcon, angle: 35, description: 'Publish your packages' }, + { name: 'share', icon: ShareIcon, angle: 10, description: 'Share packages with others' }, + ]; + + // Animation effect for sequential icon appearance + const [visibleIcons, setVisibleIcons] = useState(0); + useEffect(() => { + const timer = setTimeout(() => { + if (visibleIcons < actions.length) { + setVisibleIcons(prev => prev + 1); + } + }, visibleIcons === 0 ? 200 : 300); + + return () => clearTimeout(timer); + }, [visibleIcons, actions.length]); + + const getIconPosition = (angle: number) => { + // Convert angle to radians + const radian = (angle * Math.PI) / 180; + // Position icons on the semicircle curve + const x = Math.cos(radian); + const y = Math.sin(radian); + return { + left: `calc(50% + ${x * 45}%)`, + top: `calc(90% - ${y * 90}%)`, + }; + }; + + return ( +
+ {/* Half circle container */} +
+ + + + +
+ Darklang Logo +
+ +
+

+ Darklang Packages +

+
+ + {/* Package Action icons positioned along the curve */} + {actions.map((action, index) => { + const IconComponent = action.icon; + const position = getIconPosition(action.angle); + const isLeftSide = action.angle > 90; + const isVisible = index < visibleIcons; + + return ( + + ); + })} +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/pages/Packages/components/PackageList.tsx b/src/pages/Packages/components/PackageList.tsx new file mode 100644 index 0000000..ea6aa60 --- /dev/null +++ b/src/pages/Packages/components/PackageList.tsx @@ -0,0 +1,136 @@ +import React from "react"; +import PackageCard from "../PackageCard"; +import logoTransparent from "../../../assets/logo-dark-transparent.png"; + +interface PackageListProps { + isSearchMode: boolean; + searchQuery: string; + searchResults: any[]; + dataLoading: boolean; + dataLoaded: boolean; + + modules: any[]; + loading: boolean; + error: string | null; + + onItemClick: (item: string | string[] | any) => void; + onRetry: () => void; +} + +const PackageList: React.FC = ({ + isSearchMode, + searchQuery, + searchResults, + dataLoading, + dataLoaded, + modules, + loading, + error, + onItemClick, + onRetry +}) => { + const getTypeTag = (type: string) => { + switch (type) { + case 'function': return 'ƒ function'; + case 'type': return '🇹 type'; + case 'constant': return '𝐂 constant'; + case 'module': return '📦 module'; + default: return '🔹 item'; + } + }; + + return ( +
+ {!isSearchMode && loading && ( +
+

Loading modules...

+
+ )} + + {!isSearchMode && error && ( +
+

Error: {error}

+ +
+ )} + + {isSearchMode && dataLoading && searchQuery && ( +
+
+

+ Searching for "{searchQuery}"... +

+

+ Loading search data in the background. Results will appear shortly. +

+
+ )} + + {isSearchMode && searchResults.length === 0 && searchQuery && dataLoaded && !dataLoading && ( +
+

+ No results found for "{searchQuery}" +

+
+ )} + + {!isSearchMode && !loading && !error && modules.length === 0 && ( +
+

+ No modules found +

+
+ )} + + {/* to display search results*/} + {isSearchMode && searchResults.length > 0 && ( + searchResults.map((result, index) => ( + onItemClick(result)} + /> + )) + )} + + {/* to display the initial packages*/} + {!isSearchMode && !loading && !error && modules.length > 0 && ( + modules.map((item, index) => { + const displayName = Array.isArray(item) ? item.join('.') : String(item); + const [owner, module] = displayName.split('.'); + const description = `${module} - Core functionality from ${owner} providing essential features and utilities.`; + const tags = ["module", owner.toLowerCase(), "core"]; + + return ( + onItemClick(item)} + /> + ); + }) + )} +
+ ); +}; + +export default PackageList; \ No newline at end of file diff --git a/src/pages/Packages/components/SearchBar.tsx b/src/pages/Packages/components/SearchBar.tsx new file mode 100644 index 0000000..6be4df9 --- /dev/null +++ b/src/pages/Packages/components/SearchBar.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +const SearchIcon = ({ className = "w-6 h-6" }: { className?: string }) => ( + + + +); + +interface SearchBarProps { + searchQuery: string; + onSearchChange: (query: string) => void; + placeholder?: string; +} + +const SearchBar: React.FC = ({ + searchQuery, + onSearchChange, + placeholder = "Search packages, functions, types, constants, and more..." +}) => { + return ( +
+
+
+
+ +
+ onSearchChange(e.target.value)} + /> +
+
+
+ ); +}; + +export default SearchBar; \ No newline at end of file diff --git a/src/pages/Packages/components/Sidebar.tsx b/src/pages/Packages/components/Sidebar.tsx new file mode 100644 index 0000000..5e52e35 --- /dev/null +++ b/src/pages/Packages/components/Sidebar.tsx @@ -0,0 +1,51 @@ +import React from "react"; + +const Sidebar: React.FC = () => { + return ( +
+
+

Recent Activity

+
+
+
+ 📦 +
+
+

New Package Published

+

Darklang.ModelContextProtocol

+
+
+ +
+
+ 👤 +
+
+

New Contributor

+

@Alex_cb

+
+
+
+
+ +
+

Stay Updated

+

+ Get monthly package highlights delivered to your inbox +

+
+ + +
+
+
+ ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/pages/Packages/hooks/index.ts b/src/pages/Packages/hooks/index.ts new file mode 100644 index 0000000..8c60579 --- /dev/null +++ b/src/pages/Packages/hooks/index.ts @@ -0,0 +1,2 @@ +export { useDataFetching } from './useDataFetching'; +export { useSearch } from './useSearch'; \ No newline at end of file diff --git a/src/pages/Packages/hooks/useDataFetching.ts b/src/pages/Packages/hooks/useDataFetching.ts new file mode 100644 index 0000000..db97c6c --- /dev/null +++ b/src/pages/Packages/hooks/useDataFetching.ts @@ -0,0 +1,117 @@ +import { useState, useCallback, useEffect } from 'react'; +import { ApiResponse, SearchableItem } from '../types'; +import { processApiResponse } from '../utils/dataProcessing'; + +const API_BASE_URL = "http://dark-packages.dlio.localhost:11001"; + +interface UseDataFetchingReturn { + modules: string[][]; + allSearchableData: SearchableItem[]; + dataLoading: boolean; + dataLoaded: boolean; + loading: boolean; + error: string | null; + fetchData: () => Promise; +} + +export const useDataFetching = (): UseDataFetchingReturn => { + const [modules, setModules] = useState([]); + const [allSearchableData, setAllSearchableData] = useState([]); + const [dataLoading, setDataLoading] = useState(true); + const [dataLoaded, setDataLoaded] = useState(false); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const fetchAllSearchableData = useCallback(async () => { + setDataLoading(true); + setDataLoaded(false); + + try { + // Get all top-level modules + const topLevelUrl = `${API_BASE_URL}/search?searchDepth=direct&entityTypes=module,function,type,constant&text=&modules=`; + const topLevelResponse = await fetch(topLevelUrl); + + if (!topLevelResponse.ok) { + throw new Error(`HTTP error! status: ${topLevelResponse.status}`); + } + + const topLevelData: ApiResponse = await topLevelResponse.json(); + const allData: SearchableItem[] = [...processApiResponse(topLevelData)]; + + // Fetch data for each submodule + if (topLevelData.submodules) { + const modulePromises = topLevelData.submodules.map(async (modulePath) => { + try { + const modulePathStr = Array.isArray(modulePath) ? modulePath.join('.') : modulePath; + const moduleUrl = `${API_BASE_URL}/search?searchDepth=direct&entityTypes=module,function,type,constant&text=&modules=${encodeURIComponent(modulePathStr)}`; + + const moduleResponse = await fetch(moduleUrl); + if (moduleResponse.ok) { + const moduleData: ApiResponse = await moduleResponse.json(); + return processApiResponse(moduleData, modulePathStr); + } + return []; + } catch (err) { + console.warn(`Failed to fetch data for module ${modulePath}:`, err); + return []; + } + }); + + const moduleResults = await Promise.all(modulePromises); + allData.push(...moduleResults.flat()); + } + + setAllSearchableData(allData); + setDataLoaded(true); + } catch (err) { + console.error('Error fetching searchable data:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch searchable data'); + } finally { + setDataLoading(false); + } + }, []); + + const fetchData = useCallback(async () => { + setLoading(true); + setError(null); + + try { + const url = `${API_BASE_URL}/search?searchDepth=direct&entityTypes=module&modules=`; + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data: ApiResponse = await response.json(); + + // Filter to show only Owner.Module pairs + const ownerModules = (data.submodules || []).filter((modulePath: string[]) => + Array.isArray(modulePath) && modulePath.length === 2 + ); + + setModules(ownerModules); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Failed to fetch data'; + console.error('API Error:', errorMessage); + setError(errorMessage); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchAllSearchableData(); + fetchData(); + }, [fetchAllSearchableData, fetchData]); + + return { + modules, + allSearchableData, + dataLoading, + dataLoaded, + loading, + error, + fetchData + }; +}; \ No newline at end of file diff --git a/src/pages/Packages/hooks/useSearch.ts b/src/pages/Packages/hooks/useSearch.ts new file mode 100644 index 0000000..1296795 --- /dev/null +++ b/src/pages/Packages/hooks/useSearch.ts @@ -0,0 +1,54 @@ +import { useState, useCallback, useEffect } from 'react'; +import { SearchableItem } from '../types'; + +const SEARCH_DEBOUNCE_DELAY = 300; + +interface UseSearchReturn { + searchResults: SearchableItem[]; +} + +interface UseSearchProps { + allSearchableData: SearchableItem[]; + dataLoading: boolean; + dataLoaded: boolean; + searchQuery: string; +} + +export const useSearch = ({ + allSearchableData, + dataLoading, + dataLoaded, + searchQuery +}: UseSearchProps): UseSearchReturn => { + const [searchResults, setSearchResults] = useState([]); + + const performSearch = useCallback((query: string) => { + if (!query.trim() || dataLoading || !dataLoaded) { + setSearchResults([]); + return; + } + + const lowerQuery = query.toLowerCase(); + const filtered = allSearchableData.filter((item: SearchableItem) => { + const name = item.name.toLowerCase(); + const description = item.description.toLowerCase(); + const module = (item.module || '').toLowerCase(); + + return name.includes(lowerQuery) || + description.includes(lowerQuery) || + module.includes(lowerQuery); + }); + + setSearchResults(filtered); + }, [allSearchableData, dataLoading, dataLoaded]); + + useEffect(() => { + const timer = setTimeout(() => { + performSearch(searchQuery); + }, SEARCH_DEBOUNCE_DELAY); + + return () => clearTimeout(timer); + }, [searchQuery, performSearch]); + + return { searchResults }; +}; \ No newline at end of file diff --git a/src/pages/Packages/index.tsx b/src/pages/Packages/index.tsx new file mode 100644 index 0000000..88bf398 --- /dev/null +++ b/src/pages/Packages/index.tsx @@ -0,0 +1,131 @@ +import React, { useState, useMemo } from "react"; +import { useNavigate } from "react-router-dom"; +import Header from "./components/Header"; +import SearchBar from "./components/SearchBar"; +import FilterBar from "./components/FilterBar"; +import Sidebar from "./components/Sidebar"; +import PackageList from "./components/PackageList"; +import { useDataFetching, useSearch } from "./hooks"; +import { SearchableItem } from "./types"; + +const Packages: React.FC = () => { + const [searchQuery, setSearchQuery] = useState(""); + const [activeFilter, setActiveFilter] = useState("All"); + const navigate = useNavigate(); + + const isSearchMode = !!searchQuery.trim(); + + const { + modules, + allSearchableData, + dataLoading, + dataLoaded, + loading, + error, + fetchData + } = useDataFetching(); + + const { searchResults } = useSearch({ + allSearchableData, + dataLoading, + dataLoaded, + searchQuery + }); + + // Filter modules based on active filter + const filteredModules = useMemo(() => { + if (activeFilter === "All") { + return modules; + } + + if (activeFilter === "Darklang") { + return modules.filter((module) => { + const moduleName = Array.isArray(module) ? module.join('.') : String(module); + return moduleName.toLowerCase().startsWith('darklang'); + }); + } + + return modules; + }, [modules, activeFilter]); + + const filteredSearchResults = useMemo(() => { + if (activeFilter === "All") { + return searchResults; + } + + if (activeFilter === "Darklang") { + return searchResults.filter((result) => { + if (result.module) { + return result.module.toLowerCase().startsWith('darklang'); + } + + if (result.type === 'module') { + const moduleName = Array.isArray(result.path) + ? result.path.join('.') + : result.name; + return moduleName.toLowerCase().startsWith('darklang'); + } + + // For other types without explicit module info, default to include if no module specified + return !result.module || result.module.toLowerCase().startsWith('darklang'); + }); + } + + return searchResults; + }, [searchResults, activeFilter]); + + const handleItemClick = (item: string | string[] | SearchableItem) => { + if (isSearchMode && typeof item === 'object' && 'type' in item) { + if (item.type === 'module') { + const modulePath = Array.isArray(item.path) ? item.path : item.name.split('.'); + navigate(`/packages/${encodeURIComponent(modulePath.join('.'))}`); + } else { + if (item.module) { + navigate(`/packages/${encodeURIComponent(item.module)}#${encodeURIComponent(item.name)}`); + } + } + } else { + const modulePath = Array.isArray(item) ? item : typeof item === 'string' ? item.split('.') : []; + navigate(`/packages/${modulePath.join('.')}`); + } + }; + + const resultCount = isSearchMode ? filteredSearchResults.length : filteredModules.length; + const title = isSearchMode + ? dataLoading && searchQuery + ? `Searching for "${searchQuery}"...` + : `${resultCount} search results` + : (loading ? 'Loading...' : `${resultCount} modules`); + + return ( +
+
+ + + +
+
+

{title}

+
+ +
+ + +
+
+
+ ); +}; + +export default Packages; \ No newline at end of file diff --git a/src/pages/Packages/types.ts b/src/pages/Packages/types.ts new file mode 100644 index 0000000..4db61b9 --- /dev/null +++ b/src/pages/Packages/types.ts @@ -0,0 +1,26 @@ +export interface ApiItem { + name?: { name?: string }; + module?: string; + description?: string; + signature?: any; + definition?: any; + value?: any; +} + +export interface SearchableItem { + type: 'module' | 'function' | 'type' | 'constant'; + name: string; + path?: string[]; + module?: string; + description: string; + signature?: any; + definition?: any; + value?: any; +} + +export interface ApiResponse { + submodules?: string[][]; + fns?: ApiItem[]; + types?: ApiItem[]; + constants?: ApiItem[]; +} \ No newline at end of file diff --git a/src/pages/Packages/utils/dataProcessing.ts b/src/pages/Packages/utils/dataProcessing.ts new file mode 100644 index 0000000..d286d68 --- /dev/null +++ b/src/pages/Packages/utils/dataProcessing.ts @@ -0,0 +1,42 @@ +import { ApiItem, SearchableItem, ApiResponse } from '../types'; + +const processApiItems = ( + items: ApiItem[] | undefined, + type: 'function' | 'type' | 'constant', + modulePathStr?: string +): SearchableItem[] => { + if (!items) return []; + + return items + .filter((item: ApiItem) => item.name?.name && item.name.name !== 'Unknown') + .map((item: ApiItem) => ({ + type, + name: item.name!.name!, + module: item.module || modulePathStr || '', + description: item.description || `${type.charAt(0).toUpperCase() + type.slice(1)} ${item.name!.name}`, + ...(type === 'function' && { signature: item.signature }), + ...(type === 'type' && { definition: item.definition }), + ...(type === 'constant' && { value: item.value }) + })); +}; + +export const processApiResponse = (data: ApiResponse, modulePathStr?: string): SearchableItem[] => { + const allData: SearchableItem[] = []; + + // Process modules + if (data.submodules) { + allData.push(...data.submodules.map((modulePath: string[]) => ({ + type: 'module' as const, + name: Array.isArray(modulePath) ? modulePath.join('.') : modulePath, + path: modulePath, + description: `Module containing functionality for ${modulePath[modulePath.length - 1]}` + }))); + } + + // Process functions, types, and constants + allData.push(...processApiItems(data.fns, 'function', modulePathStr)); + allData.push(...processApiItems(data.types, 'type', modulePathStr)); + allData.push(...processApiItems(data.constants, 'constant', modulePathStr)); + + return allData; +}; \ No newline at end of file diff --git a/src/pages/Roadmap.tsx b/src/pages/Roadmap.tsx new file mode 100644 index 0000000..d1810e7 --- /dev/null +++ b/src/pages/Roadmap.tsx @@ -0,0 +1,65 @@ +const Roadmap = () => { + return ( +
+

+ Darklang Roadmap +

+ +
+

+ At Darklang, we're committed to building a powerful yet simple + platform for backend development. Our roadmap outlines our vision for + the future and the features we're working on. +

+ +
+
+
+ +
    +
  • Enhanced debugging tools with time-travel capabilities
  • +
  • Extended package management system
  • +
  • Performance optimizations for large-scale applications
  • +
  • New UI components library
  • +
+
+ +
+
+ +
    +
  • Advanced deployment strategies (canary, blue-green)
  • +
  • AI-assisted coding features
  • +
  • Enhanced data visualization tools
  • +
  • Expanded third-party service integrations
  • +
+
+ +
+
+ +
    +
  • Multi-region deployment support
  • +
  • Advanced monitoring and observability tools
  • +
  • Expanded language features for complex applications
  • +
  • Large-scale enterprise features and support
  • +
+
+
+ +
+

Help Shape Our Future

+

+ We believe in building in public and with our community. Have + suggestions or feature requests? We'd love to hear from you! +

+ +
+
+
+ ); +}; + +export default Roadmap; diff --git a/src/pages/TraceDriven/index.tsx b/src/pages/TraceDriven/index.tsx new file mode 100644 index 0000000..1503bfc --- /dev/null +++ b/src/pages/TraceDriven/index.tsx @@ -0,0 +1,34 @@ +import React from "react"; + +const TraceDriven: React.FC = () => { + return ( +

+ Traces are a core feature of Darklang that enable developers to work with live request data to build and debug applications efficiently. + Acting as an "omniscient debugger" traces provide complete visibility into the state of an application at any point in time, allowing developers + to see exactly what data flows through their code and how it behaves without needing traditional debugging tools like print statements or external loggers. + + Traces power Trace-Driven Development, a workflow where you start by sending requests to non-existent endpoints. + Darklang records those traces, which you then use to write code that handles real inputs. + This approach ensures code is developed with actual data, reducing guesswork and errors. + + Traces are records of HTTP requests (or other events) made to a Darklang app. Whether your code runs locally or in the cloud, + traces automatically capture inputs and intermediate values during execution for: + - HTTP handlers + - Worker `emit`s + - Function calls + - CRON responses + - CLI application calls + + A trace includes: + - Request Inputs: The full HTTP request, including headers, query parameters, body and metadata. + - Intermediate Values: Values computed during the execution of a handler, such as the results of expressions or function calls. + - Live Values: The evaluated results of expressions for a selected traceThese update in real-time as you write code. + - Return Values: The final output of a handler + + All traces are securely and centrally captured in the package manager, and made available in your editing environment. + This approach streamlines debugging, improves accuracy, and makes refactoring safer—all by letting real data guide the development process. +

+ ); +}; + +export default TraceDriven; diff --git a/src/pages/TypeChecking/index.tsx b/src/pages/TypeChecking/index.tsx new file mode 100644 index 0000000..e93c8f7 --- /dev/null +++ b/src/pages/TypeChecking/index.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +const TypeChecking: React.FC = () => { + return ( +
+
+        Runtime is done-ish
+        Parse-time is barely there
+      
+ +

+ Type checking:
+ - Darklang is fully statically typed with Gradual Flexibility, ensuring that types are checked at compile time to catch errors early. This helps developers ensure that changes across large programs are safe, particularly for refactoring or scaling systems
+ - Unlike traditional functional languages where the entire program must compile, Darklang employs gradual static typing. This allows incomplete or partially typed code to run, enabling developers to prototype quickly without ensuring every part of the program type-checks immediately
+ - Darklang uses small compilation units, meaning that type checking is localized to specific parts of the program, such as a single HTTP route. This reduces the scope of type changes needed when prototyping or making small modifications
+ - Instead of modifying existing types, developers create a copy of a type, make changes, and test them. Once validated, semi-automated tooling helps propagate these changes across the program, minimizing the effort required for large-scale type updates.
+ - The language supports Option and Result types for error handling, replacing nulls and exceptions to avoid common pitfalls like null pointer errors
+ - Types and functions in Darklang are versioned and immutable, ensuring that changes don’t break existing code. This is particularly useful in the package manager, where types and functions are individually versioned
+ - When updating types, developers can test new versions without affecting the entire program, and tooling assists in migrating to the new type once it’s finalized.
+ - Future plans include implementing an at-rest/static checker to perform comprehensive type checking, including preventing invalid operations (e.g., adding a String and an Int). This will leverage Darklang’s access to trace data for advanced diagnostics
+ + Benefits of Darklang’s Type Checking
+ + - Safety: Static typing ensures type mismatches are caught early, reducing runtime errors and making large-scale changes safer
+ - Productivity: Small compilation units and gradual typing allow rapid prototyping, akin to dynamic languages, while retaining static typing benefits
+ - Error Handling: The Option/Result types simplify error management, eliminating the need for exceptions and reducing null-related bugs
+ - Maintainability: Versioned types and editor integration make refactoring and maintaining code easier, especially in evolving projects
+ - Diagnostics: Integration with trace data provides unique insights into type-related issues, improving debugging and development efficiency
+ - Scalability: Versioned types and small compilation units make it easier to scale and refactor backends as projects grow
+

+ + for more content inspo:
+ - https://blog.darklang.com/real-problems-with-functional-languages/amp/
+ - https://blog.darklang.com/an-overdue-status-update/ + +
+ ); +}; + +export default TypeChecking; diff --git a/src/pages/template.tsx b/src/pages/template.tsx new file mode 100644 index 0000000..8bed605 --- /dev/null +++ b/src/pages/template.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +const TODO: React.FC = () => { + return ( +

+ TODO +

+ ); +}; + +export default TODO; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index cc7eeb4..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,50 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ["./public/*.{html,js}", "./public/**/*.{html,js}"], - theme: { - fontFamily: { - body: ["Inter", "sans-serif"], - "League-Gothic": ["League Gothic", "sans-serif"], - Assistant: ["Assistant", "sans-serif"], - Quicksand: ["Quicksand", "sans-serif"], - FiraCode: ["Fira Code", "monospace"], - }, - listStyleType: { - none: "none", - disc: "disc", - decimal: "decimal", - square: "square", - roman: "upper-roman", - }, - extend: { - colors: { - "light-background": "#FAFAFA", - purple: "#955B9F", - blue: "#747AB9", - "dark-gray": "#2F2F2F", - "code-background": "#F5F4F1", - "cli-background": "#262626", - // classic colors - "classic-green": "#A1B56C", - "classic-brown": "#A1887F", - "classic-yellow": "#E6BD81", - "classic-blue": "#86C1B9", - "classic-purple": "#B278FF", - "classic-pink": "#F56FF0", - }, - flex: { - 2: "2 2 0%", - }, - - animation: { - shine: "shine 1s linear", - }, - keyframes: { - shine: { - "100%": { left: "125%" }, - }, - }, - }, - }, - plugins: [require("@tailwindcss/line-clamp")], -}; diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..318c218 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "~/*": ["src/*"], + } + }, + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..169f2cd --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +import { resolve } from "path"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + '~': resolve(process.cwd(), 'src'), + }, + }, + build: { + minify: true, + rollupOptions: { + output: { + manualChunks: { + vendor: ["react", "react-dom", "react-router-dom"], + ui: ["highlight.js", "tailwindcss"], + }, + }, + }, + }, +});