diff --git a/.gitignore b/.gitignore index f242b1e..bfdc12d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ # Deployment artifacts .fastapps-deploy-*.tar.gz -# examples will be uploaded soon -examples/ \ No newline at end of file +# (examples subdir now tracked; keep ignoring per-example build artifacts via nested .gitignore) diff --git a/README.md b/README.md index 3972365..7feb91a 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,12 @@ export default function MyWidget() { --- +## Examples + +- `examples/pizzaz` – port of the OpenAI Apps SDK “Pizzaz” gallery built with FastApps (multiple widgets, Tailwind styling). See the example README for setup instructions. + +--- + ## Contributing We welcome contributions! Please see our contributing guidelines: diff --git a/examples/pizzaz/.gitignore b/examples/pizzaz/.gitignore new file mode 100644 index 0000000..e17dd2d --- /dev/null +++ b/examples/pizzaz/.gitignore @@ -0,0 +1,41 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +venv/ +ENV/ +env/ +.venv + +# JavaScript +node_modules/ +npm-debug.log* +*.log +dist/ +.cache/ + +# Build outputs +assets/ + +# IDEs +.vscode/ +.idea/ +*.swp +.DS_Store diff --git a/examples/pizzaz/README.md b/examples/pizzaz/README.md new file mode 100644 index 0000000..dc90640 --- /dev/null +++ b/examples/pizzaz/README.md @@ -0,0 +1,80 @@ +# Pizzaz FastApps Example + +End-to-end port of the [OpenAI Apps SDK “Pizzaz” gallery](https://github.com/openai/openai-apps-sdk-examples) built entirely with FastApps. + +This example demonstrates: + +- Multiple widgets + tools in a single FastApps app (map, carousel, albums, list, shop) +- Shared React utilities (routing, widget state, Tailwind 4 styling) +- Custom build pipeline (`build-all.mts`) with Tailwind CSS 4 (`@tailwindcss/vite`) + +## Prerequisites + +- Python 3.11+ (for FastApps CLI) +- Node.js 18+ / npm 8+ +- `fastapps` CLI installed globally (`uv tool install fastapps` or `pipx install fastapps`) + +## Quick Start + +```bash +cd examples/pizzaz +npm install # install React/Tailwind deps +npm run build # or: fastapps build +fastapps dev # starts MCP server + Cloudflare tunnel +``` + +`fastapps dev` prints the public tunnel URL (e.g. `https://xxx.trycloudflare.com/mcp`). Add that URL to ChatGPT Connectors or use [MCPJam Inspector](https://www.npmjs.com/package/@mcpjam/inspector) to view each widget. + +> **Note:** Mapbox GL uses the demo token baked into `widgets/pizza-map/index.jsx`. Replace `mapboxgl.accessToken` with your own for production use. + +## Scripts + +| Command | Description | +| ------------------ | -------------------------------------- | +| `npm run build` | Runs `build-all.mts` to bundle widgets | +| `fastapps build` | Same as above (calls the script) | +| `fastapps dev` | Dev server + Cloudflare tunnel | + +## Widget/Tool Mapping + +| Tool identifier | Widget path | Description | +| ---------------- | ---------------------------------- | ------------------------------- | +| `pizza-map` | `widgets/pizza-map/` | Mapbox map + inspector sidebar | +| `pizza-carousel` | `widgets/pizza-carousel/` | Embla carousel of places | +| `pizza-albums` | `widgets/pizza-albums/` | Photo albums + fullscreen view | +| `pizza-list` | `widgets/pizza-list/` | Ranked list UI | +| `pizza-shop` | `widgets/pizza-shop/` | Cart/checkout demo | + +Python backend lives in `server/tools/*.py` (one `BaseWidget` per identifier). Shared inputs/constants are in `server/tools/pizzaz_common.py`. + +## Project Structure + +``` +examples/pizzaz +├── build-all.mts # Vite build orchestrator (with Tailwind plugin) +├── package.json # npm deps (Tailwind, mapbox-gl, etc.) +├── server/ +│ ├── main.py # FastApps auto-discovery server +│ └── tools/ # Backend widgets (pizzaz_common, pizza_*_tool.py) +├── widgets/ +│ ├── pizza-map/ # Mapbox widget + inspector/sidebar +│ ├── pizza-carousel/ # Carousel widget +│ ├── pizza-albums/ # Albums widget +│ ├── pizza-list/ # List widget +│ ├── pizza-shop/ # Cart widget +│ ├── shared/ # Shared JSON data (markers) +│ └── styles/index.css # Tailwind 4 entrypoint +└── tailwind.config.ts # Tailwind content configuration +``` + +Generated assets are ignored (`assets/`)—run `npm run build` whenever widgets change. + +## Learn More + +- **FastApps Framework**: https://pypi.org/project/fastapps/ +- **FastApps React hooks**: https://www.npmjs.com/package/fastapps +- **Docs**: https://docs.fastapps.org/ + +## License + +MIT (same as FastApps) diff --git a/examples/pizzaz/build-all.mts b/examples/pizzaz/build-all.mts new file mode 100755 index 0000000..f213bbe --- /dev/null +++ b/examples/pizzaz/build-all.mts @@ -0,0 +1,177 @@ +import { build, type InlineConfig, type Plugin } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +import fg from "fast-glob"; +import path from "path"; +import fs from "fs"; +import crypto from "crypto"; + +// Read package.json from current working directory (project root) +const pkgPath = path.join(process.cwd(), "package.json"); +const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); + +// Find all widget directories with index.{tsx,jsx} +const widgetDirs = fg.sync("widgets/*/", { onlyDirectories: true }); +const entries = widgetDirs + .map((dir) => { + const dirPath = dir.endsWith("/") ? dir : dir + "/"; + const indexFiles = fg.sync(`${dirPath}index.{tsx,jsx}`); + return indexFiles[0]; + }) + .filter(Boolean); +const outDir = "assets"; + +function wrapEntryPlugin( + virtualId: string, + entryFile: string, + widgetName: string, +): Plugin { + return { + name: `virtual-entry-wrapper:${entryFile}`, + resolveId(id) { + if (id === virtualId) return id; + }, + load(id) { + if (id !== virtualId) { + return null; + } + + // Automatically add mounting logic - no _app.jsx needed! + return ` + import React from 'react'; + import { createRoot } from 'react-dom/client'; + import Component from ${JSON.stringify(entryFile)}; + + // Auto-mount the component + const rootElement = document.getElementById('${widgetName}-root'); + if (rootElement) { + const root = createRoot(rootElement); + root.render(React.createElement(Component)); + } else { + console.error('Root element #${widgetName}-root not found!'); + } + `; + }, + }; +} + +fs.rmSync(outDir, { recursive: true, force: true }); +fs.mkdirSync(outDir, { recursive: true }); + +const builtNames: string[] = []; + +for (const file of entries) { + const name = path.basename(path.dirname(file)); + + const entryAbs = path.resolve(file); + + const virtualId = `\0virtual-entry:${entryAbs}`; + + const createConfig = (): InlineConfig => ({ + plugins: [ + wrapEntryPlugin(virtualId, entryAbs, name), + tailwindcss(), + react(), + { + name: "remove-manual-chunks", + outputOptions(options) { + if ("manualChunks" in options) { + delete (options as any).manualChunks; + } + return options; + }, + }, + ], + esbuild: { + jsx: "automatic", + jsxImportSource: "react", + target: "es2022", + }, + build: { + target: "es2022", + outDir, + emptyOutDir: false, + chunkSizeWarningLimit: 2000, + minify: "esbuild", + cssCodeSplit: false, + rollupOptions: { + input: virtualId, + output: { + format: "es", + entryFileNames: `${name}.js`, + inlineDynamicImports: true, + assetFileNames: (info) => + (info.name || "").endsWith(".css") + ? `${name}.css` + : `[name]-[hash][extname]`, + }, + preserveEntrySignatures: "allow-extension", + treeshake: true, + }, + }, + }); + + console.group(`Building ${name} (react)`); + await build(createConfig()); + console.groupEnd(); + builtNames.push(name); + console.log(`Built ${name}`); +} + +const outputs = fs + .readdirSync("assets") + .filter((f) => f.endsWith(".js") || f.endsWith(".css")) + .map((f) => path.join("assets", f)) + .filter((p) => fs.existsSync(p)); + +const renamed = []; + +const h = crypto + .createHash("sha256") + .update(pkg.version, "utf8") + .digest("hex") + .slice(0, 4); + +console.group("Hashing outputs"); +for (const out of outputs) { + const dir = path.dirname(out); + const ext = path.extname(out); + const base = path.basename(out, ext); + const newName = path.join(dir, `${base}-${h}${ext}`); + + fs.renameSync(out, newName); + renamed.push({ old: out, neu: newName }); + console.log(`${out} -> ${newName}`); +} +console.groupEnd(); + +console.log("new hash: ", h); + +for (const name of builtNames) { + const dir = outDir; + const htmlPath = path.join(dir, `${name}-${h}.html`); + const cssPath = path.join(dir, `${name}-${h}.css`); + const jsPath = path.join(dir, `${name}-${h}.js`); + + const css = fs.existsSync(cssPath) + ? fs.readFileSync(cssPath, { encoding: "utf8" }) + : ""; + const js = fs.existsSync(jsPath) + ? fs.readFileSync(jsPath, { encoding: "utf8" }) + : ""; + + const cssBlock = css ? `\n \n` : ""; + const jsBlock = js ? `\n ` : ""; + + const html = [ + "", + "", + `${cssBlock}`, + "", + `
${jsBlock}`, + "", + "", + ].join("\n"); + fs.writeFileSync(htmlPath, html, { encoding: "utf8" }); + console.log(`${htmlPath} (generated)`); +} diff --git a/examples/pizzaz/package-lock.json b/examples/pizzaz/package-lock.json new file mode 100644 index 0000000..3179a85 --- /dev/null +++ b/examples/pizzaz/package-lock.json @@ -0,0 +1,3188 @@ +{ + "name": "pizzaz", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pizzaz", + "version": "1.0.0", + "dependencies": { + "clsx": "^2.1.1", + "embla-carousel": "^8.3.0", + "embla-carousel-react": "^8.3.0", + "fastapps": "^1.0.0", + "framer-motion": "^11.11.17", + "lucide-react": "^0.460.0", + "mapbox-gl": "^2.15.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.2" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.11", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "10.4.21", + "fast-glob": "^3.3.2", + "postcss": "8.5.6", + "tailwindcss": "4.1.11", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "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.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", + "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz", + "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "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/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/node/node_modules/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", + "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", + "tailwindcss": "4.1.17" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "dev": true, + "license": "MIT" + }, + "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.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@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 || ^7.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", + "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "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.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "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": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "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/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "license": "ISC" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.249", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz", + "integrity": "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==", + "dev": true, + "license": "ISC" + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT" + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "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/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": { + "@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.6.0" + } + }, + "node_modules/fastapps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fastapps/-/fastapps-1.1.0.tgz", + "integrity": "sha512-Cxzp7xN9nM+Me51T/WX7TMeRg3KSn0a3Mw0381FrrzycMAvR40cQwPSc4j257oug6frvLQ6y99i+8nlVZTsumA==", + "license": "MIT", + "bin": { + "fastapps-build": "build-all.mts" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "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/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": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "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/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", + "license": "ISC" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "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/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==", + "dev": true, + "license": "ISC" + }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "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-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "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==", + "license": "MIT" + }, + "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": ">=6" + } + }, + "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": ">=6" + } + }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "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.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "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-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "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-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "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.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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/lucide-react": { + "version": "0.460.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz", + "integrity": "sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mapbox-gl": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz", + "integrity": "sha512-fjv+aYrd5TIHiL7wRa+W7KjtUqKWziJMZUkK5hm8TvJ3OLeNPx4NmW/DgfYhd/jHej8wWL+QJBDbdMMAKvNC0A==", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.1", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^8.0.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + } + }, + "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" + } + }, + "node_modules/micromatch": { + "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" + }, + "engines": { + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "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/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "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/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "license": "ISC" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/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.9.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz", + "integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.5.tgz", + "integrity": "sha512-mkEmq/K8tKN63Ae2M7Xgz3c9l9YNbY+NHH6NNeUmLA3kDkhKXRsNb/ZpxaEunvGo2/3YXdk5EJU3Hxp3ocaBPw==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.5" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/reusify": { + "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.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/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.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "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==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "license": "ISC" + }, + "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" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "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/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "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/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "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" + } + } +} diff --git a/examples/pizzaz/package.json b/examples/pizzaz/package.json new file mode 100644 index 0000000..5e885ce --- /dev/null +++ b/examples/pizzaz/package.json @@ -0,0 +1,32 @@ +{ + "name": "pizzaz", + "version": "1.0.0", + "type": "module", + "description": "Floydr ChatGPT widgets project", + "scripts": { + "build": "npx tsx build-all.mts" + }, + "dependencies": { + "clsx": "^2.1.1", + "embla-carousel": "^8.3.0", + "embla-carousel-react": "^8.3.0", + "fastapps": "^1.0.0", + "framer-motion": "^11.11.17", + "lucide-react": "^0.460.0", + "mapbox-gl": "^2.15.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.2" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "@tailwindcss/vite": "^4.1.11", + "autoprefixer": "10.4.21", + "fast-glob": "^3.3.2", + "postcss": "8.5.6", + "tailwindcss": "4.1.11", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } +} diff --git a/examples/pizzaz/requirements.txt b/examples/pizzaz/requirements.txt new file mode 100644 index 0000000..921181a --- /dev/null +++ b/examples/pizzaz/requirements.txt @@ -0,0 +1,3 @@ +# All dependencies included in fastapps package +# pip install fastapps (or: uv pip install fastapps) is all you need! +fastapps>=1.3.2 diff --git a/examples/pizzaz/server/__init__.py b/examples/pizzaz/server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/pizzaz/server/api/__init__.py b/examples/pizzaz/server/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/pizzaz/server/main.py b/examples/pizzaz/server/main.py new file mode 100644 index 0000000..36be3de --- /dev/null +++ b/examples/pizzaz/server/main.py @@ -0,0 +1,101 @@ +import argparse +import importlib +import inspect +import re +import sys +from pathlib import Path +from typing import Dict + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +# Import FastApps framework +import uvicorn + +from fastapps import BaseWidget, WidgetBuilder, WidgetBuildResult, WidgetMCPServer + +PROJECT_ROOT = Path(__file__).parent.parent +TOOLS_DIR = Path(__file__).parent / "tools" +ASSETS_DIR = PROJECT_ROOT / "assets" + + +def fetch_build_results() -> Dict[str, WidgetBuildResult]: + """Parse built widget HTML files from assets directory.""" + results = {} + for html_file in ASSETS_DIR.glob("*-*.html"): + match = re.match(r"(.+)-([0-9a-f]{4})\.html$", html_file.name) + if match: + name, hash_val = match.groups() + results[name] = WidgetBuildResult( + name=name, hash=hash_val, html=html_file.read_text() + ) + return results + + +def auto_load_tools(build_results): + """Automatically discover and load all widget tools.""" + tools = [] + for tool_file in TOOLS_DIR.glob("*_tool.py"): + module_name = tool_file.stem + try: + module = importlib.import_module(f"server.tools.{module_name}") + for name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseWidget) and obj is not BaseWidget: + tool_identifier = obj.identifier + if tool_identifier in build_results: + tool_instance = obj(build_results[tool_identifier]) + tools.append(tool_instance) + print( + f"[OK] Loaded tool: {name} (identifier: {tool_identifier})" + ) + else: + print( + f"[WARNING] Warning: No build result found for tool '{tool_identifier}'" + ) + except Exception as e: + print(f"[ERROR] Error loading {tool_file.name}: {e}") + return tools + + +# Parse command-line arguments +parser = argparse.ArgumentParser(description="FastApps MCP Server") +parser.add_argument( + "--build", action="store_true", help="Build widgets on startup (for development)" +) +args = parser.parse_args() + +# Load build results +if args.build: + # Build widgets on startup + print("[INFO] Building widgets") + builder = WidgetBuilder(PROJECT_ROOT) + build_results = builder.build_all() +else: + # Load pre-built widgets from assets directory + print("[INFO] Loading pre-built widgets from assets") + build_results = fetch_build_results() + +# Auto-load and register tools +tools = auto_load_tools(build_results) + +# Create MCP server +server = WidgetMCPServer(name="my-widgets", widgets=tools) + +# Optional: Enable OAuth 2.1 authentication +# Uncomment and configure to protect your widgets with OAuth: +# +# server = WidgetMCPServer( +# name="my-widgets", +# widgets=tools, +# auth_issuer_url="https://your-tenant.us.auth0.com", +# auth_resource_server_url="https://yourdomain.com/mcp", +# auth_required_scopes=["user"], +# ) +# +# See docs: https://fastapps.org/docs/auth + +app = server.get_app() + +if __name__ == "__main__": + print(f"\n[START] Starting server with {len(tools)} tools") + uvicorn.run(app, host="0.0.0.0", port=8001) diff --git a/examples/pizzaz/server/tools/__init__.py b/examples/pizzaz/server/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/pizzaz/server/tools/pizza_albums_tool.py b/examples/pizzaz/server/tools/pizza_albums_tool.py new file mode 100644 index 0000000..8fde0d6 --- /dev/null +++ b/examples/pizzaz/server/tools/pizza_albums_tool.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Any, Dict + +from fastapps import BaseWidget + +from .pizzaz_common import WIDGET_COPY, PizzaInput, build_response + + +class PizzaAlbumsWidget(BaseWidget): + identifier = "pizza-albums" + title = WIDGET_COPY[identifier].title + input_schema = PizzaInput + invoking = WIDGET_COPY[identifier].invoking + invoked = WIDGET_COPY[identifier].invoked + + widget_csp = { + "resource_domains": ["https://persistent.oaistatic.com"], + "connect_domains": [], + } + + async def execute( + self, input_data: PizzaInput, context=None, user=None + ) -> Dict[str, Any]: + return build_response(input_data.pizza_topping) diff --git a/examples/pizzaz/server/tools/pizza_carousel_tool.py b/examples/pizzaz/server/tools/pizza_carousel_tool.py new file mode 100644 index 0000000..dcaf71c --- /dev/null +++ b/examples/pizzaz/server/tools/pizza_carousel_tool.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Any, Dict + +from fastapps import BaseWidget + +from .pizzaz_common import WIDGET_COPY, PizzaInput, build_response + + +class PizzaCarouselWidget(BaseWidget): + identifier = "pizza-carousel" + title = WIDGET_COPY[identifier].title + input_schema = PizzaInput + invoking = WIDGET_COPY[identifier].invoking + invoked = WIDGET_COPY[identifier].invoked + + widget_csp = { + "resource_domains": ["https://persistent.oaistatic.com"], + "connect_domains": [], + } + + async def execute( + self, input_data: PizzaInput, context=None, user=None + ) -> Dict[str, Any]: + return build_response(input_data.pizza_topping) diff --git a/examples/pizzaz/server/tools/pizza_list_tool.py b/examples/pizzaz/server/tools/pizza_list_tool.py new file mode 100644 index 0000000..0d98db5 --- /dev/null +++ b/examples/pizzaz/server/tools/pizza_list_tool.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Any, Dict + +from fastapps import BaseWidget + +from .pizzaz_common import WIDGET_COPY, PizzaInput, build_response + + +class PizzaListWidget(BaseWidget): + identifier = "pizza-list" + title = WIDGET_COPY[identifier].title + input_schema = PizzaInput + invoking = WIDGET_COPY[identifier].invoking + invoked = WIDGET_COPY[identifier].invoked + + widget_csp = { + "resource_domains": ["https://persistent.oaistatic.com"], + "connect_domains": [], + } + + async def execute( + self, input_data: PizzaInput, context=None, user=None + ) -> Dict[str, Any]: + return build_response(input_data.pizza_topping) diff --git a/examples/pizzaz/server/tools/pizza_map_tool.py b/examples/pizzaz/server/tools/pizza_map_tool.py new file mode 100644 index 0000000..23fea2f --- /dev/null +++ b/examples/pizzaz/server/tools/pizza_map_tool.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from typing import Any, Dict + +from fastapps import BaseWidget + +from .pizzaz_common import WIDGET_COPY, PizzaInput, build_response + + +class PizzaMapWidget(BaseWidget): + identifier = "pizza-map" + title = WIDGET_COPY[identifier].title + input_schema = PizzaInput + invoking = WIDGET_COPY[identifier].invoking + invoked = WIDGET_COPY[identifier].invoked + + widget_csp = { + "resource_domains": [ + "https://api.mapbox.com", + "https://events.mapbox.com", + "https://*.tiles.mapbox.com", + "https://tile.mapbox.com", + "https://persistent.oaistatic.com", + ], + "connect_domains": [ + "https://api.mapbox.com", + "https://events.mapbox.com", + ], + } + + async def execute( + self, input_data: PizzaInput, context=None, user=None + ) -> Dict[str, Any]: + return build_response(input_data.pizza_topping) diff --git a/examples/pizzaz/server/tools/pizza_shop_tool.py b/examples/pizzaz/server/tools/pizza_shop_tool.py new file mode 100644 index 0000000..b08963d --- /dev/null +++ b/examples/pizzaz/server/tools/pizza_shop_tool.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Any, Dict + +from fastapps import BaseWidget + +from .pizzaz_common import WIDGET_COPY, PizzaInput, build_response + + +class PizzaShopWidget(BaseWidget): + identifier = "pizza-shop" + title = WIDGET_COPY[identifier].title + input_schema = PizzaInput + invoking = WIDGET_COPY[identifier].invoking + invoked = WIDGET_COPY[identifier].invoked + + widget_csp = { + "resource_domains": ["https://persistent.oaistatic.com"], + "connect_domains": [], + } + + async def execute( + self, input_data: PizzaInput, context=None, user=None + ) -> Dict[str, Any]: + return build_response(input_data.pizza_topping) diff --git a/examples/pizzaz/server/tools/pizzaz_common.py b/examples/pizzaz/server/tools/pizzaz_common.py new file mode 100644 index 0000000..924d950 --- /dev/null +++ b/examples/pizzaz/server/tools/pizzaz_common.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Dict + +from pydantic import BaseModel, Field + +from fastapps import ConfigDict + +DEFAULT_TOPPING = "Margherita" + + +class PizzaInput(BaseModel): + """Shared input schema for all Pizzaz widgets.""" + + pizza_topping: str = Field( + DEFAULT_TOPPING, + alias="pizzaTopping", + description="Topping to mention when rendering the widget.", + ) + + model_config = ConfigDict(populate_by_name=True, extra="forbid") + + +@dataclass(frozen=True) +class WidgetCopy: + title: str + invoking: str + invoked: str + + +WIDGET_COPY: Dict[str, WidgetCopy] = { + "pizza-map": WidgetCopy( + title="Show Pizza Map", + invoking="Hand-tossing a map", + invoked="Served a fresh map", + ), + "pizza-carousel": WidgetCopy( + title="Show Pizza Carousel", + invoking="Carousel some spots", + invoked="Served a fresh carousel", + ), + "pizza-albums": WidgetCopy( + title="Show Pizza Album", + invoking="Hand-tossing an album", + invoked="Served a fresh album", + ), + "pizza-list": WidgetCopy( + title="Show Pizza List", + invoking="Hand-tossing a list", + invoked="Served a fresh list", + ), + "pizza-shop": WidgetCopy( + title="Open Pizzaz Shop", + invoking="Opening the shop", + invoked="Shop opened", + ), +} + + +def build_response(topping: str) -> Dict[str, str]: + """Structured content returned to the widget.""" + display_topping = topping or DEFAULT_TOPPING + return { + "pizzaTopping": display_topping, + "message": f"Rendered a pizza experience featuring {display_topping}.", + } diff --git a/examples/pizzaz/tailwind.config.ts b/examples/pizzaz/tailwind.config.ts new file mode 100644 index 0000000..31d1832 --- /dev/null +++ b/examples/pizzaz/tailwind.config.ts @@ -0,0 +1,7 @@ +import type { Config } from "tailwindcss"; + +export default { + content: ["./widgets/**/*.{js,jsx,ts,tsx}"], + theme: {}, + plugins: [], +} satisfies Config; diff --git a/examples/pizzaz/widgets/pizza-albums/AlbumCard.jsx b/examples/pizzaz/widgets/pizza-albums/AlbumCard.jsx new file mode 100644 index 0000000..9867194 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-albums/AlbumCard.jsx @@ -0,0 +1,28 @@ +import React from "react"; + +function AlbumCard({ album, onSelect }) { + return ( + + ); +} + +export default AlbumCard; diff --git a/examples/pizzaz/widgets/pizza-albums/FilmStrip.jsx b/examples/pizzaz/widgets/pizza-albums/FilmStrip.jsx new file mode 100644 index 0000000..049cd95 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-albums/FilmStrip.jsx @@ -0,0 +1,30 @@ +import React from "react"; + +export default function FilmStrip({ album, selectedIndex, onSelect }) { + return ( +
+ {album.photos.map((photo, idx) => ( + + ))} +
+ ); +} diff --git a/examples/pizzaz/widgets/pizza-albums/FullscreenViewer.jsx b/examples/pizzaz/widgets/pizza-albums/FullscreenViewer.jsx new file mode 100644 index 0000000..9d89691 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-albums/FullscreenViewer.jsx @@ -0,0 +1,43 @@ +import React from "react"; +import { useMaxHeight } from "fastapps"; +import FilmStrip from "./FilmStrip"; + +export default function FullscreenViewer({ album }) { + const maxHeight = useMaxHeight() ?? undefined; + const [index, setIndex] = React.useState(0); + + React.useEffect(() => { + setIndex(0); + }, [album?.id]); + + const photo = album?.photos?.[index]; + + return ( +
+
+ {/* Film strip */} +
+ +
+ {/* Main photo */} +
+
+ {photo ? ( + {photo.title + ) : null} +
+
+
+
+ ); +} diff --git a/examples/pizzaz/widgets/pizza-albums/albums.json b/examples/pizzaz/widgets/pizza-albums/albums.json new file mode 100644 index 0000000..9c934ca --- /dev/null +++ b/examples/pizzaz/widgets/pizza-albums/albums.json @@ -0,0 +1,112 @@ +{ + "albums": [ + { + "id": "summer-escape", + "title": "Summer Slice", + "cover": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png", + "photos": [ + { + "id": "s1", + "title": "Waves", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + }, + { + "id": "s2", + "title": "Palm trees", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-3.png" + }, + { + "id": "s3", + "title": "Sunset", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-6.png" + } + ] + }, + { + "id": "city-lights", + "title": "Pepperoni Nights", + "cover": "https://persistent.oaistatic.com/pizzaz/pizzaz-4.png", + "photos": [ + { + "id": "c1", + "title": "Downtown", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-5.png" + }, + { + "id": "c2", + "title": "Neon", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png" + }, + { + "id": "c3", + "title": "Streets", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + } + ] + }, + { + "id": "into-the-woods", + "title": "Truffle Forest", + "cover": "https://persistent.oaistatic.com/pizzaz/pizzaz-3.png", + "photos": [ + { + "id": "n1", + "title": "Forest path", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-6.png" + }, + { + "id": "n2", + "title": "Misty", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-4.png" + }, + { + "id": "n3", + "title": "Waterfall", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-5.png" + } + ] + }, + { + "id": "pizza-tour", + "title": "Pizza tour", + "cover": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png", + "photos": [ + { + "id": "tonys-pizza-napoletana", + "title": "Tony's Pizza Napoletana", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + }, + { + "id": "golden-boy-pizza", + "title": "Golden Boy Pizza", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-3.png" + }, + { + "id": "pizzeria-delfina-mission", + "title": "Pizzeria Delfina (Mission)", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-6.png" + }, + { + "id": "ragazza", + "title": "Ragazza", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-4.png" + }, + { + "id": "del-popolo", + "title": "Del Popolo", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-5.png" + }, + { + "id": "square-pie-guys", + "title": "Square Pie Guys", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png" + }, + { + "id": "zero-zero", + "title": "Zero Zero", + "url": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + } + ] + } + ] +} diff --git a/examples/pizzaz/widgets/pizza-albums/index.jsx b/examples/pizzaz/widgets/pizza-albums/index.jsx new file mode 100644 index 0000000..ace149f --- /dev/null +++ b/examples/pizzaz/widgets/pizza-albums/index.jsx @@ -0,0 +1,150 @@ +import React from "react"; +import "../styles/index.css"; +import useEmblaCarousel from "embla-carousel-react"; +import { ArrowLeft, ArrowRight } from "lucide-react"; +import albumsData from "./albums.json"; +import FullscreenViewer from "./FullscreenViewer"; +import AlbumCard from "./AlbumCard"; +import { useDisplayMode, useMaxHeight, useWidgetProps } from "fastapps"; + +function AlbumsCarousel({ onSelect }) { + const albums = albumsData?.albums || []; + const [emblaRef, emblaApi] = useEmblaCarousel({ + align: "center", + loop: false, + containScroll: "trimSnaps", + slidesToScroll: "auto", + dragFree: false, + }); + const [canPrev, setCanPrev] = React.useState(false); + const [canNext, setCanNext] = React.useState(false); + + React.useEffect(() => { + if (!emblaApi) return; + const updateButtons = () => { + setCanPrev(emblaApi.canScrollPrev()); + setCanNext(emblaApi.canScrollNext()); + }; + updateButtons(); + emblaApi.on("select", updateButtons); + emblaApi.on("reInit", updateButtons); + return () => { + emblaApi.off("select", updateButtons); + emblaApi.off("reInit", updateButtons); + }; + }, [emblaApi]); + + return ( +
+
+
+ {albums.map((album) => ( + + ))} +
+
+
+
+
+
+
+
+ {canPrev && ( + + )} + {canNext && ( + + )} +
+ ); +} + +function App() { + const props = useWidgetProps(); + const featuredTopping = props?.pizzaTopping ?? "Margherita"; + const displayMode = useDisplayMode(); + const [selectedAlbum, setSelectedAlbum] = React.useState(null); + const maxHeight = useMaxHeight() ?? undefined; + + const handleSelectAlbum = (album) => { + setSelectedAlbum(album); + if (window?.openai?.requestDisplayMode) { + window.openai.requestDisplayMode({ mode: "fullscreen" }); + } + }; + + return ( +
+
+ Albums inspired by {featuredTopping} +
+ {displayMode !== "fullscreen" && ( + + )} + + {displayMode === "fullscreen" && selectedAlbum && ( + + )} +
+ ); +} + +export default function PizzaAlbumsWidget() { + return ; +} diff --git a/examples/pizzaz/widgets/pizza-carousel/PlaceCard.jsx b/examples/pizzaz/widgets/pizza-carousel/PlaceCard.jsx new file mode 100644 index 0000000..6ad53dd --- /dev/null +++ b/examples/pizzaz/widgets/pizza-carousel/PlaceCard.jsx @@ -0,0 +1,39 @@ +import React from "react"; +import { Star } from "lucide-react"; + +export default function PlaceCard({ place }) { + if (!place) return null; + return ( +
+
+ {place.name} +
+
+
{place.name}
+
+
+ {place.description ? ( +
+ {place.description} +
+ ) : null} +
+ +
+
+
+ ); +} diff --git a/examples/pizzaz/widgets/pizza-carousel/index.jsx b/examples/pizzaz/widgets/pizza-carousel/index.jsx new file mode 100644 index 0000000..9e33d01 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-carousel/index.jsx @@ -0,0 +1,121 @@ +import React from "react"; +import useEmblaCarousel from "embla-carousel-react"; +import { ArrowLeft, ArrowRight } from "lucide-react"; +import "../styles/index.css"; +import markers from "../shared/markers.json"; +import PlaceCard from "./PlaceCard"; +import { useWidgetProps } from "fastapps"; + +function App() { + const props = useWidgetProps(); + const topping = props?.pizzaTopping ?? "Margherita"; + const places = markers?.places || []; + const [emblaRef, emblaApi] = useEmblaCarousel({ + align: "center", + loop: false, + containScroll: "trimSnaps", + slidesToScroll: "auto", + dragFree: false, + }); + const [canPrev, setCanPrev] = React.useState(false); + const [canNext, setCanNext] = React.useState(false); + + React.useEffect(() => { + if (!emblaApi) return; + const updateButtons = () => { + setCanPrev(emblaApi.canScrollPrev()); + setCanNext(emblaApi.canScrollNext()); + }; + updateButtons(); + emblaApi.on("select", updateButtons); + emblaApi.on("reInit", updateButtons); + return () => { + emblaApi.off("select", updateButtons); + emblaApi.off("reInit", updateButtons); + }; + }, [emblaApi]); + + return ( +
+
+ + Featuring {topping} + +
+
+
+ {places.map((place) => ( + + ))} +
+
+ {/* Edge gradients */} +
+
+
+
+
+
+ {canPrev && ( + + )} + {canNext && ( + + )} +
+ ); +} + +export default function PizzaCarouselWidget() { + return ; +} diff --git a/examples/pizzaz/widgets/pizza-list/index.jsx b/examples/pizzaz/widgets/pizza-list/index.jsx new file mode 100644 index 0000000..1a02286 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-list/index.jsx @@ -0,0 +1,116 @@ +import React from "react"; +import "../styles/index.css"; +import markers from "../shared/markers.json"; +import { PlusCircle, Star } from "lucide-react"; +import { useWidgetProps } from "fastapps"; + +function App() { + const props = useWidgetProps(); + const topping = props?.pizzaTopping ?? "Margherita"; + const places = markers?.places || []; + + return ( +
+
+
+
+
+
+ National Best Pizza List +
+
+ Featuring the {topping} crowd favorites +
+
+
+ +
+
+
+ {places.slice(0, 7).map((place, i) => ( +
+
+
+
+ {place.name} +
+ {i + 1} +
+
+
+ {place.name} +
+
+
+ + + {place.rating?.toFixed + ? place.rating.toFixed(1) + : place.rating} + +
+
+ {place.city || "–"} +
+
+
+
+
+
+ {place.city || "–"} +
+
+ +
+
+
+ ))} + {places.length === 0 && ( +
+ No pizzerias found. +
+ )} +
+
+ +
+
+
+ ); +} + +export default function PizzaListWidget() { + return ; +} diff --git a/examples/pizzaz/widgets/pizza-map/Inspector.jsx b/examples/pizzaz/widgets/pizza-map/Inspector.jsx new file mode 100644 index 0000000..7666211 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-map/Inspector.jsx @@ -0,0 +1,97 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { Star, X } from "lucide-react"; + +export default function Inspector({ place, onClose }) { + if (!place) return null; + return ( + + +
+
+ {place.name} +
+ +
+
+
{place.name}
+
+
+
+
+ Add to favorites +
+
+ Contact +
+
+
+ {place.description} Enjoy a slice at one of SF's favorites. Fresh + ingredients, great crust, and cozy vibes. +
+
+ +
+
Reviews
+
    + {[ + { + user: "Leo M.", + avatar: "https://persistent.oaistatic.com/pizzaz/user1.png", + text: "Fantastic crust and balanced toppings. The marinara is spot on!", + }, + { + user: "Priya S.", + avatar: "https://persistent.oaistatic.com/pizzaz/user2.png", + text: "Cozy vibe and friendly staff. Quick service on a Friday night.", + }, + { + user: "Maya R.", + avatar: "https://persistent.oaistatic.com/pizzaz/user3.png", + text: "Great for sharing. Will definitely come back with friends.", + }, + ].map((review, idx) => ( +
  • +
    + {`${review.user} +
    +
    + {review.user} +
    +
    {review.text}
    +
    +
    +
  • + ))} +
+
+
+
+
+ ); +} diff --git a/examples/pizzaz/widgets/pizza-map/Sidebar.jsx b/examples/pizzaz/widgets/pizza-map/Sidebar.jsx new file mode 100644 index 0000000..91bfe79 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-map/Sidebar.jsx @@ -0,0 +1,159 @@ +import React from "react"; +import useEmblaCarousel from "embla-carousel-react"; +import { Settings2, Star } from "lucide-react"; +import { AnimatePresence, motion } from "framer-motion"; +import { useDisplayMode } from "fastapps"; + +function PlaceListItem({ place, isSelected, onClick }) { + return ( +
+
+ +
+
+ ); +} + +export default function Sidebar({ places, selectedId, onSelect }) { + const [emblaRef] = useEmblaCarousel({ dragFree: true, loop: false }); + const displayMode = useDisplayMode(); + const forceMobile = displayMode !== "fullscreen"; + const scrollRef = React.useRef(null); + const [showBottomFade, setShowBottomFade] = React.useState(false); + + const updateBottomFadeVisibility = React.useCallback(() => { + const el = scrollRef.current; + if (!el) return; + const atBottom = + Math.ceil(el.scrollTop + el.clientHeight) >= el.scrollHeight; + setShowBottomFade(!atBottom); + }, []); + + React.useEffect(() => { + updateBottomFadeVisibility(); + const el = scrollRef.current; + if (!el) return; + const onScroll = () => updateBottomFadeVisibility(); + el.addEventListener("scroll", onScroll, { passive: true }); + window.addEventListener("resize", updateBottomFadeVisibility); + return () => { + el.removeEventListener("scroll", onScroll); + window.removeEventListener("resize", updateBottomFadeVisibility); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [places]); + + return ( + <> + {/* Desktop/Tablet sidebar */} +
+
+
+ {places.length} results +
+
+
+
+ {places.map((place) => ( + onSelect(place)} + /> + ))} +
+
+ + {showBottomFade && ( + +
+ + )} + +
+ + {/* Mobile bottom carousel */} +
+
+
+
+ {places.map((place) => ( +
+ onSelect(place)} + /> +
+ ))} +
+
+
+
+ + ); +} diff --git a/examples/pizzaz/widgets/pizza-map/index.jsx b/examples/pizzaz/widgets/pizza-map/index.jsx new file mode 100644 index 0000000..45f90ef --- /dev/null +++ b/examples/pizzaz/widgets/pizza-map/index.jsx @@ -0,0 +1,302 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; +import mapboxgl from "mapbox-gl"; +import "mapbox-gl/dist/mapbox-gl.css"; +import "../styles/index.css"; +import markers from "../shared/markers.json"; +import { AnimatePresence } from "framer-motion"; +import Inspector from "./Inspector"; +import Sidebar from "./Sidebar"; +import { Maximize2 } from "lucide-react"; +import { + BrowserRouter, + Outlet, + Route, + Routes, + useLocation, + useNavigate, +} from "react-router-dom"; +import { + useDisplayMode, + useMaxHeight, + useWidgetProps, + useWidgetState, +} from "fastapps"; +import "./map.css"; + +mapboxgl.accessToken = + "pk.eyJ1IjoiZXJpY25pbmciLCJhIjoiY21icXlubWM1MDRiczJvb2xwM2p0amNyayJ9.n-3O6JI5nOp_Lw96ZO5vJQ"; + +function fitMapToMarkers(map, coords) { + if (!map || !coords.length) return; + if (coords.length === 1) { + map.flyTo({ center: coords[0], zoom: 12 }); + return; + } + const bounds = coords.reduce( + (b, c) => b.extend(c), + new mapboxgl.LngLatBounds(coords[0], coords[0]), + ); + map.fitBounds(bounds, { padding: 60, animate: true }); +} + +function App() { + const props = useWidgetProps(); + const topping = props?.pizzaTopping ?? "Margherita"; + const mapRef = useRef(null); + const mapObj = useRef(null); + const markerObjs = useRef([]); + const places = markers?.places || []; + const markerCoords = places.map((p) => p.coords); + const navigate = useNavigate(); + const location = useLocation(); + const selectedId = useMemo(() => { + const match = location?.pathname?.match(/(?:^|\/)place\/([^/]+)/); + return match && match[1] ? match[1] : null; + }, [location?.pathname]); + const selectedPlace = places.find((p) => p.id === selectedId) || null; + const [viewState, setViewState] = useState(() => ({ + center: markerCoords.length > 0 ? markerCoords[0] : [0, 0], + zoom: markerCoords.length > 0 ? 12 : 2, + })); + const [_, setWidgetState] = useWidgetState(() => ({ + center: markerCoords.length > 0 ? markerCoords[0] : [0, 0], + zoom: markerCoords.length > 0 ? 12 : 2, + pizzaTopping: topping, + })); + const displayMode = useDisplayMode(); + const allowInspector = displayMode === "fullscreen"; + const maxHeight = useMaxHeight() ?? undefined; + + useEffect(() => { + if (mapObj.current) return; + mapObj.current = new mapboxgl.Map({ + container: mapRef.current, + style: "mapbox://styles/mapbox/streets-v12", + center: markerCoords.length > 0 ? markerCoords[0] : [0, 0], + zoom: markerCoords.length > 0 ? 12 : 2, + attributionControl: false, + }); + addAllMarkers(places); + setTimeout(() => { + fitMapToMarkers(mapObj.current, markerCoords); + }, 0); + // after first paint + requestAnimationFrame(() => mapObj.current.resize()); + + // or keep it in sync with window resizes + window.addEventListener("resize", mapObj.current.resize); + + return () => { + window.removeEventListener("resize", mapObj.current.resize); + mapObj.current.remove(); + }; + // eslint-disable-next-line + }, []); + + useEffect(() => { + if (!mapObj.current) return; + const handler = () => { + const c = mapObj.current.getCenter(); + setViewState({ center: [c.lng, c.lat], zoom: mapObj.current.getZoom() }); + }; + mapObj.current.on("moveend", handler); + return () => { + mapObj.current.off("moveend", handler); + }; + }, []); + + function addAllMarkers(placesList) { + markerObjs.current.forEach((m) => m.remove()); + markerObjs.current = []; + placesList.forEach((place) => { + const marker = new mapboxgl.Marker({ + color: "#F46C21", + }) + .setLngLat(place.coords) + .addTo(mapObj.current); + const el = marker.getElement(); + if (el) { + el.style.cursor = "pointer"; + el.addEventListener("click", () => { + navigate(`/place/${place.id}`); + panTo(place.coords, { offsetForInspector: true }); + }); + } + markerObjs.current.push(marker); + }); + } + + function getInspectorOffsetPx() { + if (displayMode !== "fullscreen") return 0; + if (typeof window === "undefined") return 0; + const isXlUp = + window.matchMedia && window.matchMedia("(min-width: 1280px)").matches; + const el = document.querySelector(".pizzaz-inspector"); + const w = el ? el.getBoundingClientRect().width : 360; + const half = Math.round(w / 2); + // xl: inspector on right → negative x offset; lg: inspector on left → positive x offset + return isXlUp ? -half : half; + } + + function panTo( + coord, + { offsetForInspector } = { offsetForInspector: false }, + ) { + if (!mapObj.current) return; + const inspectorOffset = offsetForInspector ? getInspectorOffsetPx() : 0; + const flyOpts = { + center: coord, + zoom: 14, + speed: 1.2, + curve: 1.6, + }; + if (inspectorOffset) { + flyOpts.offset = [inspectorOffset, 0]; + } + mapObj.current.flyTo(flyOpts); + } + + useEffect(() => { + if (!mapObj.current) return; + addAllMarkers(places); + }, [places]); + + // Pan the map when the selected place changes via routing + useEffect(() => { + if (!mapObj.current || !selectedPlace) return; + panTo(selectedPlace.coords, { offsetForInspector: true }); + }, [selectedId]); + + // Ensure Mapbox resizes when container maxHeight/display mode changes + useEffect(() => { + if (!mapObj.current) return; + mapObj.current.resize(); + }, [maxHeight, displayMode]); + + useEffect(() => { + setWidgetState((prev) => ({ + ...(prev ?? {}), + center: viewState.center, + zoom: viewState.zoom, + markers: markerCoords, + pizzaTopping: topping, + })); + }, [markerCoords, setWidgetState, topping, viewState]); + + return ( + <> +
+ + {displayMode !== "fullscreen" && ( + + )} +
+
+ Featuring {topping} +
+
+ {/* Sidebar */} + { + navigate(`/place/${place.id}`); + panTo(place.coords, { offsetForInspector: true }); + }} + /> + + {/* Inspector (right) */} + + {allowInspector && selectedPlace && ( + navigate("..")} + /> + )} + + + {/* Map */} +
+
+
+
+ + {/* Suggestion chips (bottom, fullscreen) */} + {displayMode === "fullscreen" && ( +
+
+ {["Open now", "Top rated", "Vegetarian friendly"].map((label) => ( + + ))} +
+
+ )} + + ); +} + +function RouterRoot() { + return ( + + }> + } /> + + + ); +} + +export default function PizzaMapWidget() { + return ( + + + + ); +} diff --git a/examples/pizzaz/widgets/pizza-map/map.css b/examples/pizzaz/widgets/pizza-map/map.css new file mode 100644 index 0000000..9488102 --- /dev/null +++ b/examples/pizzaz/widgets/pizza-map/map.css @@ -0,0 +1,707 @@ +.mapboxgl-map { + font: 12px/20px Helvetica Neue, Arial, Helvetica, sans-serif; + overflow: hidden; + position: relative; + -webkit-tap-highlight-color: rgb(0 0 0/0); +} +.mapboxgl-canvas { + left: 0; + position: absolute; + top: 0; +} +.mapboxgl-map:-webkit-full-screen { + height: 100%; + width: 100%; +} +.mapboxgl-canary { + background-color: salmon; +} +.mapboxgl-canvas-container.mapboxgl-interactive, +.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass { + cursor: grab; + -webkit-user-select: none; + user-select: none; +} +.mapboxgl-canvas-container.mapboxgl-interactive.mapboxgl-track-pointer { + cursor: pointer; +} +.mapboxgl-canvas-container.mapboxgl-interactive:active, +.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass:active { + cursor: grabbing; +} +.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate, +.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate .mapboxgl-canvas { + touch-action: pan-x pan-y; +} +.mapboxgl-canvas-container.mapboxgl-touch-drag-pan, +.mapboxgl-canvas-container.mapboxgl-touch-drag-pan .mapboxgl-canvas { + touch-action: pinch-zoom; +} +.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan, +.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan + .mapboxgl-canvas { + touch-action: none; +} +.mapboxgl-ctrl-bottom, +.mapboxgl-ctrl-bottom-left, +.mapboxgl-ctrl-bottom-right, +.mapboxgl-ctrl-left, +.mapboxgl-ctrl-right, +.mapboxgl-ctrl-top, +.mapboxgl-ctrl-top-left, +.mapboxgl-ctrl-top-right { + pointer-events: none; + position: absolute; + z-index: 2; +} +.mapboxgl-ctrl-top-left { + left: 0; + top: 0; +} +.mapboxgl-ctrl-top { + left: 50%; + top: 0; + transform: translateX(-50%); +} +.mapboxgl-ctrl-top-right { + right: 0; + top: 0; +} +.mapboxgl-ctrl-right { + right: 0; + top: 50%; + transform: translateY(-50%); +} +.mapboxgl-ctrl-bottom-right { + bottom: 0; + right: 0; +} +.mapboxgl-ctrl-bottom { + bottom: 0; + left: 50%; + transform: translateX(-50%); +} +.mapboxgl-ctrl-bottom-left { + bottom: 0; + left: 0; +} +.mapboxgl-ctrl-left { + left: 0; + top: 50%; + transform: translateY(-50%); +} +.mapboxgl-ctrl { + clear: both; + pointer-events: auto; + transform: translate(0); +} +.mapboxgl-ctrl-top-left .mapboxgl-ctrl { + float: left; + margin: 10px 0 0 10px; +} +.mapboxgl-ctrl-top .mapboxgl-ctrl { + float: left; + margin: 10px 0; +} +.mapboxgl-ctrl-top-right .mapboxgl-ctrl { + float: right; + margin: 10px 10px 0 0; +} +.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl, +.mapboxgl-ctrl-right .mapboxgl-ctrl { + float: right; + margin: 0 10px 10px 0; +} +.mapboxgl-ctrl-bottom .mapboxgl-ctrl { + float: left; + margin: 10px 0; +} +.mapboxgl-ctrl-bottom-left .mapboxgl-ctrl, +.mapboxgl-ctrl-left .mapboxgl-ctrl { + float: left; + margin: 0 0 10px 10px; +} +.mapboxgl-ctrl-group { + background: #fff; + border-radius: 4px; +} +.mapboxgl-ctrl-group:not(:empty) { + box-shadow: 0 0 0 2px #0000001a; +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl-group:not(:empty) { + box-shadow: 0 0 0 2px ButtonText; + } +} +.mapboxgl-ctrl-group button { + background-color: initial; + border: 0; + box-sizing: border-box; + cursor: pointer; + display: block; + height: 29px; + outline: none; + overflow: hidden; + padding: 0; + width: 29px; +} +.mapboxgl-ctrl-group button + button { + border-top: 1px solid #ddd; +} +.mapboxgl-ctrl button .mapboxgl-ctrl-icon { + background-position: 50%; + background-repeat: no-repeat; + display: block; + height: 100%; + width: 100%; +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl-icon { + background-color: initial; + } + .mapboxgl-ctrl-group button + button { + border-top: 1px solid ButtonText; + } +} +.mapboxgl-ctrl-attrib-button:focus, +.mapboxgl-ctrl-group button:focus { + box-shadow: 0 0 2px 2px #0096ff; +} +.mapboxgl-ctrl button:disabled { + cursor: not-allowed; +} +.mapboxgl-ctrl button:disabled .mapboxgl-ctrl-icon { + opacity: 0.25; +} +.mapboxgl-ctrl-group button:first-child { + border-radius: 4px 4px 0 0; +} +.mapboxgl-ctrl-group button:last-child { + border-radius: 0 0 4px 4px; +} +.mapboxgl-ctrl-group button:only-child { + border-radius: inherit; +} +.mapboxgl-ctrl button:not(:disabled):hover { + background-color: #0000000d; +} +.mapboxgl-ctrl-group button:focus:focus-visible { + box-shadow: 0 0 2px 2px #0096ff; +} +.mapboxgl-ctrl-group button:focus:not(:focus-visible) { + box-shadow: none; +} +.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); + } +} +@media (-ms-high-contrast: black-on-white) { + .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); + } +} +.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); + } +} +@media (-ms-high-contrast: black-on-white) { + .mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); + } +} +.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath id='south' d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E"); +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath id='south' d='M10.5 16l4 8 4-8h-8z' fill='%23999'/%3E%3C/svg%3E"); + } +} +@media (-ms-high-contrast: black-on-white) { + .mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 29 29'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath id='south' d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E"); + } +} +.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23aaa'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' fill='%23f00'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2' display='none'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2' display='none'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); +} +.mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-waiting + .mapboxgl-ctrl-icon { + animation: mapboxgl-spin 2s linear infinite; +} +@media (-ms-high-contrast: active) { + .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23999'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' fill='%23f00'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2' display='none'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl + button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error + .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2' display='none'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } +} +@media (-ms-high-contrast: black-on-white) { + .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23000'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' display='none'/%3E%3C/svg%3E"); + } + .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23666'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7z'/%3E%3Ccircle id='dot' cx='10' cy='10' r='2'/%3E%3Cpath id='stroke' d='M14 5l1 1-9 9-1-1 9-9z' fill='%23f00'/%3E%3C/svg%3E"); + } +} +@keyframes mapboxgl-spin { + 0% { + transform: rotate(0deg); + } + to { + transform: rotate(1turn); + } +} +a.mapboxgl-ctrl-logo { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd' viewBox='0 0 88 23'%3E%3Cdefs%3E%3Cpath id='logo' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='text' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='clip'%3E%3Crect x='0' y='0' width='100%25' height='100%25' fill='white'/%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/mask%3E%3Cg id='outline' opacity='0.3' stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23clip)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23text' mask='url(%23clip)'/%3E%3C/g%3E%3Cg id='fill' opacity='0.9' fill='%23fff'%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/g%3E%3C/svg%3E"); + background-repeat: no-repeat; + cursor: pointer; + display: block; + height: 23px; + margin: 0 0 -4px -4px; + overflow: hidden; + width: 88px; +} +a.mapboxgl-ctrl-logo.mapboxgl-compact { + width: 23px; +} +@media (-ms-high-contrast: active) { + a.mapboxgl-ctrl-logo { + background-color: initial; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd' viewBox='0 0 88 23'%3E%3Cdefs%3E%3Cpath id='logo' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='text' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='clip'%3E%3Crect x='0' y='0' width='100%25' height='100%25' fill='white'/%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/mask%3E%3Cg id='outline' opacity='1' stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23clip)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23text' mask='url(%23clip)'/%3E%3C/g%3E%3Cg id='fill' opacity='1' fill='%23fff'%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/g%3E%3C/svg%3E"); + } +} +@media (-ms-high-contrast: black-on-white) { + a.mapboxgl-ctrl-logo { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd' viewBox='0 0 88 23'%3E%3Cdefs%3E%3Cpath id='logo' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='text' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='clip'%3E%3Crect x='0' y='0' width='100%25' height='100%25' fill='white'/%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/mask%3E%3Cg id='outline' opacity='1' stroke='%23fff' stroke-width='3' fill='%23fff'%3E%3Ccircle mask='url(%23clip)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23text' mask='url(%23clip)'/%3E%3C/g%3E%3Cg id='fill' opacity='1' fill='%23000'%3E%3Cuse xlink:href='%23logo'/%3E%3Cuse xlink:href='%23text'/%3E%3C/g%3E%3C/svg%3E"); + } +} +.mapboxgl-ctrl.mapboxgl-ctrl-attrib { + background-color: #ffffff80; + margin: 0; + padding: 0 5px; +} +@media screen { + .mapboxgl-ctrl-attrib.mapboxgl-compact { + background-color: #fff; + border-radius: 12px; + box-sizing: initial; + margin: 10px; + min-height: 20px; + padding: 2px 24px 2px 0; + position: relative; + } + .mapboxgl-ctrl-attrib.mapboxgl-compact-show { + padding: 2px 28px 2px 8px; + visibility: visible; + } + .mapboxgl-ctrl-bottom-left > .mapboxgl-ctrl-attrib.mapboxgl-compact-show, + .mapboxgl-ctrl-left > .mapboxgl-ctrl-attrib.mapboxgl-compact-show, + .mapboxgl-ctrl-top-left > .mapboxgl-ctrl-attrib.mapboxgl-compact-show { + border-radius: 12px; + padding: 2px 8px 2px 28px; + } + .mapboxgl-ctrl-attrib.mapboxgl-compact .mapboxgl-ctrl-attrib-inner { + display: none; + } + .mapboxgl-ctrl-attrib-button { + background-color: #ffffff80; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E"); + border: 0; + border-radius: 12px; + box-sizing: border-box; + cursor: pointer; + display: none; + height: 24px; + outline: none; + position: absolute; + right: 0; + top: 0; + width: 24px; + } + .mapboxgl-ctrl-bottom-left .mapboxgl-ctrl-attrib-button, + .mapboxgl-ctrl-left .mapboxgl-ctrl-attrib-button, + .mapboxgl-ctrl-top-left .mapboxgl-ctrl-attrib-button { + left: 0; + } + .mapboxgl-ctrl-attrib.mapboxgl-compact .mapboxgl-ctrl-attrib-button, + .mapboxgl-ctrl-attrib.mapboxgl-compact-show .mapboxgl-ctrl-attrib-inner { + display: block; + } + .mapboxgl-ctrl-attrib.mapboxgl-compact-show .mapboxgl-ctrl-attrib-button { + background-color: #0000000d; + } + .mapboxgl-ctrl-bottom-right > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + bottom: 0; + right: 0; + } + .mapboxgl-ctrl-right > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + right: 0; + } + .mapboxgl-ctrl-top-right > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + right: 0; + top: 0; + } + .mapboxgl-ctrl-top-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + left: 0; + top: 0; + } + .mapboxgl-ctrl-bottom-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + bottom: 0; + left: 0; + } + .mapboxgl-ctrl-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + left: 0; + } +} +@media screen and (-ms-high-contrast: active) { + .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' fill='%23fff'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E"); + } +} +@media screen and (-ms-high-contrast: black-on-white) { + .mapboxgl-ctrl-attrib.mapboxgl-compact:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E"); + } +} +.mapboxgl-ctrl-attrib a { + color: #000000bf; + text-decoration: none; +} +.mapboxgl-ctrl-attrib a:hover { + color: inherit; + text-decoration: underline; +} +.mapboxgl-ctrl-attrib .mapbox-improve-map { + font-weight: 700; + margin-left: 2px; +} +.mapboxgl-attrib-empty { + display: none; +} +.mapboxgl-ctrl-scale { + background-color: #ffffffbf; + border: 2px solid #333; + border-top: #333; + box-sizing: border-box; + color: #333; + font-size: 10px; + padding: 0 5px; + white-space: nowrap; +} +.mapboxgl-popup { + display: flex; + left: 0; + pointer-events: none; + position: absolute; + top: 0; + will-change: transform; +} +.mapboxgl-popup-anchor-top, +.mapboxgl-popup-anchor-top-left, +.mapboxgl-popup-anchor-top-right { + flex-direction: column; +} +.mapboxgl-popup-anchor-bottom, +.mapboxgl-popup-anchor-bottom-left, +.mapboxgl-popup-anchor-bottom-right { + flex-direction: column-reverse; +} +.mapboxgl-popup-anchor-left { + flex-direction: row; +} +.mapboxgl-popup-anchor-right { + flex-direction: row-reverse; +} +.mapboxgl-popup-tip { + border: 10px solid #0000; + height: 0; + width: 0; + z-index: 1; +} +.mapboxgl-popup-anchor-top .mapboxgl-popup-tip { + align-self: center; + border-bottom-color: #fff; + border-top: none; +} +.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip { + align-self: flex-start; + border-bottom-color: #fff; + border-left: none; + border-top: none; +} +.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip { + align-self: flex-end; + border-bottom-color: #fff; + border-right: none; + border-top: none; +} +.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip { + align-self: center; + border-bottom: none; + border-top-color: #fff; +} +.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip { + align-self: flex-start; + border-bottom: none; + border-left: none; + border-top-color: #fff; +} +.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip { + align-self: flex-end; + border-bottom: none; + border-right: none; + border-top-color: #fff; +} +.mapboxgl-popup-anchor-left .mapboxgl-popup-tip { + align-self: center; + border-left: none; + border-right-color: #fff; +} +.mapboxgl-popup-anchor-right .mapboxgl-popup-tip { + align-self: center; + border-left-color: #fff; + border-right: none; +} +.mapboxgl-popup-close-button { + background-color: initial; + border: 0; + border-radius: 0 3px 0 0; + cursor: pointer; + position: absolute; + right: 0; + top: 0; +} +.mapboxgl-popup-close-button:hover { + background-color: #0000000d; +} +.mapboxgl-popup-content { + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px #0000001a; + padding: 10px 10px 15px; + pointer-events: auto; + position: relative; +} +.mapboxgl-popup-anchor-top-left .mapboxgl-popup-content { + border-top-left-radius: 0; +} +.mapboxgl-popup-anchor-top-right .mapboxgl-popup-content { + border-top-right-radius: 0; +} +.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-content { + border-bottom-left-radius: 0; +} +.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-content { + border-bottom-right-radius: 0; +} +.mapboxgl-popup-track-pointer { + display: none; +} +.mapboxgl-popup-track-pointer * { + pointer-events: none; + user-select: none; +} +.mapboxgl-map:hover .mapboxgl-popup-track-pointer { + display: flex; +} +.mapboxgl-map:active .mapboxgl-popup-track-pointer { + display: none; +} +.mapboxgl-marker { + left: 0; + opacity: 1; + position: absolute; + top: 0; + transition: opacity 0.2s; + will-change: transform; +} +.mapboxgl-user-location-dot, +.mapboxgl-user-location-dot:before { + background-color: #1da1f2; + border-radius: 50%; + height: 15px; + width: 15px; +} +.mapboxgl-user-location-dot:before { + animation: mapboxgl-user-location-dot-pulse 2s infinite; + content: ""; + position: absolute; +} +.mapboxgl-user-location-dot:after { + border: 2px solid #fff; + border-radius: 50%; + box-shadow: 0 0 3px #00000059; + box-sizing: border-box; + content: ""; + height: 19px; + left: -2px; + position: absolute; + top: -2px; + width: 19px; +} +.mapboxgl-user-location-show-heading .mapboxgl-user-location-heading { + height: 0; + width: 0; +} +.mapboxgl-user-location-show-heading .mapboxgl-user-location-heading:after, +.mapboxgl-user-location-show-heading .mapboxgl-user-location-heading:before { + border-bottom: 7.5px solid #4aa1eb; + content: ""; + position: absolute; +} +.mapboxgl-user-location-show-heading .mapboxgl-user-location-heading:before { + border-left: 7.5px solid #0000; + transform: translateY(-28px) skewY(-20deg); +} +.mapboxgl-user-location-show-heading .mapboxgl-user-location-heading:after { + border-right: 7.5px solid #0000; + transform: translate(7.5px, -28px) skewY(20deg); +} +@keyframes mapboxgl-user-location-dot-pulse { + 0% { + opacity: 1; + transform: scale(1); + } + 70% { + opacity: 0; + transform: scale(3); + } + to { + opacity: 0; + transform: scale(1); + } +} +.mapboxgl-user-location-dot-stale { + background-color: #aaa; +} +.mapboxgl-user-location-dot-stale:after { + display: none; +} +.mapboxgl-user-location-accuracy-circle { + background-color: #1da1f233; + border-radius: 100%; + height: 1px; + width: 1px; +} +.mapboxgl-crosshair, +.mapboxgl-crosshair .mapboxgl-interactive, +.mapboxgl-crosshair .mapboxgl-interactive:active { + cursor: crosshair; +} +.mapboxgl-boxzoom { + background: #fff; + border: 2px dotted #202020; + height: 0; + left: 0; + opacity: 0.5; + position: absolute; + top: 0; + width: 0; +} +@media print { + .mapbox-improve-map { + display: none; + } +} +.mapboxgl-scroll-zoom-blocker, +.mapboxgl-touch-pan-blocker { + align-items: center; + background: #000000b3; + color: #fff; + display: flex; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, + sans-serif; + height: 100%; + justify-content: center; + left: 0; + opacity: 0; + pointer-events: none; + position: absolute; + text-align: center; + top: 0; + transition: opacity 0.75s ease-in-out; + transition-delay: 1s; + width: 100%; +} +.mapboxgl-scroll-zoom-blocker-show, +.mapboxgl-touch-pan-blocker-show { + opacity: 1; + transition: opacity 0.1s ease-in-out; +} +.mapboxgl-canvas-container.mapboxgl-touch-pan-blocker-override.mapboxgl-scrollable-page, +.mapboxgl-canvas-container.mapboxgl-touch-pan-blocker-override.mapboxgl-scrollable-page + .mapboxgl-canvas { + touch-action: pan-x pan-y; +} diff --git a/examples/pizzaz/widgets/pizza-shop/index.tsx b/examples/pizzaz/widgets/pizza-shop/index.tsx new file mode 100644 index 0000000..eff9c8d --- /dev/null +++ b/examples/pizzaz/widgets/pizza-shop/index.tsx @@ -0,0 +1,1464 @@ +import "../styles/index.css"; +import clsx from "clsx"; +import { AnimatePresence, LayoutGroup, motion } from "framer-motion"; +import { Minus, Plus, ShoppingCart } from "lucide-react"; +import { + type MouseEvent as ReactMouseEvent, + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; +import { BrowserRouter, useLocation, useNavigate } from "react-router-dom"; +import { + useDisplayMode, + useMaxHeight, + useOpenAiGlobal, + useWidgetProps, + useWidgetState, +} from "fastapps"; + +type NutritionFact = { + label: string; + value: string; +}; + +type CartItem = { + id: string; + name: string; + price: number; + description: string; + shortDescription?: string; + detailSummary?: string; + nutritionFacts?: NutritionFact[]; + highlights?: string[]; + tags?: string[]; + quantity: number; + image: string; +}; + +type PizzazCartWidgetState = { + state?: "checkout" | null; + cartItems?: CartItem[]; + selectedCartItemId?: string | null; +}; + +type PizzazCartWidgetProps = { + cartItems?: CartItem[]; + widgetState?: Partial | null; +}; + +const SERVICE_FEE = 3; +const DELIVERY_FEE = 2.99; +const TAX_FEE = 3.4; +const CONTINUE_TO_PAYMENT_EVENT = "pizzaz-shop:continue-to-payment"; + +const FILTERS: Array<{ + id: "all" | "vegetarian" | "vegan" | "size" | "spicy"; + label: string; + tag?: string; +}> = [ + { id: "all", label: "All" }, + { id: "vegetarian", label: "Vegetarian", tag: "vegetarian" }, + { id: "vegan", label: "Vegan", tag: "vegan" }, + { id: "size", label: "Size", tag: "size" }, + { id: "spicy", label: "Spicy", tag: "spicy" }, +]; + +const INITIAL_CART_ITEMS: CartItem[] = [ + { + id: "marys-chicken", + name: "Mary's Chicken", + price: 19.48, + description: + "Tender organic chicken breasts trimmed for easy cooking. Raised without antibiotics and air chilled for exceptional flavor.", + shortDescription: "Organic chicken breasts", + detailSummary: "4 lbs • $3.99/lb", + nutritionFacts: [ + { label: "Protein", value: "8g" }, + { label: "Fat", value: "9g" }, + { label: "Sugar", value: "12g" }, + { label: "Calories", value: "160" }, + ], + highlights: [ + "No antibiotics or added hormones.", + "Air chilled and never frozen for peak flavor.", + "Raised in the USA on a vegetarian diet.", + ], + quantity: 2, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/chicken.png", + tags: ["size"], + }, + { + id: "avocados", + name: "Avocados", + price: 1, + description: + "Creamy Hass avocados picked at peak ripeness. Ideal for smashing into guacamole or topping tacos.", + shortDescription: "Creamy Hass avocados", + detailSummary: "3 ct • $1.00/ea", + nutritionFacts: [ + { label: "Fiber", value: "7g" }, + { label: "Fat", value: "15g" }, + { label: "Potassium", value: "485mg" }, + { label: "Calories", value: "160" }, + ], + highlights: [ + "Perfectly ripe and ready for slicing.", + "Rich in healthy fats and naturally creamy.", + ], + quantity: 2, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/avocado.png", + tags: ["vegan"], + }, + { + id: "hojicha-pizza", + name: "Hojicha Pizza", + price: 15.5, + description: + "Wood-fired crust layered with smoky hojicha tea sauce and melted mozzarella with a drizzle of honey for an adventurous slice.", + shortDescription: "Smoky hojicha sauce & honey", + detailSummary: '12" pie • Serves 2', + nutritionFacts: [ + { label: "Protein", value: "14g" }, + { label: "Fat", value: "18g" }, + { label: "Sugar", value: "9g" }, + { label: "Calories", value: "320" }, + ], + highlights: [ + "Smoky roasted hojicha glaze with honey drizzle.", + "Stone-fired crust with a delicate char.", + ], + quantity: 2, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/hojicha-pizza.png", + tags: ["vegetarian", "size", "spicy"], + }, + { + id: "chicken-pizza", + name: "Chicken Pizza", + price: 7, + description: + "Classic thin-crust pizza topped with roasted chicken, caramelized onions, and herb pesto.", + shortDescription: "Roasted chicken & pesto", + detailSummary: '10" personal • Serves 1', + nutritionFacts: [ + { label: "Protein", value: "20g" }, + { label: "Fat", value: "11g" }, + { label: "Carbs", value: "36g" }, + { label: "Calories", value: "290" }, + ], + highlights: [ + "Roasted chicken with caramelized onions.", + "Fresh basil pesto and mozzarella.", + ], + quantity: 1, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/chicken-pizza.png", + tags: ["size"], + }, + { + id: "matcha-pizza", + name: "Matcha Pizza", + price: 5, + description: + "Crisp dough spread with velvety matcha cream and mascarpone. Earthy green tea notes balance gentle sweetness.", + shortDescription: "Velvety matcha cream", + detailSummary: '8" dessert • Serves 2', + nutritionFacts: [ + { label: "Protein", value: "6g" }, + { label: "Fat", value: "10g" }, + { label: "Sugar", value: "14g" }, + { label: "Calories", value: "240" }, + ], + highlights: [ + "Stone-baked crust with delicate crunch.", + "Matcha mascarpone with white chocolate drizzle.", + ], + quantity: 1, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/matcha-pizza.png", + tags: ["vegetarian"], + }, + { + id: "pesto-pizza", + name: "Pesto Pizza", + price: 12.5, + description: + "Hand-tossed crust brushed with bright basil pesto, layered with fresh mozzarella, and finished with roasted cherry tomatoes.", + shortDescription: "Basil pesto & tomatoes", + detailSummary: '12" pie • Serves 2', + nutritionFacts: [ + { label: "Protein", value: "16g" }, + { label: "Fat", value: "14g" }, + { label: "Carbs", value: "28g" }, + { label: "Calories", value: "310" }, + ], + highlights: [ + "House-made pesto with sweet basil and pine nuts.", + "Roasted cherry tomatoes for a pop of acidity.", + ], + quantity: 1, + image: "https://persistent.oaistatic.com/pizzaz-cart-xl/matcha-pizza.png", + tags: ["vegetarian", "size"], + }, +]; + +const cloneCartItem = (item: CartItem): CartItem => ({ + ...item, + nutritionFacts: item.nutritionFacts?.map((fact) => ({ ...fact })), + highlights: item.highlights ? [...item.highlights] : undefined, + tags: item.tags ? [...item.tags] : undefined, +}); + +const createDefaultCartItems = (): CartItem[] => + INITIAL_CART_ITEMS.map((item) => cloneCartItem(item)); + +const createDefaultWidgetState = (): PizzazCartWidgetState => ({ + state: null, + cartItems: createDefaultCartItems(), + selectedCartItemId: null, +}); + +const nutritionFactsEqual = ( + a?: NutritionFact[], + b?: NutritionFact[], +): boolean => { + if (!a?.length && !b?.length) { + return true; + } + if (!a || !b || a.length !== b.length) { + return false; + } + return a.every((fact, index) => { + const other = b[index]; + if (!other) { + return false; + } + return fact.label === other.label && fact.value === other.value; + }); +}; + +const highlightsEqual = (a?: string[], b?: string[]): boolean => { + if (!a?.length && !b?.length) { + return true; + } + if (!a || !b || a.length !== b.length) { + return false; + } + return a.every((highlight, index) => highlight === b[index]); +}; + +const cartItemsEqual = (a: CartItem[], b: CartItem[]): boolean => { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i += 1) { + const left = a[i]; + const right = b[i]; + if (!right) { + return false; + } + if ( + left.id !== right.id || + left.quantity !== right.quantity || + left.name !== right.name || + left.price !== right.price || + left.description !== right.description || + left.shortDescription !== right.shortDescription || + left.detailSummary !== right.detailSummary || + !nutritionFactsEqual(left.nutritionFacts, right.nutritionFacts) || + !highlightsEqual(left.highlights, right.highlights) || + !highlightsEqual(left.tags, right.tags) || + left.image !== right.image + ) { + return false; + } + } + return true; +}; + +type SelectedCartItemPanelProps = { + item: CartItem; + onAdjustQuantity: (id: string, delta: number) => void; +}; + +function SelectedCartItemPanel({ + item, + onAdjustQuantity, +}: SelectedCartItemPanelProps) { + const nutritionFacts = Array.isArray(item.nutritionFacts) + ? item.nutritionFacts + : []; + const highlights = Array.isArray(item.highlights) ? item.highlights : []; + + const hasNutritionFacts = nutritionFacts.length > 0; + const hasHighlights = highlights.length > 0; + + return ( +
+
+
+ {item.name} +
+
+
+ +
+
+
+

+ ${item.price.toFixed(2)} +

+

{item.name}

+
+
+ + + {item.quantity} + + +
+
+ +

{item.description}

+ + {item.detailSummary ? ( +

{item.detailSummary}

+ ) : null} + + {hasNutritionFacts ? ( +
+ {nutritionFacts.map((fact) => ( +
+

{fact.value}

+

{fact.label}

+
+ ))} +
+ ) : null} + + {hasHighlights ? ( +
+ {highlights.map((highlight, index) => ( +

{highlight}

+ ))} +
+ ) : null} +
+
+ ); +} + +type CheckoutDetailsPanelProps = { + shouldShowCheckoutOnly: boolean; + subtotal: number; + total: number; + onContinueToPayment?: () => void; +}; + +function CheckoutDetailsPanel({ + shouldShowCheckoutOnly, + subtotal, + total, + onContinueToPayment, +}: CheckoutDetailsPanelProps) { + return ( + <> + {!shouldShowCheckoutOnly && ( +
+

Checkout details

+
+ )} + +
+
+

Delivery address

+
+
+

+ 1234 Main St, San Francisco, CA +

+

+ Leave at door - Delivery instructions +

+
+ +
+
+
+

Fast

+

+ 50 min - 2 hr 10 min +

+
+ Free +
+
+
+

Priority

+

35 min

+
+ Free +
+
+
+ +
+
+

Delivery tip

+

100% goes to the shopper

+
+
+ + + + +
+
+ +
+
+ Subtotal + ${subtotal.toFixed(2)} +
+
+ Total + + ${total.toFixed(2)} + +
+

+ +
+ + ); +} + +function App() { + const maxHeight = useMaxHeight() ?? undefined; + const displayMode = useDisplayMode(); + const isFullscreen = displayMode === "fullscreen"; + const widgetProps = useWidgetProps(() => ({})); + const [widgetState, setWidgetState] = useWidgetState( + createDefaultWidgetState, + ); + const navigate = useNavigate(); + const location = useLocation(); + const isCheckoutRoute = useMemo(() => { + const pathname = location?.pathname ?? ""; + if (!pathname) { + return false; + } + + return pathname === "/checkout" || pathname.endsWith("/checkout"); + }, [location?.pathname]); + + const defaultCartItems = useMemo(() => createDefaultCartItems(), []); + const cartGridRef = useRef(null); + const [gridColumnCount, setGridColumnCount] = useState(1); + + const mergeWithDefaultItems = useCallback( + (items: CartItem[]): CartItem[] => { + const existingIds = new Set(items.map((item) => item.id)); + const merged = items.map((item) => { + const defaultItem = defaultCartItems.find( + (candidate) => candidate.id === item.id, + ); + + if (!defaultItem) { + return cloneCartItem(item); + } + + const enriched: CartItem = { + ...cloneCartItem(defaultItem), + ...item, + tags: item.tags ? [...item.tags] : defaultItem.tags, + nutritionFacts: + item.nutritionFacts ?? + defaultItem.nutritionFacts?.map((fact) => ({ ...fact })), + highlights: + item.highlights != null + ? [...item.highlights] + : defaultItem.highlights + ? [...defaultItem.highlights] + : undefined, + }; + + return cloneCartItem(enriched); + }); + + defaultCartItems.forEach((defaultItem) => { + if (!existingIds.has(defaultItem.id)) { + merged.push(cloneCartItem(defaultItem)); + } + }); + + return merged; + }, + [defaultCartItems], + ); + + const resolvedCartItems = useMemo(() => { + if (Array.isArray(widgetState?.cartItems) && widgetState.cartItems.length) { + return mergeWithDefaultItems(widgetState.cartItems); + } + + if ( + Array.isArray(widgetProps?.widgetState?.cartItems) && + widgetProps.widgetState.cartItems.length + ) { + return mergeWithDefaultItems(widgetProps.widgetState.cartItems); + } + + if (Array.isArray(widgetProps?.cartItems) && widgetProps.cartItems.length) { + return mergeWithDefaultItems(widgetProps.cartItems); + } + + return mergeWithDefaultItems(defaultCartItems); + }, [ + defaultCartItems, + mergeWithDefaultItems, + widgetProps?.cartItems, + widgetProps?.widgetState?.cartItems, + widgetState, + ]); + + const [cartItems, setCartItems] = useState(resolvedCartItems); + + useEffect(() => { + setCartItems((previous) => + cartItemsEqual(previous, resolvedCartItems) + ? previous + : resolvedCartItems, + ); + }, [resolvedCartItems]); + + const resolvedSelectedCartItemId = + widgetState?.selectedCartItemId ?? + widgetProps?.widgetState?.selectedCartItemId ?? + null; + + const [selectedCartItemId, setSelectedCartItemId] = useState( + resolvedSelectedCartItemId, + ); + + useEffect(() => { + setSelectedCartItemId((prev) => + prev === resolvedSelectedCartItemId ? prev : resolvedSelectedCartItemId, + ); + }, [resolvedSelectedCartItemId]); + + const view = useOpenAiGlobal("view"); + const viewParams = view?.params; + const isModalView = view?.mode === "modal"; + const checkoutFromState = + (widgetState?.state ?? widgetProps?.widgetState?.state) === "checkout"; + const modalParams = + viewParams && typeof viewParams === "object" + ? (viewParams as { + state?: unknown; + cartItems?: unknown; + subtotal?: unknown; + total?: unknown; + totalItems?: unknown; + }) + : null; + + const modalState = + modalParams && typeof modalParams.state === "string" + ? (modalParams.state as string) + : null; + + const isCartModalView = isModalView && modalState === "cart"; + const shouldShowCheckoutOnly = + isCheckoutRoute || (isModalView && !isCartModalView); + const wasModalViewRef = useRef(isModalView); + + useEffect(() => { + if (!viewParams || typeof viewParams !== "object") { + return; + } + + const paramsWithSelection = viewParams as { + selectedCartItemId?: unknown; + }; + + const selectedIdFromParams = paramsWithSelection.selectedCartItemId; + + if ( + typeof selectedIdFromParams === "string" && + selectedIdFromParams !== selectedCartItemId + ) { + setSelectedCartItemId(selectedIdFromParams); + return; + } + + if (selectedIdFromParams === null && selectedCartItemId !== null) { + setSelectedCartItemId(null); + } + }, [selectedCartItemId, viewParams]); + + const [hoveredCartItemId, setHoveredCartItemId] = useState( + null, + ); + const [activeFilters, setActiveFilters] = useState([]); + + const updateWidgetState = useCallback( + (partial: Partial) => { + setWidgetState((previous) => ({ + ...createDefaultWidgetState(), + ...(previous ?? {}), + ...partial, + })); + }, + [setWidgetState], + ); + + useEffect(() => { + if (!Array.isArray(widgetState?.cartItems)) { + return; + } + + const merged = mergeWithDefaultItems(widgetState.cartItems); + + if (!cartItemsEqual(widgetState.cartItems, merged)) { + updateWidgetState({ cartItems: merged }); + } + }, [mergeWithDefaultItems, updateWidgetState, widgetState?.cartItems]); + + useEffect(() => { + if (wasModalViewRef.current && !isModalView && checkoutFromState) { + updateWidgetState({ state: null }); + } + + wasModalViewRef.current = isModalView; + }, [checkoutFromState, isModalView, updateWidgetState]); + + const adjustQuantity = useCallback( + (id: string, delta: number) => { + setCartItems((previousItems) => { + const updatedItems = previousItems.map((item) => + item.id === id + ? { ...item, quantity: Math.max(0, item.quantity + delta) } + : item, + ); + + if (!cartItemsEqual(previousItems, updatedItems)) { + updateWidgetState({ cartItems: updatedItems }); + } + + return updatedItems; + }); + }, + [updateWidgetState], + ); + + useEffect(() => { + if (!shouldShowCheckoutOnly) { + return; + } + + setHoveredCartItemId(null); + }, [shouldShowCheckoutOnly]); + + const manualCheckoutTriggerRef = useRef(false); + + const requestModalWithAnchor = useCallback( + ({ + title, + params, + anchorElement, + }: { + title: string; + params: Record; + anchorElement?: HTMLElement | null; + }) => { + if (isModalView) { + return; + } + + const anchorRect = anchorElement?.getBoundingClientRect(); + const anchor = + anchorRect == null + ? undefined + : { + top: anchorRect.top, + left: anchorRect.left, + width: anchorRect.width, + height: anchorRect.height, + }; + + void (async () => { + try { + await window?.openai?.requestModal?.({ + title, + params, + ...(anchor ? { anchor } : {}), + }); + } catch (error) { + console.error("Failed to open checkout modal", error); + } + })(); + }, + [isModalView], + ); + + const openCheckoutModal = useCallback( + (anchorElement?: HTMLElement | null) => { + requestModalWithAnchor({ + title: "Checkout", + params: { state: "checkout" }, + anchorElement, + }); + }, + [requestModalWithAnchor], + ); + + const openCartItemModal = useCallback( + ({ + selectedId, + selectedName, + anchorElement, + }: { + selectedId: string; + selectedName: string | null; + anchorElement?: HTMLElement | null; + }) => { + requestModalWithAnchor({ + title: selectedName ?? selectedId, + params: { state: "checkout", selectedCartItemId: selectedId }, + anchorElement, + }); + }, + [requestModalWithAnchor], + ); + + const handleCartItemSelect = useCallback( + (id: string, anchorElement?: HTMLElement | null) => { + const itemName = cartItems.find((item) => item.id === id)?.name ?? null; + manualCheckoutTriggerRef.current = true; + setSelectedCartItemId(id); + updateWidgetState({ selectedCartItemId: id, state: "checkout" }); + openCartItemModal({ + selectedId: id, + selectedName: itemName, + anchorElement, + }); + }, + [cartItems, openCartItemModal, updateWidgetState], + ); + + const subtotal = useMemo( + () => + cartItems.reduce( + (total, item) => total + item.price * Math.max(0, item.quantity), + 0, + ), + [cartItems], + ); + + const total = subtotal + SERVICE_FEE + DELIVERY_FEE + TAX_FEE; + + const totalItems = useMemo( + () => + cartItems.reduce((total, item) => total + Math.max(0, item.quantity), 0), + [cartItems], + ); + + const visibleCartItems = useMemo(() => { + if (!activeFilters.length) { + return cartItems; + } + + return cartItems.filter((item) => { + const tags = item.tags ?? []; + + return activeFilters.every((filterId) => { + const filterMeta = FILTERS.find((filter) => filter.id === filterId); + if (!filterMeta?.tag) { + return true; + } + return tags.includes(filterMeta.tag); + }); + }); + }, [activeFilters, cartItems]); + + const updateItemColumnPlacement = useCallback(() => { + const gridNode = cartGridRef.current; + + const width = gridNode?.offsetWidth ?? 0; + + let baseColumnCount = 1; + if (width >= 768) { + baseColumnCount = 3; + } else if (width >= 640) { + baseColumnCount = 2; + } + + const columnCount = isFullscreen + ? Math.max(baseColumnCount, 3) + : baseColumnCount; + + if (gridNode) { + gridNode.style.gridTemplateColumns = `repeat(${columnCount}, minmax(0, 1fr))`; + } + + setGridColumnCount(columnCount); + }, [isFullscreen]); + + const handleFilterToggle = useCallback( + (id: string) => { + setActiveFilters((previous) => { + if (id === "all") { + return []; + } + + const isActive = previous.includes(id); + if (isActive) { + return []; + } + + return [id]; + }); + + requestAnimationFrame(() => { + updateItemColumnPlacement(); + }); + }, + [updateItemColumnPlacement], + ); + + useEffect(() => { + const node = cartGridRef.current; + + if (!node) { + return; + } + + const observer = + typeof ResizeObserver !== "undefined" + ? new ResizeObserver(() => { + requestAnimationFrame(updateItemColumnPlacement); + }) + : null; + + observer?.observe(node); + window.addEventListener("resize", updateItemColumnPlacement); + + return () => { + observer?.disconnect(); + window.removeEventListener("resize", updateItemColumnPlacement); + }; + }, [updateItemColumnPlacement]); + + const openCartModal = useCallback( + (anchorElement?: HTMLElement | null) => { + if (isModalView || shouldShowCheckoutOnly) { + return; + } + + requestModalWithAnchor({ + title: "Cart", + params: { + state: "cart", + cartItems, + subtotal, + total, + totalItems, + }, + anchorElement, + }); + }, + [ + cartItems, + isModalView, + requestModalWithAnchor, + shouldShowCheckoutOnly, + subtotal, + total, + totalItems, + ], + ); + + type CartSummaryItem = { + id: string; + name: string; + price: number; + quantity: number; + image?: string; + }; + + const cartSummaryItems: CartSummaryItem[] = useMemo(() => { + if (!isCartModalView) { + return []; + } + + const items = Array.isArray(modalParams?.cartItems) + ? modalParams?.cartItems + : null; + + if (!items) { + return cartItems.map((item) => ({ + id: item.id, + name: item.name, + price: item.price, + quantity: Math.max(0, item.quantity), + image: item.image, + })); + } + + const sanitized = items + .map((raw, index) => { + if (!raw || typeof raw !== "object") { + return null; + } + const candidate = raw as Record; + const id = + typeof candidate.id === "string" ? candidate.id : `cart-${index}`; + const name = + typeof candidate.name === "string" ? candidate.name : "Item"; + const priceValue = Number(candidate.price); + const quantityValue = Number(candidate.quantity); + const price = Number.isFinite(priceValue) ? priceValue : 0; + const quantity = Number.isFinite(quantityValue) + ? Math.max(0, quantityValue) + : 0; + const image = + typeof candidate.image === "string" ? candidate.image : undefined; + + return { + id, + name, + price, + quantity, + image, + } as CartSummaryItem; + }) + .filter(Boolean) as CartSummaryItem[]; + + if (sanitized.length === 0) { + return cartItems.map((item) => ({ + id: item.id, + name: item.name, + price: item.price, + quantity: Math.max(0, item.quantity), + image: item.image, + })); + } + + return sanitized; + }, [cartItems, isCartModalView, modalParams?.cartItems]); + + const cartSummarySubtotal = useMemo(() => { + if (!isCartModalView) { + return subtotal; + } + + const candidate = Number(modalParams?.subtotal); + return Number.isFinite(candidate) ? candidate : subtotal; + }, [isCartModalView, modalParams?.subtotal, subtotal]); + + const cartSummaryTotal = useMemo(() => { + if (!isCartModalView) { + return total; + } + + const candidate = Number(modalParams?.total); + return Number.isFinite(candidate) ? candidate : total; + }, [isCartModalView, modalParams?.total, total]); + + const cartSummaryTotalItems = useMemo(() => { + if (!isCartModalView) { + return totalItems; + } + + const candidate = Number(modalParams?.totalItems); + return Number.isFinite(candidate) ? candidate : totalItems; + }, [isCartModalView, modalParams?.totalItems, totalItems]); + + const handleContinueToPayment = useCallback( + (event?: ReactMouseEvent) => { + const anchorElement = event?.currentTarget ?? null; + + if (typeof window !== "undefined") { + const detail = { + subtotal: isCartModalView ? cartSummarySubtotal : subtotal, + total: isCartModalView ? cartSummaryTotal : total, + totalItems: isCartModalView ? cartSummaryTotalItems : totalItems, + }; + + try { + window.dispatchEvent( + new CustomEvent(CONTINUE_TO_PAYMENT_EVENT, { detail }), + ); + } catch (error) { + console.error("Failed to dispatch checkout navigation event", error); + } + } + + if (isCartModalView) { + return; + } + + manualCheckoutTriggerRef.current = true; + updateWidgetState({ state: "checkout" }); + const shouldNavigateToCheckout = isCartModalView || !isCheckoutRoute; + + if (shouldNavigateToCheckout) { + navigate("/checkout"); + return; + } + + openCheckoutModal(anchorElement); + }, + [ + cartSummarySubtotal, + cartSummaryTotal, + cartSummaryTotalItems, + isCartModalView, + isCheckoutRoute, + navigate, + openCheckoutModal, + subtotal, + total, + totalItems, + updateWidgetState, + ], + ); + + const handleSeeAll = useCallback(async () => { + if (typeof window === "undefined") { + return; + } + + try { + await window?.openai?.requestDisplayMode?.({ mode: "fullscreen" }); + } catch (error) { + console.error("Failed to request fullscreen display mode", error); + } + }, []); + + useLayoutEffect(() => { + const raf = requestAnimationFrame(updateItemColumnPlacement); + + return () => { + cancelAnimationFrame(raf); + }; + }, [updateItemColumnPlacement, visibleCartItems]); + + const selectedCartItem = useMemo(() => { + if (selectedCartItemId == null) { + return null; + } + return cartItems.find((item) => item.id === selectedCartItemId) ?? null; + }, [cartItems, selectedCartItemId]); + + const selectedCartItemName = selectedCartItem?.name ?? null; + const shouldShowSelectedCartItemPanel = + selectedCartItem != null && !isFullscreen; + + useEffect(() => { + if (isCheckoutRoute) { + return; + } + + if (!checkoutFromState) { + return; + } + + if (manualCheckoutTriggerRef.current) { + manualCheckoutTriggerRef.current = false; + return; + } + + if (selectedCartItemId) { + openCartItemModal({ + selectedId: selectedCartItemId, + selectedName: selectedCartItemName, + }); + return; + } + + openCheckoutModal(); + }, [ + isCheckoutRoute, + checkoutFromState, + openCartItemModal, + openCheckoutModal, + selectedCartItemId, + selectedCartItemName, + ]); + + const cartPanel = ( +
+ {!shouldShowCheckoutOnly && ( +
+ {!isFullscreen ? ( +
+ +
+ ) : ( +
Results
+ )} + +
+ )} + + +
+ + {visibleCartItems.map((item, index) => { + const isHovered = hoveredCartItemId === item.id; + const shortDescription = + item.shortDescription ?? item.description.split(".")[0]; + const columnCount = Math.max(gridColumnCount, 1); + const rowStartIndex = + Math.floor(index / columnCount) * columnCount; + const itemsRemaining = visibleCartItems.length - rowStartIndex; + const rowSize = Math.min(columnCount, itemsRemaining); + const positionInRow = index - rowStartIndex; + + const isSingle = rowSize === 1; + const isLeft = positionInRow === 0; + const isRight = positionInRow === rowSize - 1; + + return ( + + handleCartItemSelect( + item.id, + event.currentTarget as HTMLElement, + ) + } + onMouseEnter={() => setHoveredCartItemId(item.id)} + onMouseLeave={() => setHoveredCartItemId(null)} + className={clsx( + "group mb-4 flex cursor-pointer flex-col overflow-hidden border border-transparent bg-white transition-colors", + isHovered && "border-[#0f766e]", + )} + > +
+ {item.name} + +
+
+
+
+

+ {item.name} +

+

+ ${item.price.toFixed(2)} +

+
+ {shortDescription ? ( +

+ {shortDescription} +

+ ) : null} +
+
+ + + {item.quantity} + + +
+
+
+ + ); + })} + +
+ +
+ ); + + if (isCartModalView && !isCheckoutRoute) { + return ( +
+
+ {cartSummaryItems.length ? ( + cartSummaryItems.map((item) => ( +
+
+ {item.image ? ( + {item.name} + ) : null} +
+
+
+
+

+ {item.name} +

+

+ ${item.price.toFixed(2)} • Qty{" "} + {Math.max(0, item.quantity)} +

+
+ + ${(item.price * Math.max(0, item.quantity)).toFixed(2)} + +
+
+ )) + ) : ( +

+ Your cart is empty. +

+ )} +
+ +
+
+ Subtotal + ${cartSummarySubtotal.toFixed(2)} +
+
+ Total + ${cartSummaryTotal.toFixed(2)} +
+
+ +
+ ); + } + + const checkoutPanel = ( +
+ {shouldShowSelectedCartItemPanel ? ( + + ) : ( + + )} +
+ ); + + return ( +
+
+ {shouldShowCheckoutOnly ? ( + checkoutPanel + ) : isFullscreen ? ( +
+
{cartPanel}
+
{checkoutPanel}
+
+ ) : ( + cartPanel + )} + {!isFullscreen && !shouldShowCheckoutOnly && ( +
+ +
+ )} +
+
+ ); +} + +export default function PizzaShopWidget() { + return ( + + + + ); +} diff --git a/examples/pizzaz/widgets/shared/markers.json b/examples/pizzaz/widgets/shared/markers.json new file mode 100644 index 0000000..0eaeee1 --- /dev/null +++ b/examples/pizzaz/widgets/shared/markers.json @@ -0,0 +1,104 @@ +{ + "places": [ + { + "id": "nova-slice-lab", + "name": "Nova Slice Lab", + "coords": [-122.4098, 37.8001], + "description": "Award‑winning Neapolitan pies in North Beach.", + "city": "North Beach", + "rating": 4.8, + "price": "$$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png" + }, + { + "id": "midnight-marinara", + "name": "Midnight Marinara", + "coords": [-122.4093, 37.7990], + "description": "Focaccia‑style squares, late‑night favorite.", + "city": "North Beach", + "rating": 4.6, + "price": "$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + }, + { + "id": "cinder-oven-co", + "name": "Cinder Oven Co.", + "coords": [-122.4255, 37.7613], + "description": "Thin‑crust classics on 18th Street.", + "city": "Mission", + "rating": 4.5, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-3.png" + }, + { + "id": "neon-crust-works", + "name": "Neon Crust Works", + "coords": [-122.4388, 37.7775], + "description": "Deep‑dish and cornmeal crust favorites.", + "city": "Alamo Square", + "rating": 4.5, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-6.png" + }, + { + "id": "luna-pie-collective", + "name": "Luna Pie Collective", + "coords": [-122.4077, 37.7990], + "description": "Wood‑fired pies and burrata in North Beach.", + "city": "North Beach", + "rating": 4.6, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-4.png" + }, + { + "id": "bricklight-deep-dish", + "name": "Bricklight Deep Dish", + "coords": [-122.4097, 37.7992], + "description": "Chicago‑style pies from Tony Gemignani.", + "city": "North Beach", + "rating": 4.4, + "price": "$$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-5.png" + }, + { + "id": "garden-ember-pies", + "name": "Garden Ember Pies", + "coords": [-122.4380, 37.7722], + "description": "Neighborhood spot with seasonal toppings.", + "city": "Lower Haight", + "rating": 4.4, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-1.png" + }, + { + "id": "atlas-fire-pizzeria", + "name": "Atlas Fire Pizzeria", + "coords": [-122.4123, 37.7899], + "description": "Sourdough, wood‑fired pies near Nob Hill.", + "city": "Nob Hill", + "rating": 4.6, + "price": "$$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-2.png" + }, + { + "id": "circuit-slice-garage", + "name": "Circuit Slice Garage", + "coords": [-122.4135, 37.7805], + "description": "Crispy‑edged Detroit‑style in SoMa.", + "city": "SoMa", + "rating": 4.5, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-3.png" + }, + { + "id": "velvet-mozza-lounge", + "name": "Velvet Mozza Lounge", + "coords": [-122.4019, 37.7818], + "description": "Bianca pies and cocktails near Yerba Buena.", + "city": "Yerba Buena", + "rating": 4.3, + "price": "$$", + "thumbnail": "https://persistent.oaistatic.com/pizzaz/pizzaz-6.png" + } + ] +} diff --git a/examples/pizzaz/widgets/styles/index.css b/examples/pizzaz/widgets/styles/index.css new file mode 100644 index 0000000..b8e9127 --- /dev/null +++ b/examples/pizzaz/widgets/styles/index.css @@ -0,0 +1,38 @@ +@import "tailwindcss" source(".."); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer utilities { + .overflow-auto > *, + .overflow-scroll > *, + .overflow-x-auto > *, + .overflow-y-auto > * { + scrollbar-color: auto; + } + + .overflow-auto, + .overflow-scroll, + .overflow-x-auto, + .overflow-y-auto, + .overflow-x-scroll, + .overflow-y-scroll { + scrollbar-color: rgb(0, 0, 0, 0.1) transparent; + + @media (prefers-color-scheme: dark) { + scrollbar-color: rgb(255, 255, 255, 0.1) transparent; + } + } + + .overflow-auto:hover, + .overflow-scroll:hover, + .overflow-x-auto:hover, + .overflow-y-auto:hover { + scrollbar-color: rgb(0, 0, 0, 0.2) transparent; + + @media (prefers-color-scheme: dark) { + scrollbar-color: rgb(255, 255, 255, 0.2) transparent; + } + } +}