commit 6e33faa5fd9dd6e7f397c744800cd942572bfccf Author: Tom Butcher Date: Fri Sep 25 23:41:39 2026 +0100 Initialize project structure with client and server setup, including environment configuration, package management, and essential assets. Added .env.example, .gitignore, package.json, pnpm workspace configuration, and client-side files including HTML, CSS, and SVG assets. diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..40049b3 --- /dev/null +++ b/.env.example @@ -0,0 +1,45 @@ +# Server +PORT=3001 +NODE_ENV=development +MONGODB_URI=mongodb://127.0.0.1:27017/toms-allotment +SESSION_SECRET=replace-with-long-random-string + +# Browser origin (CORS + session). In dev with Vite, use the Vite dev server URL so cookies work through the proxy. +CLIENT_URL=http://localhost:5173 + +# OIDC (optional — omit to disable sign-in; watering stays disabled) +OIDC_ISSUER=https://your-idp.example.com/realms/your-realm +OIDC_CLIENT_ID=toms-allotment +OIDC_CLIENT_SECRET= +# In dev, use Vite proxy so the callback is same-site with the UI: +OIDC_REDIRECT_URI=http://localhost:5173/auth/callback + +# MQTT (optional — copy config/mqtt.example.json to config/mqtt.json) +MQTT_CONFIG_PATH= +MQTT_PASSWORD= + +# Single TTN device (optional — overrides packages/server/config.json device.*) +# DEMO_* still supported; DEVICE_TTN_* wins if both are set. +DEMO_TTN_APP_ID=demo-app +DEMO_TTN_DEV_ID=demo-device +# DEVICE_TTN_APPLICATION_ID= +# DEVICE_TTN_DEVICE_ID= +# DEVICE_NAME= + +# S3-compatible storage (patch images). Prefer env for secrets — do not commit keys. +# Cloudflare R2: endpoint https://.r2.cloudflarestorage.com (no bucket in path). +# Region is forced to "auto" for R2 when the endpoint host is r2.cloudflarestorage.com. +# MinIO: set S3_ENDPOINT (e.g. http://localhost:9000) and S3_FORCE_PATH_STYLE=true +S3_BUCKET= +S3_PREFIX=allotment-images +S3_ENDPOINT= +# R2 API token or MinIO access key (AWS_* also work if unset here) +S3_ACCESS_KEY_ID= +S3_SECRET_ACCESS_KEY= +# Optional (temporary creds / assume-role) +S3_SESSION_TOKEN= +S3_FORCE_PATH_STYLE= +AWS_REGION= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_SESSION_TOKEN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af3f68a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +.env +*.log +.DS_Store diff --git a/package.json b/package.json new file mode 100644 index 0000000..ff4415e --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "toms-allotment", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "concurrently \"pnpm --filter @toms-allotment/server dev\" \"pnpm --filter @toms-allotment/client dev\"", + "build": "pnpm --filter @toms-allotment/client build", + "start": "pnpm --filter @toms-allotment/client build && pnpm --filter @toms-allotment/server start" + }, + "devDependencies": { + "concurrently": "^8.2.2" + } +} diff --git a/packages/client/assets/lottie/no-flower-plant.json b/packages/client/assets/lottie/no-flower-plant.json new file mode 100644 index 0000000..f648614 --- /dev/null +++ b/packages/client/assets/lottie/no-flower-plant.json @@ -0,0 +1,10329 @@ +{ + "nm": "Main Scene", + "cl": "main scene", + "ddd": 0, + "h": 1080, + "w": 1080, + "meta": { + "g": "@lottiefiles/creator 1.84.0" + }, + "layers": [ + { + "ty": 4, + "nm": "Dust-Foreground", + "cl": "dirt-foreground", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [50.385, 53.564, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [500, 500, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [539.425, 797.822], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-2.58, -1.497], + [0, 0], + [3.141, 0.719], + [0, 0] + ], + "o": [ + [0, 0], + [2.613, 1.497], + [0, 0], + [-3.109, -0.715], + [0, 0] + ], + "v": [ + [-5.638, -0.391], + [2.629, -0.781], + [5.638, 2.278], + [-0.876, 0.29], + [-5.638, -0.393] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [66.558, 97.134], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-3.607, -0.913], + [0, 0], + [2.382, 0.13], + [0, 0] + ], + "o": [ + [0, 0], + [3.57, 0.911], + [0, 0], + [-2.38, -0.094], + [0, 0] + ], + "v": [ + [-6.697, 1.219], + [0.712, -2], + [6.697, 2.913], + [-2.893, 1.09], + [-6.695, 1.22] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [80.182, 91.195], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 3, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-2.418, -0.781], + [0, 0], + [1.59, 0.617] + ], + "o": [ + [0, 0], + [2.413, 0.813], + [0, 0], + [-1.554, -0.618] + ], + "v": [ + [-4.877, -0.846], + [0.909, -2.083], + [4.877, 2.864], + [-1.042, 0.228] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [52.67, 84.021], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 4", + "cl": "group 4", + "ix": 4, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-2.578, 1.172], + [-0.727, -1.073], + [2.217, -0.879] + ], + "o": [ + [0, 0], + [2.579, -1.203], + [0.694, 1.075], + [-2.217, 0.879] + ], + "v": [ + [-6.596, 3.091], + [-1.902, -1.888], + [5.902, 0.682], + [-2.1, 1.366] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [46.882, 92.383], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 5", + "cl": "group 5", + "ix": 5, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-5.325, -3.482], + [0, 0], + [2.777, -0.356], + [0, 0] + ], + "o": [ + [3.34, 2.178], + [0, 0], + [-2.744, 0.327], + [1.653, -3.904] + ], + "v": [ + [2.547, -0.879], + [6.382, 4.361], + [-1.488, 1.105], + [-6.382, 2.733] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [31.788, 91.96], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 6", + "cl": "group 6", + "ix": 6, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-1.851, -1.464], + [0, 0], + [4.43, 0.425], + [0, 0] + ], + "o": [ + [0, 0], + [1.819, 1.464], + [0, 0], + [-4.431, -0.424], + [0, 0] + ], + "v": [ + [-1.852, -2.961], + [4.629, -1.497], + [6.877, 2.961], + [-1.819, -0.229], + [-6.877, -0.424] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [17.172, 89.647], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 7", + "cl": "group 7", + "ix": 7, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-1.622, -0.065], + [-0.927, -0.412], + [-2.347, 0.975], + [0, 0], + [-0.995, 1.171], + [0, 0], + [-0.923, 1.142], + [-0.331, -0.165], + [-0.894, 0], + [0, 0], + [0, 0], + [1.019, -0.321], + [0, 0], + [1.458, 0], + [0, 0], + [-0.858, -0.781], + [0, 0], + [0.86, -0.063], + [0, 0], + [-1.059, -0.879], + [-0.529, -0.814], + [1.322, -0.619], + [0.761, -0.879], + [0.349, -0.087], + [0, 0], + [1.721, 0.944], + [0, 0] + ], + "o": [ + [0, 0], + [1.016, 0.04], + [0, 0], + [2.315, -0.947], + [0, 0], + [1.025, -1.136], + [0, 0], + [0.962, -1.172], + [0.362, 0.163], + [0.894, 0], + [0, 0], + [-1.051, 0.2], + [-1.787, 0.521], + [0, 0], + [-1.421, 0], + [0, 0], + [0.827, 0.748], + [0, 0], + [-0.859, 0.062], + [0, 0], + [0.694, 0.585], + [-1.554, -0.685], + [-0.993, 0.455], + [-0.357, 0.043], + [-1.62, 0.421], + [0, 0], + [-1.72, -0.975], + [0, 0] + ], + "v": [ + [-17.033, 6.279], + [-13.592, 3.482], + [-10.65, 4.165], + [-8.336, -0.096], + [-4.896, -0.617], + [-3.805, -4.391], + [-1.292, -5.368], + [1.915, -8.007], + [5.422, -9.829], + [8.596, -12.107], + [10.617, -10.87], + [17.033, -5.958], + [13.924, -5.176], + [11.51, -3.387], + [7.372, -5.078], + [5.16, -3.516], + [9.889, -1.922], + [11.773, 0.876], + [4.003, 2.012], + [1.452, 4.396], + [5.62, 6.122], + [7.702, 8.758], + [2.975, 8.27], + [0.262, 10.482], + [-0.797, 10.678], + [-3.876, 12.107], + [-7.381, 8.495], + [-14.258, 8.887] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5059, 0.2078, 0.1137], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [40.487, 78.976], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 1 + }, + { + "ty": 4, + "nm": "Dust-Body-Bit5", + "cl": "dirt-body-bit5", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [-5.551, 316.252, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [535.385], + "t": 0 + }, + { + "s": [551.635], + "t": 5 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.124, + "y": -1.177 + }, + "i": { + "x": 0.833, + "y": 1 + }, + "s": [869.252], + "t": 0 + }, + { + "s": [874.002], + "t": 5 + } + ], + "ix": 4 + } + }, + "r": { + "a": 0, + "k": 200, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Ellipse 1", + "cl": "ellipse 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.046, 0], + [0, -11.046], + [11.046, 0], + [3.238, 10.56] + ], + "o": [ + [11.046, 0], + [0, 11.046], + [-11.046, 0], + [-4.035, -13.159] + ], + "v": [ + [-1.812, -14.004], + [20, 0], + [0, 10.5], + [-20, 0] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [-5, 321], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 2 + }, + { + "ty": 4, + "nm": "Dust-Body-Bit4", + "cl": "dirt-body-bit4", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [-5.551, 316.252, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [539.135], + "t": 0 + }, + { + "s": [525.385], + "t": 5 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.124, + "y": 0.276 + }, + "i": { + "x": 0.833, + "y": 1 + }, + "s": [893.002], + "t": 0 + }, + { + "s": [872.752], + "t": 5 + } + ], + "ix": 4 + } + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Ellipse 1", + "cl": "ellipse 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.046, 0], + [0, -11.046], + [11.046, 0], + [-5.172, 9.76] + ], + "o": [ + [11.046, 0], + [0, 11.046], + [-11.046, 0], + [5.565, -10.5] + ], + "v": [ + [0, -20], + [20, 0], + [0, 10.5], + [-20, 0] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [-5, 321], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 3 + }, + { + "ty": 4, + "nm": "Flower2-Body", + "cl": "flower2-body", + "sr": 1, + "st": -10, + "op": 100, + "ip": 50, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [149.483, 139.344, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [0, 0, 100], + "t": 50 + }, + { + "s": [200, 200, 100], + "t": 90 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [491.422, 328.829], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-0.738, -0.005], + [-1.108, -1.064], + [-0.087, -1.535], + [0.252, -0.688], + [0.505, -0.53], + [0.674, -0.284], + [0.732, 0.009], + [1.106, 1.047], + [0.105, 1.521], + [-0.25, 0.697], + [-0.506, 0.538], + [-0.678, 0.291] + ], + "o": [ + [1.535, 0.023], + [1.107, 1.064], + [0.042, 0.731], + [-0.253, 0.688], + [-0.506, 0.53], + [-0.675, 0.284], + [-1.521, -0.018], + [-1.107, -1.046], + [-0.05, -0.738], + [0.249, -0.696], + [0.506, -0.538], + [0.679, -0.29] + ], + "v": [ + [-0.287, -5.692], + [3.824, -4.001], + [5.682, 0.043], + [5.363, 2.195], + [4.213, 4.041], + [2.424, 5.275], + [0.292, 5.692], + [-3.795, 4.036], + [-5.679, 0.043], + [-5.376, -2.132], + [-4.231, -4.003], + [-2.435, -5.26] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 0.9098, 0.0941], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [142.198, 128.222], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.495, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [0, 0], + [0.713, 2.729], + [-2.322, 1.111], + [0, 0], + [-3.056, -1.439], + [0, 0], + [3.309, -1.519], + [0, 0], + [0.528, -1.481], + [3.252, 0.425], + [0, 0], + [2.109, 2.09], + [0.313, 2.167], + [0, 0] + ], + "o": [ + [0, 0], + [-0.44, -2.169], + [2.288, -0.906], + [0, 0], + [3.572, 2.307], + [4.279, 0.019], + [-3.03, 1.795], + [0, 0], + [-1.201, 1.545], + [-3.217, -0.421], + [0, 0], + [-1.252, -1.241], + [-0.696, -2.299], + [0, 0] + ], + "v": [ + [-14.046, -2.526], + [-16.965, -6.567], + [-13.498, -12.008], + [-8.656, -11.838], + [-1.526, -11.331], + [4.107, -7.603], + [7.381, 5.318], + [3.113, 7.1], + [2.239, 10.466], + [-4.232, 12.882], + [-9.661, 10.757], + [-13.75, 6.896], + [-16.709, 1.487], + [-14.057, -2.536] + ] + } + ], + "t": 50 + }, + { + "s": [ + { + "c": true, + "i": [ + [0, 0], + [2.157, 7.554], + [-8.293, -2.659], + [0, 0], + [-12.107, -3.575], + [0, 0], + [17.246, -7.392], + [0, 0], + [-1.486, -5.649], + [6.806, -1.078], + [0, 0], + [5.723, -1.328], + [1.988, 4.902], + [0, 0] + ], + "o": [ + [0, 0], + [-2.239, -7.48], + [8.292, 2.577], + [0, 0], + [12.105, 3.656], + [12.44, -13.296], + [-6.551, 2.901], + [0, 0], + [0.915, 3.656], + [-17.329, 2.744], + [0, 0], + [-3.979, 0.913], + [-4.893, -11.8], + [0, 0] + ], + "v": [ + [-14.046, -2.526], + [-30.796, -10.745], + [-20.846, -22.958], + [-8.656, -11.838], + [1.212, -26.459], + [4.107, -7.603], + [18.62, 7.767], + [3.113, 7.1], + [11.316, 17.238], + [4.268, 27.29], + [-9.661, 10.757], + [-19.945, 18.566], + [-30.973, 12.005], + [-14.057, -2.536] + ] + } + ], + "t": 90 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 1, 1], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [1, 10], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [148.251, 137.797], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 4 + }, + { + "ty": 4, + "nm": "Flower3-Body", + "cl": "flower3-body", + "sr": 1, + "st": 0, + "op": 100, + "ip": 50, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [202.237, 109.947, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [0, 0, 100], + "t": 50 + }, + { + "s": [200, 200, 100], + "t": 80 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [597.474, 270.893], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-0.735, 0.001], + [-0.082, -3.157], + [0.25, -0.691], + [0.507, -0.532], + [0.677, -0.282], + [0.733, 0.015], + [0.164, 3.157], + [-0.251, 0.693], + [-0.505, 0.536], + [-0.675, 0.292] + ], + "o": [ + [3.151, 0], + [0.048, 0.733], + [-0.25, 0.69], + [-0.506, 0.531], + [-0.678, 0.281], + [-3.067, 0], + [-0.043, -0.735], + [0.252, -0.693], + [0.504, -0.537], + [0.675, -0.293] + ], + "v": [ + [-0.293, -5.69], + [5.677, 0.039], + [5.371, 2.199], + [4.223, 4.052], + [2.428, 5.285], + [0.289, 5.689], + [-5.68, 0.039], + [-5.365, -2.126], + [-4.219, -3.989], + [-2.431, -5.245] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 0.9098, 0.0941], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [202.238, 110.026], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.675, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [0, 0], + [0.431, 2.339], + [-2.7, 0.476], + [0, 0], + [-5.96, -1.996], + [0, 0], + [3.247, -1.593], + [0, 0], + [0.827, -2.339], + [2.28, -0.072], + [0, 0], + [3.162, 0.45], + [-0.696, 3.068], + [0, 0] + ], + "o": [ + [0, 0], + [-0.298, -1.854], + [2.793, -0.493], + [0, 0], + [4.43, 1.285], + [5.362, 0.737], + [-2.221, 1.324], + [0, 0], + [-0.758, 2.144], + [-4.175, 0.131], + [0, 0], + [-2.489, -0.644], + [0.702, -3.095], + [0, 0] + ], + "v": [ + [-14.089, -2.524], + [-18.081, -7.199], + [-13.517, -12.57], + [-8.619, -11.84], + [-1.119, -13.072], + [4.147, -7.602], + [9.178, 5.482], + [3.068, 7.188], + [3.158, 12.135], + [-3.343, 15.444], + [-9.62, 10.761], + [-14.979, 10.366], + [-19.446, 2.973], + [-14.096, -2.536] + ] + } + ], + "t": 50 + }, + { + "s": [ + { + "c": true, + "i": [ + [0, 0], + [2.239, 7.474], + [-8.291, -2.574], + [0, 0], + [-12.107, -3.655], + [0, 0], + [17.164, -7.477], + [0, 0], + [-1.494, -5.649], + [6.717, -1.08], + [0, 0], + [5.638, -1.33], + [1.991, 4.82], + [0, 0] + ], + "o": [ + [0, 0], + [-2.154, -7.56], + [8.292, 2.659], + [0, 0], + [12.105, 3.573], + [12.437, -13.209], + [-6.551, 2.824], + [0, 0], + [0.912, 3.655], + [-17.333, 2.741], + [0, 0], + [-3.981, 0.998], + [-4.891, -11.797], + [0, 0] + ], + "v": [ + [-14.089, -2.524], + [-30.841, -10.662], + [-20.808, -22.96], + [-8.619, -11.84], + [1.251, -26.378], + [4.147, -7.602], + [18.657, 7.851], + [3.068, 7.188], + [11.361, 17.239], + [4.313, 27.292], + [-9.62, 10.761], + [-19.901, 18.568], + [-30.93, 12.088], + [-14.096, -2.536] + ] + } + ], + "t": 80 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 1, 1], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [207.329, 109.599], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 5 + }, + { + "ty": 4, + "nm": "Flower1-Body", + "cl": "flower1-body", + "sr": 1, + "st": -5, + "op": 101, + "ip": 55, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.836, 231.79, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [0, 0, 100], + "t": 55 + }, + { + "s": [200, 200, 100], + "t": 94 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [554.672, 514.579], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-0.736, 0.001], + [-0.084, -3.157], + [0.251, -0.69], + [0.506, -0.531], + [0.677, -0.282], + [0.733, 0.015], + [0.166, 3.157], + [-0.251, 0.693], + [-0.504, 0.536], + [-0.676, 0.293] + ], + "o": [ + [3.151, 0], + [0.048, 0.733], + [-0.251, 0.691], + [-0.507, 0.531], + [-0.677, 0.282], + [-3.069, 0], + [-0.043, -0.736], + [0.252, -0.693], + [0.505, -0.537], + [0.675, -0.293] + ], + "v": [ + [-0.292, -5.691], + [5.679, 0.043], + [5.372, 2.201], + [4.224, 4.054], + [2.428, 5.286], + [0.289, 5.69], + [-5.682, 0.043], + [-5.366, -2.123], + [-4.22, -3.987], + [-2.431, -5.245] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 0.9098, 0.0941], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [192.121, 219.357], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.512, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [0, 0], + [0.322, 2.238], + [-1.278, 0.318], + [0, 0], + [-3.695, -0.853], + [0, 0], + [2.873, -1.47], + [0, 0], + [0.169, -1.461], + [3.725, 0.535], + [0, 0], + [2.272, 0.706], + [-0.071, 2.127], + [0, 0] + ], + "o": [ + [0, 0], + [-0.437, -3.041], + [2.216, -0.646], + [0, 0], + [3.414, 1.074], + [5.096, 0.609], + [-2.187, 1.061], + [0, 0], + [-0.313, 2.395], + [-4.707, -0.676], + [0, 0], + [-2.307, -1.101], + [0.123, -3.696], + [0, 0] + ], + "v": [ + [-14.091, -2.576], + [-15.178, -6.621], + [-12.493, -12.171], + [-8.617, -11.881], + [-1.884, -11.964], + [4.145, -7.643], + [7.211, 5.882], + [3.068, 7.144], + [1.601, 11.174], + [-4.364, 13.997], + [-9.618, 10.718], + [-13.031, 8.525], + [-16.231, 3.49], + [-14.099, -2.576] + ] + } + ], + "t": 55 + }, + { + "s": [ + { + "c": true, + "i": [ + [0, 0], + [2.241, 7.477], + [-8.29, -2.575], + [0, 0], + [-12.104, -3.655], + [0, 0], + [17.167, -7.475], + [0, 0], + [-1.493, -5.652], + [6.717, -1.078], + [0, 0], + [5.636, -1.327], + [1.99, 4.821], + [0, 0] + ], + "o": [ + [0, 0], + [-2.157, -7.559], + [8.293, 2.659], + [0, 0], + [12.108, 3.573], + [12.44, -13.211], + [-6.55, 2.826], + [0, 0], + [0.912, 3.655], + [-17.328, 2.826], + [0, 0], + [-3.984, 0.999], + [-4.894, -11.794], + [0, 0] + ], + "v": [ + [-14.091, -2.576], + [-30.84, -10.718], + [-20.807, -23.015], + [-8.617, -11.881], + [1.249, -26.421], + [4.145, -7.643], + [18.657, 7.809], + [3.068, 7.144], + [11.361, 17.199], + [4.311, 27.251], + [-9.618, 10.718], + [-19.898, 18.525], + [-30.93, 12.045], + [-14.098, -2.576] + ] + } + ], + "t": 94 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [1, 1, 1], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [197.213, 218.988], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 6 + }, + { + "ty": 3, + "nm": "Null 21", + "cl": "null 21", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [0, 0, 100], + "t": 5 + }, + { + "s": [100, 100, 100], + "t": 50 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.012, + "y": 0.14 + }, + "i": { + "x": 0.196, + "y": 1 + }, + "s": [508.5], + "t": 5 + }, + { + "s": [477.5], + "t": 30 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.012, + "y": 0.16 + }, + "i": { + "x": 0.196, + "y": 1 + }, + "s": [762.5], + "t": 5 + }, + { + "s": [660], + "t": 30 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.02, + "y": -0.019 + }, + "i": { + "x": 0.342, + "y": 1 + }, + "s": [60], + "t": 5 + }, + { + "s": [0], + "t": 50 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 7 + }, + { + "ty": 4, + "nm": "Leaf2-Body-Lines", + "cl": "leaf leaf2-body-lines", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [76.172, -100.921, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [0.994, -1.494], + [-24.381, -8.394], + [13.02, 0.58], + [-6.956, 3.488], + [-4.642, -2.739], + [14.587, 0.747], + [-1.425, -0.225], + [0, 0], + [-6.884, 3.491], + [-1.324, -0.996], + [-1.906, -1.412], + [13.928, 0.665], + [-6.635, 3.407], + [0, 0], + [13.847, 4.651] + ], + "o": [ + [-25.042, -8.558], + [-0.497, 0.829], + [-11.858, 5.56], + [19.57, 4.987], + [6.22, 2.405], + [-13.102, 6.894], + [1.391, 0.384], + [0, 0], + [20.482, 5.233], + [2.654, 1.745], + [2.157, 1.663], + [-12.604, 6.481], + [20.067, 5.152], + [9.453, 7.477], + [-2.57, -16.196], + [0, 0] + ], + "v": [ + [2.243, -14.831], + [-55.803, -25.88], + [-3.396, -11.091], + [-40.381, -3.948], + [2.566, -8.93], + [18.902, -0.872], + [-22.227, 8.016], + [-17.999, 8.93], + [-18.331, 8.93], + [26.034, 3.696], + [32.003, 8.016], + [38.141, 12.669], + [-1.33, 21.06], + [42.122, 15.826], + [56.299, 27.374], + [2.236, -14.831] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [79.716, 274.778], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 8, + "parent": 7 + }, + { + "ty": 4, + "nm": "Leaf2-Body-Reflection", + "cl": "leaf leaf2-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [76.172, -100.921, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [9.453, 8.649], + [-3.233, -0.334], + [-9.7, -11.797], + [0, 0] + ], + "o": [ + [-1.163, -0.834], + [-9.371, -8.64], + [3.151, 0.241], + [9.699, 11.797], + [0, 0] + ], + "v": [ + [10.7, 10.803], + [-20.892, -8.724], + [-31.426, -20.354], + [24.96, 2.826], + [34.497, 20.688] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [99.452, 267.257], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 9, + "parent": 7 + }, + { + "ty": 3, + "nm": "Null 27", + "cl": "null 27", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [0, 0, 100], + "t": 5 + }, + { + "s": [100, 100, 100], + "t": 30 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [538.5, 865], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 10 + }, + { + "ty": 4, + "nm": "Bottom-Stem-Reflection", + "cl": "bottom-stem-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [15.172, -305.921, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [0.071, -0.263], + [0.172, -0.211], + [0.243, -0.124], + [0.272, -0.014], + [0.456, 0.38], + [0.103, 0.586], + [2.984, 11.963], + [0, 0.094], + [-0.032, 0.274], + [-0.141, 0.236], + [-0.226, 0.157], + [-0.27, 0.05], + [-0.488, -0.337], + [-0.146, -0.575], + [-3.151, -12.711], + [0, -0.083] + ], + "o": [ + [0.041, 0.269], + [-0.072, 0.263], + [-0.172, 0.212], + [-0.243, 0.123], + [-0.594, 0.005], + [-0.456, -0.381], + [0, -0.082], + [-2.821, -11.546], + [-0.084, -0.262], + [0.031, -0.273], + [0.141, -0.236], + [0.226, -0.156], + [0.589, -0.068], + [0.488, 0.336], + [0, 0], + [2.987, 12.215], + [0, 0] + ], + "v": [ + [7.457, 26.202], + [7.411, 27.01], + [7.043, 27.73], + [6.413, 28.239], + [5.633, 28.447], + [4.008, 27.865], + [3.143, 26.368], + [-2.576, -6.699], + [-7.386, -25.891], + [-7.465, -26.704], + [-7.203, -27.476], + [-6.646, -28.072], + [-5.893, -28.384], + [-4.223, -27.967], + [-3.24, -26.554], + [1.654, -7.364], + [7.459, 26.202] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [164.721, 371.494], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 11, + "parent": 10 + }, + { + "ty": 4, + "nm": "Leaf2-Body-Background", + "cl": "leaf leaf2-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [76.172, -100.921, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0.525, 3.82], + [4.003, 10.672], + [12.853, 2.577], + [17.664, 0.996], + [-33.75, -20.27], + [-20.064, 0.334], + [-7.023, -4.153] + ], + "o": [ + [0.709, -5.939], + [-8.374, -22.183], + [-2.57, -0.498], + [10.035, 14.207], + [14.259, 8.553], + [13.233, -0.244], + [3.965, -1.453] + ], + "v": [ + [50.99, -21.419], + [47.348, -46.524], + [-6.303, -85.736], + [-90.8, -94.543], + [-41.874, -22.678], + [15.836, -14.46], + [47.799, -11.896] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [93.236, 321.099], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 12, + "parent": 7 + }, + { + "ty": 4, + "nm": "Bottom-Stem", + "cl": "bottom-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.024, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [0.294, 2.574], + [1.025, 1.32], + [-0.535, -1.703], + [-0.13, -2.595], + [0, 0] + ], + "o": [ + [-1.081, -1.051], + [-1.975, -1.93], + [1.465, 2.297], + [0, 0], + [0.373, 2.155] + ], + "v": [ + [83.721, 85.577], + [79.99, 82.331], + [74.549, 80.354], + [75.395, 85.746], + [75.391, 85.746] + ] + } + ], + "t": 0 + }, + { + "s": [ + { + "c": true, + "i": [ + [0.83, 11.302], + [17.357, 18.257], + [-7.023, -4.153], + [-1.469, -25.168], + [0, 0] + ], + "o": [ + [-2.025, -28.331], + [-2.725, -3.93], + [12.59, 19.28], + [0, 0], + [0.669, 11.797] + ], + "v": [ + [89.971, 83.077], + [50.99, -21.419], + [47.799, -11.896], + [71.395, 82.746], + [71.391, 82.746] + ] + } + ], + "t": 30 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [93.236, 321.099], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 13 + }, + { + "ty": 3, + "nm": "Null 22", + "cl": "null 22", + "sr": 1, + "st": 0, + "op": 100, + "ip": 15, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.336, + "y": 1 + }, + "s": [0, 0, 100], + "t": 15 + }, + { + "s": [100, 100, 100], + "t": 65 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.133 + }, + "i": { + "x": 0.503, + "y": 1 + }, + "s": [516.5], + "t": 15 + }, + { + "s": [551], + "t": 45 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.153 + }, + "i": { + "x": 0.503, + "y": 1 + }, + "s": [770.25], + "t": 15 + }, + { + "s": [655.5], + "t": 45 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.336, + "y": 1 + }, + "s": [-60], + "t": 15 + }, + { + "s": [0], + "t": 60 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 14 + }, + { + "ty": 4, + "nm": "Leaf1-Body-Reflection", + "cl": "leaf leaf1-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 15, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [2.672, -96.421, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-3.158, -12.297] + ], + "o": [ + [11.859, -23.266], + [1.325, 5.149] + ], + "v": [ + [-27.696, 13.876], + [26.371, -1.579] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [257.171, 230.288], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 15, + "parent": 14 + }, + { + "ty": 4, + "nm": "Leaf1-Body-Lines", + "cl": "leaf leaf1-body-lines", + "sr": 1, + "st": 0, + "op": 100, + "ip": 15, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [2.672, -96.421, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-18.574, 0.914], + [7.047, 3.658] + ], + "o": [ + [17.167, 9.137], + [-27.445, 7.146], + [0, 0] + ], + "v": [ + [-24.337, -9.513], + [28.897, 2.367], + [-28.898, -5.196] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [281.427, 258.328], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-17.415, 5.983], + [-1.243, -1.912], + [31.426, -11.545], + [5.475, -4.318], + [0, 0] + ], + "o": [ + [31.508, -10.878], + [0.832, 1.163], + [-19.821, 7.313], + [-19.985, 15.785], + [2.239, -20.689] + ], + "v": [ + [-3.937, -18.944], + [69.529, -33.067], + [-0.786, -12.63], + [-40.507, 10.302], + [-70.36, 34.979] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [259.029, 265.762], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 3, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-19.073, 0.998], + [1.742, -0.331], + [0, 0], + [7.214, 3.822], + [0, 0], + [-0.838, -0.416] + ], + "o": [ + [17.661, 9.304], + [-1.824, 0.5], + [0, 0], + [-28.027, 7.313], + [0, 0], + [0.745, 0.416], + [0, 0] + ], + "v": [ + [-22.512, -10.302], + [32.05, 1.826], + [26.742, 2.988], + [27.075, 2.988], + [-32.05, -4.82], + [-27.405, -9.142], + [-24.992, -7.892] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [255.804, 274.25], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 4", + "cl": "group 4", + "ix": 4, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-18.663, 0.916], + [7.048, 3.737] + ], + "o": [ + [17.244, 9.138], + [-27.448, 7.064], + [0, 0] + ], + "v": [ + [-24.382, -9.514], + [28.94, 2.45], + [-28.94, -5.191] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [233.047, 290.313], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 16, + "parent": 14 + }, + { + "ty": 4, + "nm": "Leaf1-Body-Background", + "cl": "leaf leaf1-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 15, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [2.672, -96.421, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [34.328, 2.901], + [13.029, -39.213], + [3.648, -9.389], + [0, 0], + [-2.461, -0.017], + [-18.979, -2.333], + [-29.353, 18.942], + [-14.261, 15.368], + [0.164, 0.665] + ], + "o": [ + [-45.36, -3.899], + [-3.815, 11.799], + [0, 0], + [-2.118, 6.125], + [5.578, 2.426], + [9.535, 1.075], + [20.233, -13.042], + [10.45, -11.216], + [-1.576, -6.229] + ], + "v": [ + [29.356, -65.855], + [-75.956, -16.087], + [-79.274, 18.142], + [-79.386, 18.928], + [-77.043, 29.071], + [-40.3, 38.249], + [24.793, 23.128], + [68.326, -21.07], + [97.931, -44.004] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [257.504, 277.447], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 17, + "parent": 14 + }, + { + "ty": 4, + "nm": "Leaf1-Stem", + "cl": "leaf1-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 15, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [1.02, -0.339], + [0, 0], + [0.121, -2.285], + [-1.409, -2.187], + [0, 0], + [-0.113, 1.183], + [-0.086, 1.733] + ], + "o": [ + [0, 0], + [-0.493, 1.5], + [-0.129, 3.215], + [0, 0], + [-0.099, -3.201], + [0.238, -2.484], + [0.414, -2.267] + ], + "v": [ + [-92.149, 66.892], + [-93.386, 68.803], + [-95.125, 74.839], + [-93.845, 81.241], + [-91.155, 86.504], + [-90.891, 80.621], + [-90.293, 75.196] + ] + } + ], + "t": 15 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [4.52, -4.339], + [0, 0], + [2.936, -7.072], + [0, -10.221], + [0, 0], + [-3.732, 9.055], + [-3.016, 3.271] + ], + "o": [ + [0, 0], + [-1.493, 1.625], + [-4.147, 10.216], + [0, 0], + [0, -9.317], + [1.826, -4.383], + [4.789, -3.517] + ], + "v": [ + [-80.024, 27.267], + [-82.511, 28.428], + [-91.75, 41.714], + [-98.095, 68.991], + [-90.03, 75.629], + [-84.391, 43.996], + [-76.793, 30.446] + ] + } + ], + "t": 35 + }, + { + "s": [ + { + "c": true, + "i": [ + [4.52, -4.339], + [0, 0], + [2.936, -7.072], + [0, -10.221], + [0, 0], + [-3.732, 9.055], + [-3.016, 3.271] + ], + "o": [ + [0, 0], + [-5.818, 4.542], + [-4.147, 10.216], + [0, 0], + [0, -9.317], + [1.826, -4.383], + [4.789, -3.517] + ], + "v": [ + [-74.774, 17.642], + [-74.886, 18.428], + [-91.125, 39.089], + [-98.095, 68.991], + [-89.405, 69.754], + [-83.016, 42.996], + [-72.543, 28.571] + ] + } + ], + "t": 45 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [257.504, 277.447], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 18 + }, + { + "ty": 3, + "nm": "Null 23", + "cl": "null 23", + "sr": 1, + "st": 0, + "op": 100, + "ip": 10, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.255, + "y": 1 + }, + "s": [0, 0, 100], + "t": 30 + }, + { + "s": [100, 100, 100], + "t": 70 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.167 + }, + "i": { + "x": 0.336, + "y": 1 + }, + "s": [521.5], + "t": 30 + }, + { + "s": [526.5], + "t": 60 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.167 + }, + "i": { + "x": 0.336, + "y": 1 + }, + "s": [376.5], + "t": 30 + }, + { + "s": [251.5], + "t": 60 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.667, + "y": 1 + }, + "s": [25], + "t": 30 + }, + { + "s": [0], + "t": 70 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 19 + }, + { + "ty": 4, + "nm": "Leaf6-Body-Shadow", + "cl": "leaf leaf6-body-shadow", + "sr": 1, + "st": 0, + "op": 100, + "ip": 10, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [27.172, 307.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-9.371, 1.745], + [4.642, 13.129], + [-4.399, -16.869], + [-7.132, -7.893] + ], + "o": [ + [0, 0], + [-4.56, -13.127], + [2.072, 8.142], + [5.79, 6.314] + ], + "v": [ + [25.417, 34.645], + [-12.314, -23.264], + [-21.019, -9.637], + [-9.411, 15.87] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5098, 0.7059, 0.1843], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [134.489, 59.719], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 20, + "parent": 19 + }, + { + "ty": 4, + "nm": "Leaf6-Body-Reflection", + "cl": "leaf leaf6-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 10, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [27.172, 307.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-0.166, 5.069], + [-7.381, -17.115] + ], + "o": [ + [0, 0], + [0.242, -5.077], + [7.38, 17.031] + ], + "v": [ + [9.952, 26.258], + [-16.169, -21.181], + [8.956, 3.991] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [144.316, 49.082], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 21, + "parent": 19 + }, + { + "ty": 4, + "nm": "Leaf6-Body-Background", + "cl": "leaf leaf6-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 10, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [27.172, 307.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-0.064, 2.388], + [13.27, 20.616], + [3.568, -1.91], + [-5.066, -19.025], + [-6.799, -2.826], + [-6.527, -1.664] + ], + "o": [ + [0.987, -9.123], + [-16.416, -25.504], + [-3.564, 1.994], + [5.142, 19.11], + [4.956, 2.12], + [2.143, 0.995] + ], + "v": [ + [27.526, -72.563], + [16.548, -128.072], + [-25.413, -158.894], + [-31.549, -113.781], + [-1.117, -77.307], + [23.069, -70.17] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [140.538, 170.676], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 22, + "parent": 19 + }, + { + "ty": 4, + "nm": "Main-Stem", + "cl": "main-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 10, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [0.095, 2.833], + [0.686, 2.263], + [0.268, -1.005], + [0.385, -1.967], + [-0.367, -2.204], + [-0.518, -1.98], + [0, 0], + [0.264, 2.803] + ], + "o": [ + [-0.068, -2.046], + [-0.439, -1.237], + [0.268, 1.495], + [-0.24, 1.658], + [0.423, 2.539], + [0, 0], + [-0.376, -1.651], + [-0.361, -1.322] + ], + "v": [ + [18.406, 170.495], + [17.526, 164.062], + [14.819, 160.455], + [14.077, 164.167], + [14.329, 168.653], + [14.855, 171.804], + [23.839, 187.976], + [19.449, 176.021] + ] + } + ], + "t": 10 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.011, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [-0.068, 10.704], + [5.578, 13.054], + [-0.607, -8.505], + [-0.048, -15.614], + [-1.617, -16.704], + [-2.985, -16.45], + [0, 0], + [0.579, 13.458] + ], + "o": [ + [0.113, -17.636], + [-2.564, -5.362], + [3.341, 12.493], + [-0.365, 12.033], + [1.402, 14.485], + [0, 0], + [-2.818, -15.454], + [-0.736, -13.697] + ], + "v": [ + [31.531, 41.12], + [26.151, -12.938], + [20.069, -10.671], + [21.327, 45.292], + [8.579, 121.528], + [13.23, 160.804], + [25.339, 172.601], + [18.199, 122.521] + ] + } + ], + "t": 30 + }, + { + "s": [ + { + "c": true, + "i": [ + [0.084, 52.508], + [5.578, 13.054], + [-0.357, -1.755], + [-0.048, -15.614], + [-2.155, -46.525], + [-2.985, -16.45], + [0, 0], + [0.579, 13.458] + ], + "o": [ + [-0.049, -17.636], + [-0.439, -1.237], + [3.341, 12.493], + [0.082, 53.17], + [0.663, 14.538], + [0, 0], + [-2.818, -15.454], + [-2.229, -47.934] + ], + "v": [ + [36.531, -26.38], + [27.526, -72.563], + [23.069, -70.17], + [27.827, -27.208], + [10.079, 114.528], + [13.23, 160.804], + [25.089, 158.726], + [19.699, 115.521] + ] + } + ], + "t": 60 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [140.538, 170.676], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 23 + }, + { + "ty": 3, + "nm": "Null 24", + "cl": "null 24", + "sr": 1, + "st": 0, + "op": 100, + "ip": 20, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.372, + "y": 1 + }, + "s": [0, 0, 100], + "t": 20 + }, + { + "s": [100, 100, 100], + "t": 60 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.015, + "y": 0 + }, + "i": { + "x": 0.489, + "y": 1 + }, + "s": [511.5], + "t": 20 + }, + { + "s": [496.5], + "t": 40 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.015, + "y": 0 + }, + "i": { + "x": 0.489, + "y": 1 + }, + "s": [588.5], + "t": 20 + }, + { + "s": [470], + "t": 40 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.116, + "y": 1 + }, + "s": [89], + "t": 20 + }, + { + "s": [0], + "t": 60 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 24 + }, + { + "ty": 4, + "nm": "Leaf4-Body-Lines", + "cl": "leaf leaf4-body-lines", + "sr": 1, + "st": 0, + "op": 100, + "ip": 20, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [57.172, 89.079, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [13.844, 4.738], + [0.994, -1.496], + [-24.379, -8.311], + [13.015, 0.665], + [-6.961, 3.406], + [-5.219, -3.101], + [14.508, 0.747], + [-1.327, -0.249], + [0, 0], + [-6.881, 3.489], + [-1.327, -1.083], + [-1.824, -1.494], + [13.932, 0.747], + [-6.637, 3.406], + [0, 0] + ], + "o": [ + [-25.044, -8.461], + [-0.497, 0.829], + [-11.777, 5.56], + [19.568, 5.077], + [5.653, 2.208], + [-13.102, 6.895], + [1.406, 0.331], + [0, 0], + [20.399, 5.236], + [2.572, 1.746], + [2.157, 1.658], + [-12.522, 6.481], + [20.067, 5.152], + [9.453, 7.398], + [-2.572, -16.196] + ], + "v": [ + [2.242, -14.873], + [-55.806, -25.838], + [-3.482, -11.131], + [-40.382, -3.988], + [2.565, -8.89], + [18.9, -0.912], + [-22.229, 8.062], + [-18.083, 8.972], + [-18.324, 8.972], + [26.036, 3.739], + [32.008, 8.062], + [38.061, 12.709], + [-1.327, 21.02], + [42.127, 15.867], + [56.303, 27.334] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [82.539, 174.539], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 25, + "parent": 24 + }, + { + "ty": 4, + "nm": "Leaf4-Body-Reflection", + "cl": "leaf leaf4-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 20, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [57.172, 89.079, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [9.454, 8.722], + [-3.233, -0.331], + [-9.784, -11.794], + [0, 0] + ], + "o": [ + [-1.081, -0.829], + [-9.371, -8.642], + [3.151, 0.242], + [9.702, 11.882], + [0, 0] + ], + "v": [ + [10.615, 10.8], + [-20.896, -8.723], + [-31.428, -20.356], + [24.958, 2.822], + [34.493, 20.687] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [111.479, 169.305], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 26, + "parent": 24 + }, + { + "ty": 4, + "nm": "Leaf4-Body-Background", + "cl": "leaf leaf4-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 20, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [57.172, 89.079, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-1.146, 3.138], + [6.459, 17.04], + [12.853, 2.492], + [17.664, 1.001], + [-33.75, -20.19], + [-20.067, 0.331], + [-6.858, -1.341] + ], + "o": [ + [1.671, -4.398], + [-8.287, -22.1], + [-2.572, -0.498], + [10.032, 14.209], + [14.261, 8.551], + [9.841, -0.185], + [2.991, 3.493] + ], + "v": [ + [66.262, 22.607], + [64.381, -10.379], + [10.818, -49.513], + [-73.68, -58.404], + [-24.755, 13.466], + [32.958, 21.685], + [58.652, 22.053] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [88.135, 187.006], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 27, + "parent": 24 + }, + { + "ty": 4, + "nm": "Leaf4-Stem", + "cl": "leaf4-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 20, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [0.467, 3.083], + [0.604, 2.013], + [0.089, -1.434], + [-0.188, -2.007], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [-0.361, -2.385], + [-0.646, -5.112], + [-0.161, 2.316], + [0.312, 3.618], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [75.477, 79.38], + [74.887, 74.232], + [72.027, 75.178], + [72.179, 82.002], + [71.89, 89.866], + [74.076, 86.279], + [74.317, 85.529], + [75.562, 85.944] + ] + } + ], + "t": 20 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.667, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [1.022, 8.645], + [-0.209, 2.575], + [0.776, -2.247], + [-0.534, -4.943], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [-0.546, -4.517], + [-0.646, -5.112], + [-0.349, 3.503], + [0.862, 8.456], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [68.789, 52.88], + [67.574, 35.419], + [63.839, 35.366], + [64.741, 53.939], + [69.015, 73.429], + [72.138, 72.341], + [72.379, 71.591], + [73.624, 72.007] + ] + } + ], + "t": 30 + }, + { + "s": [ + { + "c": true, + "i": [ + [1.578, 14.207], + [2.607, 3.879], + [-0.786, -2.309], + [-0.879, -7.878], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [-0.731, -6.649], + [-0.646, -5.112], + [2.991, 3.493], + [1.412, 13.294], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [72.102, 38.379], + [63.762, 18.107], + [56.152, 17.553], + [66.304, 39.126], + [66.14, 56.991], + [70.201, 58.404], + [70.442, 57.654], + [71.687, 58.069] + ] + } + ], + "t": 40 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [88.135, 187.006], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 28 + }, + { + "ty": 3, + "nm": "Null 25", + "cl": "null 25", + "sr": 1, + "st": 0, + "op": 100, + "ip": 25, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.329, + "y": 1 + }, + "s": [0, 0, 100], + "t": 25 + }, + { + "s": [100, 100, 100], + "t": 70 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.167 + }, + "i": { + "x": 0.328, + "y": 1 + }, + "s": [521], + "t": 25 + }, + { + "s": [556], + "t": 55 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.167 + }, + "i": { + "x": 0.328, + "y": 1 + }, + "s": [537.5], + "t": 25 + }, + { + "s": [437.5], + "t": 55 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [-25], + "t": 25 + }, + { + "s": [0], + "t": 70 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 29 + }, + { + "ty": 4, + "nm": "Leaf3-Body-Reflection", + "cl": "leaf leaf3-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 25, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-2.328, 121.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-3.154, -12.297] + ], + "o": [ + [11.854, -23.263], + [1.431, 5.162] + ], + "v": [ + [-27.747, 13.875], + [26.316, -1.578] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [245.44, 137.318], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 30, + "parent": 29 + }, + { + "ty": 4, + "nm": "Leaf3-Body-Lines", + "cl": "leaf leaf3-body-lines", + "sr": 1, + "st": 0, + "op": 100, + "ip": 25, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-2.328, 121.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-14.183, 0.662], + [5.474, 2.908] + ], + "o": [ + [13.186, 6.982], + [-20.991, 5.4], + [0, 0] + ], + "v": [ + [-18.658, -7.271], + [22.142, 1.871], + [-22.142, -4.029] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [223.506, 182.306], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-14.259, 0.667], + [5.39, 2.908] + ], + "o": [ + [13.188, 6.979], + [-20.979, 5.4], + [0, 0] + ], + "v": [ + [-18.658, -7.269], + [22.141, 1.869], + [-22.14, -4.03] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [260.572, 157.797], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 3, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-0.995, -1.411], + [24.046, -8.898], + [4.227, -3.321], + [0, 0], + [-13.35, 4.651] + ], + "o": [ + [24.127, -8.308], + [0.579, 0.917], + [-15.174, 5.561], + [-15.34, 12.131], + [1.66, -15.868], + [0, 0] + ], + "v": [ + [-3.025, -14.586], + [53.278, -25.387], + [-0.62, -9.674], + [-31.053, 7.855], + [-53.857, 26.798], + [-3.025, -14.576] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [243.448, 163.535], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 4", + "cl": "group 4", + "ix": 4, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-14.595, 0.747], + [1.324, -0.249], + [0, 0], + [5.55, 2.903], + [0, 0], + [-0.582, -0.334] + ], + "o": [ + [13.512, 7.143], + [-1.325, 0.334], + [0, 0], + [-21.473, 5.56], + [0, 0], + [0.662, 0.333], + [0, 0] + ], + "v": [ + [-17.161, -7.889], + [24.549, 1.415], + [20.57, 2.329], + [20.811, 2.329], + [-24.549, -3.649], + [-20.983, -6.975], + [-19.156, -6.059] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.4118, 0.5961, 0.0549], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [240.915, 169.965], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 31, + "parent": 29 + }, + { + "ty": 4, + "nm": "Leaf3-Body-Background", + "cl": "leaf leaf3-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 25, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-2.328, 121.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-2.985, 8.971], + [-34.744, -2.993], + [-1.161, -4.821], + [8.042, -8.638], + [15.507, -10.051], + [7.296, 0.829], + [5.141, 1.83], + [-0.226, 4.086] + ], + "o": [ + [2.821, -7.146], + [9.952, -30.075], + [26.299, 2.243], + [0.164, 0.413], + [-10.947, 11.717], + [-22.47, 14.541], + [-11.031, -1.332], + [-3.777, -0.234], + [0, 0] + ], + "v": [ + [-55.891, 9.306], + [-53.322, -16.865], + [27.278, -54.999], + [79.77, -38.215], + [57.131, -20.689], + [23.796, 13.125], + [-26.038, 24.676], + [-50.253, 19.191], + [-56.303, 12.371] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [235.78, 177.043], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 32, + "parent": 29 + }, + { + "ty": 4, + "nm": "Leaf3-Stem", + "cl": "leaf3-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 25, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.027, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [0.348, -1.484], + [0.331, -3.035], + [0.283, -1.785], + [0, 0], + [-0.203, 1.665], + [-0.811, 2.394] + ], + "o": [ + [-0.902, 3.141], + [-0.174, 1.601], + [0, 0], + [-0.22, -1.993], + [0.297, -2.21], + [0.649, -1.914] + ], + "v": [ + [-70.753, 69.816], + [-72.486, 77.242], + [-72.562, 83.492], + [-74.434, 82.075], + [-74.202, 76.167], + [-72.803, 69.746] + ] + } + ], + "t": 25 + }, + { + "s": [ + { + "c": true, + "i": [ + [3.473, -1.984], + [2.982, -4.571], + [1.745, -9.14], + [0, 0], + [-6.059, 9.135], + [-5.524, 2.164] + ], + "o": [ + [-4.227, 2.743], + [-5.392, 8.219], + [0, 0], + [1.824, -9.969], + [3.315, -5.077], + [5.524, -2.164] + ], + "v": [ + [-50.253, 19.191], + [-61.111, 30.492], + [-71.562, 57.992], + [-79.934, 55.575], + [-68.327, 25.167], + [-56.303, 12.371] + ] + } + ], + "t": 55 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [235.78, 177.043], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 33 + }, + { + "ty": 3, + "nm": "Null 26", + "cl": "null 26", + "sr": 1, + "st": 0, + "op": 100, + "ip": 30, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [0, 0, 0], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "o": { + "x": 0.333, + "y": 0 + }, + "i": { + "x": 0.099, + "y": 1 + }, + "s": [0, 0, 100], + "t": 35 + }, + { + "s": [100, 100, 100], + "t": 80 + } + ], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.162 + }, + "i": { + "x": 0.499, + "y": 1 + }, + "s": [525.25], + "t": 30 + }, + { + "s": [574], + "t": 60 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.01, + "y": 0.184 + }, + "i": { + "x": 0.499, + "y": 1 + }, + "s": [374.25], + "t": 30 + }, + { + "s": [263.5], + "t": 60 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [-89], + "t": 30 + }, + { + "s": [0], + "t": 80 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 0, + "ix": 11 + } + }, + "ind": 34 + }, + { + "ty": 4, + "nm": "Leaf5-Body-Reflection", + "cl": "leaf leaf5-body-reflection", + "sr": 1, + "st": 0, + "op": 100, + "ip": 30, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-20.328, 295.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-23.963, 10.136], + [3.233, -4.405], + [12.437, -24.005] + ], + "o": [ + [0, 0], + [23.886, -10.139], + [-3.233, 4.404], + [0, 0] + ], + "v": [ + [-29.933, 27.002], + [-1.826, -16.864], + [26.701, -18.688], + [-29.933, 27.001] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.7255, 0.8471, 0.2078], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [226.492, 38.532], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 35, + "parent": 34 + }, + { + "ty": 4, + "nm": "Leaf5-Body-Lines", + "cl": "leaf leaf5-body-lines", + "sr": 1, + "st": 0, + "op": 100, + "ip": 30, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-20.328, 295.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-13.101, 4.735], + [5.79, 1.247] + ], + "o": [ + [14.262, 3.155], + [-18.076, 11.296], + [0, 0] + ], + "v": [ + [-19.157, -3.156], + [21.473, -5.648], + [-21.473, 1.082] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5098, 0.7059, 0.1843], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [249.795, 45.512], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 2", + "cl": "group 2", + "ix": 2, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.113, 8.224], + [-1.332, -1.163], + [19.988, -15.537], + [2.985, -4.402], + [0, 0] + ], + "o": [ + [20.234, -15.039], + [0.83, 0.749], + [-12.522, 9.72], + [-10.857, 16.196], + [-2.896, -16.117] + ], + "v": [ + [-5.851, -13.667], + [43.736, -40.171], + [-2.203, -9.598], + [-25.667, 16.244], + [-41.67, 41.334] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5098, 0.7059, 0.1843], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [234.58, 54.943], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 3", + "cl": "group 3", + "ix": 3, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-13.352, 4.821], + [1.197, -0.6], + [0, 0], + [5.971, 1.329], + [0, 0], + [-0.654, -0.177] + ], + "o": [ + [14.679, 3.322], + [-1.124, 0.729], + [0, 0], + [-18.491, 11.548], + [0, 0], + [0.671, 0.096], + [0, 0] + ], + "v": [ + [-17.991, -4.235], + [23.638, -6.73], + [20.154, -4.735], + [20.395, -4.818], + [-23.637, 1.995], + [-21.224, -2.241], + [-19.234, -1.83] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5098, 0.7059, 0.1843], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [234.863, 63.042], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 4", + "cl": "group 4", + "ix": 4, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-13.102, 4.733], + [5.79, 1.332] + ], + "o": [ + [14.262, 3.16], + [-18.077, 11.299], + [0, 0] + ], + "v": [ + [-19.157, -3.16], + [21.474, -5.65], + [-21.473, 0.996] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.5098, 0.7059, 0.1843], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [222.1, 80.075], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 36, + "parent": 34 + }, + { + "ty": 4, + "nm": "Leaf5-Body-Background", + "cl": "leaf leaf5-body-background", + "sr": 1, + "st": 0, + "op": 100, + "ip": 30, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [-20.328, 295.579, 0], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [25.124, -5.149], + [0.828, -32.393], + [0, 0], + [-1.951, -9.633], + [-5.22, -0.502], + [-10.65, 1.762], + [-16.914, 20.604], + [-6.881, 14.62], + [0.242, 0.413] + ], + "o": [ + [-33.337, 6.812], + [0, 0], + [-0.138, 5.504], + [0, 0], + [5.22, 0.502], + [7.048, -1.168], + [11.692, -14.289], + [5.055, -10.816], + [-2.403, -4.407] + ], + "v": [ + [11.531, -70.66], + [-53.066, -10.423], + [-53.061, -10.41], + [-47.927, 17.827], + [-39.845, 24.297], + [-15.83, 22.906], + [27.455, -2.436], + [49.097, -45.138], + [65.262, -68.829] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [232.375, 78.452], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 37, + "parent": 34 + }, + { + "ty": 4, + "nm": "Leaf5-Stem", + "cl": "leaf5-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 30, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [3.552, -3.528], + [0, 0], + [0.837, -2.733], + [0.233, -1.814], + [0, 0], + [0, 0], + [-0.526, 1.285], + [-0.338, 2.163], + [-0.258, 1.685] + ], + "o": [ + [0, 0], + [-0.41, 1.47], + [-0.562, 1.836], + [-0.142, 1.936], + [0, 0], + [0, 0], + [0.099, -1.09], + [0.301, -1.925], + [0, 0] + ], + "v": [ + [-65.427, 89.077], + [-66.465, 92.953], + [-67.188, 97.212], + [-68.108, 102.363], + [-68.19, 106.059], + [-64.593, 105.029], + [-64.974, 101.764], + [-64.426, 97.223], + [-63.492, 91.988] + ] + } + ], + "t": 30 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [3.552, -3.528], + [0, 0], + [2.563, -3.664], + [0.483, -5.064], + [0, 0], + [0, 0], + [-1.026, 5.91], + [-2.14, 4.55], + [-1.758, 4.935] + ], + "o": [ + [0, 0], + [-1.535, 3.47], + [-3.258, 4.658], + [-0.892, 5.311], + [0, 0], + [0, 0], + [0.974, -4.09], + [3.551, -7.55], + [0, 0] + ], + "v": [ + [-48.927, 28.327], + [-49.715, 29.453], + [-55.063, 42.963], + [-60.608, 57.362], + [-61.19, 75.809], + [-57.093, 76.404], + [-55.224, 61.138], + [-52.176, 49.598], + [-45.367, 33.488] + ] + } + ], + "t": 45 + }, + { + "s": [ + { + "c": true, + "i": [ + [3.552, -3.528], + [0, 0], + [3.371, -4.946], + [2.309, -5.064], + [0, 0], + [0, 0], + [-3.069, 6.981], + [-3.057, 3.992], + [1.008, 8.065] + ], + "o": [ + [0, 0], + [0.636, 5.951], + [-3.202, 4.697], + [-4.896, 10.967], + [0, 0], + [0, 0], + [1.619, -3.561], + [5.212, -6.805], + [-1.008, -8.065] + ], + "v": [ + [-47.927, 13.827], + [-46.965, 26.828], + [-51.188, 43.713], + [-60.608, 59.612], + [-61.19, 75.809], + [-55.718, 71.404], + [-54.224, 62.513], + [-46.426, 50.473], + [-39.617, 26.613] + ] + } + ], + "t": 60 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [232.375, 78.452], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 38 + }, + { + "ty": 4, + "nm": "Flower2-Stem", + "cl": "flower2-stem", + "sr": 1, + "st": 0, + "op": 100, + "ip": 50, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [0, 0], + [-0.133, -1.75], + [0, 0], + [0, 0], + [-0.41, 3.348], + [0, 0.083] + ], + "o": [ + [0, 0.082], + [0.313, 4.109], + [0, 0], + [0, 0], + [0.254, -2.071], + [0, 0] + ], + "v": [ + [6.052, 9.977], + [9.622, 14.077], + [9.951, 19.648], + [6.101, 24.744], + [6.11, 18.773], + [6.362, 14.251] + ] + } + ], + "t": 50 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [-4.187, -3.599], + [-4.309, -9.639], + [0, 0], + [0, 0], + [3.151, 7.061], + [4.248, 4.05] + ], + "o": [ + [4.187, 3.599], + [4.891, 10.965], + [0, 0], + [0, 0], + [-3.73, -8.309], + [-4.248, -4.05] + ], + "v": [ + [-6.698, -12.148], + [7.497, 4.202], + [9.951, 19.648], + [4.476, 15.244], + [2.985, 6.273], + [-9.638, -9.373] + ] + } + ], + "t": 60 + }, + { + "s": [ + { + "c": true, + "i": [ + [-4.187, -3.599], + [-4.309, -9.639], + [0, 0], + [0, 0], + [3.151, 7.061], + [4.248, 4.05] + ], + "o": [ + [4.187, 3.599], + [4.891, 10.965], + [0, 0], + [0, 0], + [-3.73, -8.309], + [-4.248, -4.05] + ], + "v": [ + [-9.448, -19.648], + [9.372, 3.452], + [9.951, 19.648], + [4.476, 15.244], + [2.985, 6.273], + [-14.263, -14.498] + ] + } + ], + "t": 70 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [162.636, 157.049], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 39 + }, + { + "ty": 4, + "nm": "Flower1-Stem", + "cl": "flower1-stem", + "sr": 1, + "st": 10, + "op": 110, + "ip": 50, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [180.336, 254.04, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [200, 200, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [553.672, 559.079], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [ + { + "c": true, + "i": [ + [0, 0], + [0.226, -2.073], + [0, 0], + [0, 0], + [-1.581, 1.785], + [-0.082, 0] + ], + "o": [ + [-0.082, 0], + [-0.224, 2.06], + [0, 0], + [0, 0], + [1.544, -1.84], + [0, 0] + ], + "v": [ + [-8.492, 13.024], + [-8.939, 17.438], + [-9.215, 21.415], + [-12.604, 14.867], + [-10.582, 9.713], + [-7.811, 6.758] + ] + } + ], + "t": 50 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0, + "y": 1 + }, + "s": [ + { + "c": true, + "i": [ + [2.954, -1.651], + [4.89, -6.396], + [0, 0], + [0, 0], + [-6.551, 8.379], + [-4.795, 2.085] + ], + "o": [ + [-5.599, 3.13], + [-4.148, 5.403], + [0, 0], + [0, 0], + [5.638, -7.479], + [2.898, -1.26] + ], + "v": [ + [7.383, -8.476], + [-5.064, 3.938], + [-8.465, 14.04], + [-12.604, 14.867], + [-8.707, 1.088], + [7.189, -11.367] + ] + } + ], + "t": 60 + }, + { + "s": [ + { + "c": true, + "i": [ + [5.546, -3.224], + [4.89, -6.396], + [0, 0], + [0, 0], + [-6.551, 8.379], + [-4.773, 2.135] + ], + "o": [ + [-5.546, 3.224], + [-4.148, 5.403], + [0, 0], + [0, 0], + [5.638, -7.479], + [4.773, -2.135] + ], + "v": [ + [15.258, -9.476], + [-3.814, 4.813], + [-6.965, 12.29], + [-12.604, 14.867], + [-8.707, 1.088], + [12.189, -14.867] + ] + } + ], + "t": 70 + } + ], + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.6118, 0.7882, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [166.538, 243.252], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 40 + }, + { + "ty": 4, + "nm": "Dust-Body-Bit3", + "cl": "dirt-body-bit3", + "sr": 1, + "st": 3, + "op": 103, + "ip": 3, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [-5.551, 316.252, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [30, 30, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [529.385], + "t": 3 + }, + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [518.879], + "t": 6 + }, + { + "s": [511.385], + "t": 9 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.124, + "y": 0.162 + }, + "i": { + "x": 0.475, + "y": 0.975 + }, + "s": [864.252], + "t": 3 + }, + { + "o": { + "x": 0.433, + "y": -0.019 + }, + "i": { + "x": 0.94, + "y": 0.934 + }, + "s": [843.505], + "t": 6 + }, + { + "s": [865.252], + "t": 9 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [0], + "t": 3 + }, + { + "s": [-60], + "t": 9 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Ellipse 1", + "cl": "ellipse 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.046, 0], + [0, -11.046], + [11.046, 0], + [-5.172, 9.76] + ], + "o": [ + [11.046, 0], + [0, 11.046], + [-11.046, 0], + [5.565, -10.5] + ], + "v": [ + [0, -20], + [20, 0], + [0, 10.5], + [-20, 0] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [-5, 321], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 41 + }, + { + "ty": 4, + "nm": "Dust-Body-Bit2", + "cl": "dirt-body-bit2", + "sr": 1, + "st": 3, + "op": 103, + "ip": 3, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [-5.551, 316.252, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [60, 60, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.092, + "y": 0.198 + }, + "i": { + "x": 0.472, + "y": 0.405 + }, + "s": [529.385], + "t": 3 + }, + { + "o": { + "x": 0.357, + "y": 0.275 + }, + "i": { + "x": 0.924, + "y": 1.015 + }, + "s": [556.379], + "t": 8 + }, + { + "s": [595.885], + "t": 13 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.124, + "y": 0.379 + }, + "i": { + "x": 0.476, + "y": 0.745 + }, + "s": [864.252], + "t": 3 + }, + { + "o": { + "x": 0.523, + "y": -0.158 + }, + "i": { + "x": 0.94, + "y": 0.839 + }, + "s": [849.505], + "t": 8 + }, + { + "s": [873.252], + "t": 13 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [0], + "t": 3 + }, + { + "s": [60], + "t": 13 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Ellipse 1", + "cl": "ellipse 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.046, 0], + [0, -11.046], + [11.046, 0], + [-5.172, 9.76] + ], + "o": [ + [11.046, 0], + [0, 11.046], + [-11.046, 0], + [5.565, -10.5] + ], + "v": [ + [0, -20], + [20, 0], + [0, 10.5], + [-20, 0] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [-5, 321], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 42 + }, + { + "ty": 4, + "nm": "Dust-Body-Bit1", + "cl": "dirt-body-bit1", + "sr": 1, + "st": 3, + "op": 103, + "ip": 3, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [-5.551, 316.252, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [60, 60, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "o": { + "x": 0.06, + "y": 0.144 + }, + "i": { + "x": 0.478, + "y": 0.57 + }, + "s": [529.385], + "t": 3 + }, + { + "o": { + "x": 0.477, + "y": 0.539 + }, + "i": { + "x": 0.924, + "y": 0.98 + }, + "s": [488.879], + "t": 8 + }, + { + "s": [459.385], + "t": 13 + } + ], + "ix": 3 + }, + "y": { + "a": 1, + "k": [ + { + "o": { + "x": 0.124, + "y": 0.497 + }, + "i": { + "x": 0.481, + "y": 1.644 + }, + "s": [864.252], + "t": 3 + }, + { + "o": { + "x": 0.595, + "y": 0.209 + }, + "i": { + "x": 0.94, + "y": 0.94 + }, + "s": [853.005], + "t": 8 + }, + { + "s": [892.752], + "t": 13 + } + ], + "ix": 4 + } + }, + "r": { + "a": 1, + "k": [ + { + "o": { + "x": 0.167, + "y": 0.167 + }, + "i": { + "x": 0.833, + "y": 0.833 + }, + "s": [0], + "t": 3 + }, + { + "s": [-60], + "t": 13 + } + ], + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Ellipse 1", + "cl": "ellipse 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [-11.046, 0], + [0, -11.046], + [11.046, 0], + [-5.172, 9.76] + ], + "o": [ + [11.046, 0], + [0, 11.046], + [-11.046, 0], + [5.565, -10.5] + ], + "v": [ + [0, -20], + [20, 0], + [0, 10.5], + [-20, 0] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [-5, 321], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 43 + }, + { + "ty": 4, + "nm": "Dust-Body-Backdrop", + "cl": "dirt-body-backdrop", + "sr": 1, + "st": 0, + "op": 100, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { + "a": { + "a": 0, + "k": [50.385, 53.564, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [500, 500, 100], + "ix": 6 + }, + "sk": { + "a": 0, + "k": 0 + }, + "p": { + "a": 0, + "k": [539.425, 797.822], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "sa": { + "a": 0, + "k": 0 + }, + "o": { + "a": 0, + "k": 100, + "ix": 11 + } + }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Group 1", + "cl": "group 1", + "ix": 1, + "cix": 2, + "np": 2, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "cl": "path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [0, 0], + [-15.179, 17.93], + [7.44, 3.742], + [0.791, 0.78], + [3.737, 0.616], + [0.33, -0.099], + [1.155, 0.065], + [3.472, -2.533], + [3.112, 0.455], + [0, 0], + [0, -4.36], + [0, 0] + ], + "o": [ + [14.187, 10.868], + [1.917, -2.278], + [-3.737, -1.888], + [-2.446, -2.407], + [-3.079, -0.521], + [-4.631, 1.463], + [-1.29, -0.095], + [-2.612, 1.92], + [-7.176, -1.042], + [0, 0], + [0, 4.327], + [0, 0] + ], + "v": [ + [-43.303, 12.72], + [48.625, 8.263], + [35.266, -5.306], + [25.644, -6.999], + [17.907, -16.988], + [-2.694, -21.771], + [-10.266, -18.354], + [-20.021, -15.041], + [-26.275, -8.272], + [-37.518, -6.808], + [-50.084, 5.752], + [-43.305, 12.715] + ] + }, + "ix": 2 + } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "cl": "fill 1", + "c": { + "a": 0, + "k": [0.3412, 0.1333, 0.0784], + "ix": 4 + }, + "r": 1, + "o": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": "tr", + "a": { + "a": 0, + "k": [0, 0], + "ix": 1 + }, + "s": { + "a": 0, + "k": [100, 100], + "ix": 3 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "p": { + "a": 0, + "k": [50.686, 85.259], + "ix": 2 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + } + } + ] + } + ], + "ind": 44 + } + ], + "v": "5.7.0", + "fr": 30, + "op": 100, + "ip": 0, + "assets": [] +} diff --git a/packages/client/assets/lottie/water-animation.lottie b/packages/client/assets/lottie/water-animation.lottie new file mode 100644 index 0000000..ffd34e5 Binary files /dev/null and b/packages/client/assets/lottie/water-animation.lottie differ diff --git a/packages/client/assets/png/pcb-board.png b/packages/client/assets/png/pcb-board.png new file mode 100644 index 0000000..d3c7611 Binary files /dev/null and b/packages/client/assets/png/pcb-board.png differ diff --git a/packages/client/assets/png/solar-panel.png b/packages/client/assets/png/solar-panel.png new file mode 100644 index 0000000..560a0d0 Binary files /dev/null and b/packages/client/assets/png/solar-panel.png differ diff --git a/packages/client/assets/svg/Loading.svg b/packages/client/assets/svg/Loading.svg new file mode 100644 index 0000000..64ad659 --- /dev/null +++ b/packages/client/assets/svg/Loading.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/client/assets/svg/PatchDry.svg b/packages/client/assets/svg/PatchDry.svg new file mode 100644 index 0000000..d7651f5 --- /dev/null +++ b/packages/client/assets/svg/PatchDry.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/assets/svg/PatchNormal.svg b/packages/client/assets/svg/PatchNormal.svg new file mode 100644 index 0000000..2e87d7f --- /dev/null +++ b/packages/client/assets/svg/PatchNormal.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/assets/svg/PatchWet.svg b/packages/client/assets/svg/PatchWet.svg new file mode 100644 index 0000000..19c382d --- /dev/null +++ b/packages/client/assets/svg/PatchWet.svg @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/assets/svg/produce/apple.svg b/packages/client/assets/svg/produce/apple.svg new file mode 100644 index 0000000..182b8e4 --- /dev/null +++ b/packages/client/assets/svg/produce/apple.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/beet.svg b/packages/client/assets/svg/produce/beet.svg new file mode 100644 index 0000000..3af1764 --- /dev/null +++ b/packages/client/assets/svg/produce/beet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/cabbage.svg b/packages/client/assets/svg/produce/cabbage.svg new file mode 100644 index 0000000..f84b33c --- /dev/null +++ b/packages/client/assets/svg/produce/cabbage.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/carrot.svg b/packages/client/assets/svg/produce/carrot.svg new file mode 100644 index 0000000..9088b0c --- /dev/null +++ b/packages/client/assets/svg/produce/carrot.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/assets/svg/produce/cherry.svg b/packages/client/assets/svg/produce/cherry.svg new file mode 100644 index 0000000..5348537 --- /dev/null +++ b/packages/client/assets/svg/produce/cherry.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/corn.svg b/packages/client/assets/svg/produce/corn.svg new file mode 100644 index 0000000..6ee0a30 --- /dev/null +++ b/packages/client/assets/svg/produce/corn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/cucumber.svg b/packages/client/assets/svg/produce/cucumber.svg new file mode 100644 index 0000000..918e1ba --- /dev/null +++ b/packages/client/assets/svg/produce/cucumber.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/kiwi.svg b/packages/client/assets/svg/produce/kiwi.svg new file mode 100644 index 0000000..786dd79 --- /dev/null +++ b/packages/client/assets/svg/produce/kiwi.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/leek.svg b/packages/client/assets/svg/produce/leek.svg new file mode 100644 index 0000000..51bb03d --- /dev/null +++ b/packages/client/assets/svg/produce/leek.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/lettuce.svg b/packages/client/assets/svg/produce/lettuce.svg new file mode 100644 index 0000000..5f3cf69 --- /dev/null +++ b/packages/client/assets/svg/produce/lettuce.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/peas.svg b/packages/client/assets/svg/produce/peas.svg new file mode 100644 index 0000000..524c6c4 --- /dev/null +++ b/packages/client/assets/svg/produce/peas.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/plum.svg b/packages/client/assets/svg/produce/plum.svg new file mode 100644 index 0000000..8e1af85 --- /dev/null +++ b/packages/client/assets/svg/produce/plum.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/potato.svg b/packages/client/assets/svg/produce/potato.svg new file mode 100644 index 0000000..47632b5 --- /dev/null +++ b/packages/client/assets/svg/produce/potato.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/radish.svg b/packages/client/assets/svg/produce/radish.svg new file mode 100644 index 0000000..c7bd98e --- /dev/null +++ b/packages/client/assets/svg/produce/radish.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/raspberry.svg b/packages/client/assets/svg/produce/raspberry.svg new file mode 100644 index 0000000..ddeefd2 --- /dev/null +++ b/packages/client/assets/svg/produce/raspberry.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/strawberry.svg b/packages/client/assets/svg/produce/strawberry.svg new file mode 100644 index 0000000..2db7831 --- /dev/null +++ b/packages/client/assets/svg/produce/strawberry.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/assets/svg/produce/tomato.svg b/packages/client/assets/svg/produce/tomato.svg new file mode 100644 index 0000000..9cee538 --- /dev/null +++ b/packages/client/assets/svg/produce/tomato.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/client/index.html b/packages/client/index.html new file mode 100644 index 0000000..0973901 --- /dev/null +++ b/packages/client/index.html @@ -0,0 +1,26 @@ + + + + + + Tom's Allotment + + + + + + + +
+ + + diff --git a/packages/client/package-lock.json b/packages/client/package-lock.json new file mode 100644 index 0000000..a0fa6ca --- /dev/null +++ b/packages/client/package-lock.json @@ -0,0 +1,3192 @@ +{ + "name": "@toms-allotment/client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@toms-allotment/client", + "version": "0.0.0", + "dependencies": { + "antd": "^5.29.3", + "blurhash": "^2.0.5", + "dayjs": "^1.11.13", + "fflate": "^0.8.2", + "lottie-react": "^2.4.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.6.0", + "react-responsive": "^10.0.1", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.1", + "simplebar-react": "^3.3.2", + "socket.io-client": "^4.8.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.3", + "vite": "^5.4.10", + "vite-plugin-svgo": "^2.0.0", + "vite-plugin-svgr": "^4.5.0" + } + }, + "../../node_modules/.pnpm/@vitejs+plugin-react@4.7.0_vite@5.4.21_@types+node@25.5.0_/node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "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" + }, + "devDependencies": { + "@vitejs/react-common": "workspace:*", + "babel-plugin-react-compiler": "19.1.0-rc.2", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "rolldown": "1.0.0-beta.27", + "tsdown": "^0.12.9", + "vitest": "^3.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "../../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom": { + "version": "18.3.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "../../node_modules/.pnpm/react@18.3.1/node_modules/react": { + "version": "18.3.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "../../node_modules/.pnpm/socket.io-client@4.8.3/node_modules/socket.io-client": { + "version": "4.8.3", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "../../node_modules/.pnpm/vite@5.4.21_@types+node@25.5.0/node_modules/vite": { + "version": "5.4.21", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "devDependencies": { + "@ampproject/remapping": "^2.3.0", + "@babel/parser": "^7.25.6", + "@jridgewell/trace-mapping": "^0.3.25", + "@polka/compression": "^1.0.0-next.25", + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-commonjs": "^26.0.1", + "@rollup/plugin-dynamic-import-vars": "^2.1.2", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "15.2.3", + "@rollup/pluginutils": "^5.1.0", + "@types/escape-html": "^1.0.4", + "@types/pnpapi": "^0.0.5", + "artichokie": "^0.2.1", + "cac": "^6.7.14", + "chokidar": "^3.6.0", + "connect": "^3.7.0", + "convert-source-map": "^2.0.0", + "cors": "^2.8.5", + "cross-spawn": "^7.0.3", + "debug": "^4.3.6", + "dep-types": "link:./src/types", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "es-module-lexer": "^1.5.4", + "escape-html": "^1.0.3", + "estree-walker": "^3.0.3", + "etag": "^1.8.1", + "fast-glob": "^3.3.2", + "http-proxy": "^1.18.1", + "launch-editor-middleware": "^2.9.1", + "lightningcss": "^1.26.0", + "magic-string": "^0.30.11", + "micromatch": "^4.0.8", + "mlly": "^1.7.1", + "mrmime": "^2.0.0", + "open": "^8.4.2", + "parse5": "^7.1.2", + "pathe": "^1.1.2", + "periscopic": "^4.0.2", + "picocolors": "^1.0.1", + "picomatch": "^2.3.1", + "postcss-import": "^16.1.0", + "postcss-load-config": "^4.0.2", + "postcss-modules": "^6.0.0", + "resolve.exports": "^2.0.2", + "rollup-plugin-dts": "^6.1.1", + "rollup-plugin-esbuild": "^6.1.1", + "rollup-plugin-license": "^3.5.2", + "sass": "^1.77.8", + "sass-embedded": "^1.77.8", + "sirv": "^2.0.4", + "source-map-support": "^0.5.21", + "strip-ansi": "^7.1.0", + "strip-literal": "^2.1.0", + "tsconfck": "^3.1.4", + "tslib": "^2.7.0", + "types": "link:./types", + "ufo": "^1.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/@ant-design/colors": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz", + "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz", + "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@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.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "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/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "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/@rc-component/async-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.0.tgz", + "integrity": "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.3.tgz", + "integrity": "sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.1.tgz", + "integrity": "sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.1.tgz", + "integrity": "sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.44.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "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/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "resolved": "../../node_modules/.pnpm/@vitejs+plugin-react@4.7.0_vite@5.4.21_@types+node@25.5.0_/node_modules/@vitejs/plugin-react", + "link": true + }, + "node_modules/antd": { + "version": "5.29.3", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz", + "integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.2.1", + "@ant-design/cssinjs": "^1.23.0", + "@ant-design/cssinjs-utils": "^1.1.3", + "@ant-design/fast-color": "^2.0.6", + "@ant-design/icons": "^5.6.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.26.0", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.1.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.3.0", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.34.0", + "rc-checkbox": "~3.5.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.3.0", + "rc-dropdown": "~4.2.1", + "rc-field-form": "~2.7.1", + "rc-image": "~7.12.0", + "rc-input": "~1.8.0", + "rc-input-number": "~9.5.0", + "rc-mentions": "~2.20.0", + "rc-menu": "~9.16.1", + "rc-motion": "^2.9.5", + "rc-notification": "~5.6.4", + "rc-pagination": "~5.1.0", + "rc-picker": "~4.11.3", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.1", + "rc-resize-observer": "^1.4.3", + "rc-segmented": "~2.7.0", + "rc-select": "~14.16.8", + "rc-slider": "~11.1.9", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.54.0", + "rc-tabs": "~15.7.0", + "rc-textarea": "~1.10.2", + "rc-tooltip": "~6.4.0", + "rc-tree": "~5.13.1", + "rc-tree-select": "~5.27.0", + "rc-upload": "~4.11.0", + "rc-util": "^5.44.4", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz", + "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/blurhash": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.5.tgz", + "integrity": "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==", + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "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/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "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/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "2" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT", + "peer": true + }, + "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/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", + "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "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/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "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/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "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/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "license": "MIT" + }, + "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/lottie-react": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lottie-react/-/lottie-react-2.4.1.tgz", + "integrity": "sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==", + "license": "MIT", + "dependencies": { + "lottie-web": "^5.10.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/lottie-web": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz", + "integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==", + "license": "MIT" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "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/matchmediaquery": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz", + "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==", + "license": "MIT", + "dependencies": { + "css-mediaquery": "^0.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "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/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/rc-cascader": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz", + "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz", + "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz", + "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz", + "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.44.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz", + "integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-image": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz", + "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz", + "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz", + "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.8.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz", + "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.8.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.10.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.1", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz", + "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz", + "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz", + "integrity": "sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz", + "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz", + "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz", + "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.1.tgz", + "integrity": "sha512-izj1Nw/Dw2Vb7EVr+D/E9lUTkBe+kKC+SAFSU9zqr7WV2W5Ktaa9Gc7cB2jTqgk8GROJayltaec+DBlYKc6d+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.8", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz", + "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.54.0", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz", + "integrity": "sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.44.3", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz", + "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz", + "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.8.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz", + "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1", + "rc-util": "^5.44.3" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz", + "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz", + "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.11.0.tgz", + "integrity": "sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/rc-virtual-list": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz", + "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/react": { + "resolved": "../../node_modules/.pnpm/react@18.3.1/node_modules/react", + "link": true + }, + "node_modules/react-dom": { + "resolved": "../../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom", + "link": true + }, + "node_modules/react-icons": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz", + "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "peer": true + }, + "node_modules/react-responsive": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.1.tgz", + "integrity": "sha512-OM5/cRvbtUWEX8le8RCT8scA8y2OPtb0Q/IViEyCEM5FBN8lRrkUOZnu87I88A6njxDldvxG+rLBxWiA7/UM9g==", + "license": "MIT", + "dependencies": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.4.2", + "prop-types": "^15.6.1", + "shallow-equal": "^3.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "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.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recharts": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz", + "integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts/node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/recharts/node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/recharts/node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT", + "peer": true + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "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/shallow-equal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", + "license": "MIT" + }, + "node_modules/simplebar-core": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/simplebar-core/-/simplebar-core-1.3.2.tgz", + "integrity": "sha512-qKgTTuTqapjsFGkNhCjyPhysnbZGpQqNmjk0nOYjFN5ordC/Wjvg+RbYCyMSnW60l/Z0ZS82GbNltly6PMUH1w==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" + } + }, + "node_modules/simplebar-react": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-3.3.2.tgz", + "integrity": "sha512-ZsgcQhKLtt5ra0BRIJeApfkTBQCa1vUPA/WXI4HcYReFt+oCEOvdVz6rR/XsGJcKxTlCRPmdGx1uJIUChupo+A==", + "license": "MIT", + "dependencies": { + "simplebar-core": "^1.3.2" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/socket.io-client": { + "resolved": "../../node_modules/.pnpm/socket.io-client@4.8.3/node_modules/socket.io-client", + "link": true + }, + "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/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/vite": { + "resolved": "../../node_modules/.pnpm/vite@5.4.21_@types+node@25.5.0/node_modules/vite", + "link": true + }, + "node_modules/vite-plugin-svgo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgo/-/vite-plugin-svgo-2.0.0.tgz", + "integrity": "sha512-WaiOUlmt1fKw1w/WWJnx9gVk42D1BASKZmGCPpy6iTLE5iIemN2lim3MwI7Rffyt3aQnPrLfLSLnI8ZUJ9s49g==", + "dev": true, + "license": "ISC", + "dependencies": { + "svgo": "3.3.2" + }, + "peerDependencies": { + "typescript": ">=4.9.4", + "vite": ">=4.0.2" + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.5.0.tgz", + "integrity": "sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.2.0", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 0000000..d9009d5 --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,32 @@ +{ + "name": "@toms-allotment/client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "antd": "^5.29.3", + "blurhash": "^2.0.5", + "dayjs": "^1.11.13", + "fflate": "^0.8.2", + "lottie-react": "^2.4.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.6.0", + "react-responsive": "^10.0.1", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.1", + "simplebar-react": "^3.3.2", + "socket.io-client": "^4.8.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.3", + "vite": "^5.4.10", + "vite-plugin-svgo": "^2.0.0", + "vite-plugin-svgr": "^4.5.0" + } +} diff --git a/packages/client/src/App.css b/packages/client/src/App.css new file mode 100644 index 0000000..d46d0ec --- /dev/null +++ b/packages/client/src/App.css @@ -0,0 +1,219 @@ +:root { + /* Mirrors .sidebar width: 25% of viewport with 420px minimum */ + --sidebar-width: max(25vw, 380px); + --app-main-padding-top: 18vh; + --app-main-padding-bottom: 18vh; + --app-main-padding-right: 10vw; + --app-main-padding-left: 10vw; + --bg: #0f1419; + --surface: #1a222c; + --border: #2a3544; + /* Same language as .btn default border (Button.css) */ + --border-ui-width: 2px; + --border-ui-color: color-mix(in srgb, var(--text) 8%, transparent); + --danger-border-color: color-mix(in srgb, var(--danger) 8%, transparent); + --text: #e8eef5; + --muted: color-mix(in srgb, var(--text) 75%, transparent); + --accent: #9cc815; + --accent-dim: #1fa86a; + --danger: #e56868; + --bg-spot: #1a2838; + --sidebar-bg: #0c1218; + --link-hover: color-mix(in srgb, var(--text) 8%, transparent); + --on-accent: #0a120e; + --warn: #e6b35c; + --font: "Rubik", system-ui, sans-serif; + --mono: "JetBrains Mono", ui-monospace, monospace; +} + +html { + height: 100%; + overflow: hidden; + overscroll-behavior: none; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + height: 100%; + overflow: hidden; + overscroll-behavior: none; + background: var(--bg); + color: var(--text); + font-family: var(--font); + font-size: 15px; + line-height: 1.5; +} + +#root { + height: 100%; +} + +.app-shell { + position: relative; + width: 100vw; + height: 100dvh; + overflow: hidden; +} + +.app-main { + width: 100vw; + height: 100dvh; + min-width: 0; + display: flex; + flex-direction: column; + padding: 0; +} + +.app-shell-inner { + transition: transform 0.35s cubic-bezier(0.33, 1, 0.68, 1); + transform-origin: 0% 50%; +} + +.app-shell--mobile .app-shell-inner { + transform-origin: 50% 50%; +} + +.page-title { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + line-height: 1.2; + letter-spacing: -0.02em; +} + +.overview-page { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +.overview-section.wide { + width: 100%; +} + +.sub { + margin: 0.35rem 0 0; + color: var(--muted); + font-size: 0.9rem; +} + +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; +} + +.dot.ok { + background: var(--accent); +} + +.dot.bad { + background: var(--warn); +} + +.user { + color: var(--muted); + font-size: 0.9rem; +} + +.grid { + display: grid; + gap: 1.25rem; +} + +.metrics.metrics--horizontal > * { + flex: 1 1 140px; + min-width: 0; +} + +.metrics .label { + display: block; + font-size: 0.75rem; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.25rem; +} + +.metrics strong { + font-size: 1.35rem; + font-weight: 600; +} + +.field { + display: flex; + flex-direction: column; + margin-bottom: 1rem; + font-size: 0.9rem; + color: var(--muted); +} + +.field select { + display: block; + margin-top: 0.35rem; + width: 100%; + max-width: 320px; + padding: 0.5rem 0.65rem; + border-radius: 8px; + border: var(--border-ui-width) solid var(--border-ui-color); + background: var(--bg); + color: var(--text); + font-family: inherit; +} + +.hint { + font-size: 0.85rem; + color: var(--muted); +} + +.muted { + color: var(--muted); + font-size: 0.85rem; +} + +.sessions { + list-style: none; + margin: 0; + padding: 0; +} + +.sessions li { + padding: 0.65rem 0; + border-bottom: var(--border-ui-width) solid var(--border-ui-color); + display: grid; + gap: 0.35rem; +} + +.sessions li:last-child { + border-bottom: none; +} + +.mono { + font-family: var(--mono); + font-size: 14px; + font-weight: 600; +} + +.animated { + transition: + transform 0.35s cubic-bezier(0.33, 1, 0.68, 1), + opacity 0.35s cubic-bezier(0.33, 1, 0.68, 1); + opacity: 0; + transform: translateY(20px); +} + +.animated.visible { + transform: translateY(0); + opacity: 1; +} + +.animated.hidden { + transform: translateY(-20px); + opacity: 0; +} diff --git a/packages/client/src/App.jsx b/packages/client/src/App.jsx new file mode 100644 index 0000000..a562f07 --- /dev/null +++ b/packages/client/src/App.jsx @@ -0,0 +1,153 @@ +import React, { + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + BrowserRouter, + Navigate, + Route, + Routes, + useLocation, +} from "react-router-dom"; +import AnimatedOutlet from "./components/AnimatedOutlet.jsx"; +import { PageScroll } from "./components/PageScroll.jsx"; +import { Sidebar } from "./components/Sidebar.jsx"; +import { AuthProvider } from "./contexts/AuthContext.jsx"; +import { ApiProvider } from "./contexts/ApiContext.jsx"; +import { LottieFileProvider } from "./contexts/LottieFileContext.jsx"; +import { ThemeProvider, useTheme } from "./contexts/ThemeContext.jsx"; +import { PatchesPage } from "./pages/PatchesPage.jsx"; +import { PowerPage } from "./pages/PowerPage.jsx"; +import { WaterTankPage } from "./pages/WaterTankPage.jsx"; +import { WateringSessionPage } from "./pages/WateringSessionPage.jsx"; +import { WaterSchedulesPage } from "./pages/WaterSchedulesPage.jsx"; + +const SITE_TITLE = "Tom's Allotment"; + +const ROUTES = [ + { + path: "/patches", + element: , + title: "Patches", + scrollable: true, + }, + { + path: "/water-tank", + element: , + title: "Water tank", + scrollable: true, + }, + { + path: "/power", + element: , + title: "Power", + scrollable: true, + }, + { + path: "/watering-sessions", + element: , + title: "Watering sessions", + scrollable: true, + }, + { + path: "/water-schedules", + element: , + title: "Watering schedules", + scrollable: true, + }, +]; + +const REDIRECT_ROUTES = [ + { path: "/", to: "/patches" }, + { path: "/overview", to: "/patches" }, +]; + +function normalizePathname(pathname) { + if (pathname.length > 1 && pathname.endsWith("/")) { + return pathname.slice(0, -1); + } + return pathname; +} + +function routeMetaForPathname(pathname) { + const key = normalizePathname(pathname); + const route = ROUTES.find((r) => normalizePathname(r.path) === key); + return route + ? { title: route.title, scrollable: route.scrollable } + : { title: SITE_TITLE, scrollable: true }; +} + +function AppLayout() { + const { isMobile, pageTransitionDuration } = useTheme(); + const location = useLocation(); + const { title: pageTitle, scrollable: routeScrollable } = useMemo( + () => routeMetaForPathname(location.pathname), + [location.pathname], + ); + + const [scrollable, setScrollable] = useState(routeScrollable); + const prevPathnameRef = useRef(location.pathname); + + useEffect(() => { + if (prevPathnameRef.current === location.pathname) return; + + prevPathnameRef.current = location.pathname; + const id = window.setTimeout(() => { + setScrollable(routeScrollable); + }, pageTransitionDuration); + + return () => { + window.clearTimeout(id); + }; + }, [location.pathname, routeScrollable, pageTransitionDuration]); + + useEffect(() => { + document.title = + pageTitle === SITE_TITLE ? SITE_TITLE : `${pageTitle} · ${SITE_TITLE}`; + }, [pageTitle]); + + return ( +
+
+ +
+ + + +
+
+
+ ); +} + +export default function App() { + return ( + + + + + + + }> + {REDIRECT_ROUTES.map(({ path, to }) => ( + } + /> + ))} + {ROUTES.map(({ path, element }) => ( + + ))} + + + + + + + + ); +} diff --git a/packages/client/src/components/AnimatedOutlet.css b/packages/client/src/components/AnimatedOutlet.css new file mode 100644 index 0000000..3d108bd --- /dev/null +++ b/packages/client/src/components/AnimatedOutlet.css @@ -0,0 +1,82 @@ +.animated-outlet-container { + position: relative; + width: 100%; +} + +.animated-outlet { + width: 100%; + will-change: opacity, transform; +} + +/* Stable view: stays in document flow so scroll height matches content */ +.animated-outlet.visible { + position: relative; + z-index: 10; + opacity: 1; + transform: translateY(0); + will-change: auto; +} + +/* New route: overlays while old still defines block height */ +.animated-outlet.enter { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 20; + min-height: 100%; + opacity: 0; + transform: translateY(20px); + animation: animatedOutletEnter var(--page-transition-duration, 500ms) ease + forwards; +} + +/* Outgoing route: same flow as visible; motion matches AnimatedText exit */ +.animated-outlet.exit { + position: relative; + z-index: 10; + opacity: 1; + transform: translateY(0); + animation: animatedOutletExit var(--page-transition-duration, 500ms) ease + forwards; +} + +@keyframes animatedOutletEnter { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes animatedOutletExit { + to { + opacity: 0; + transform: translateY(-20px); + } +} + +/* Back / forward: enter from top, exit downward */ +.animated-outlet.enter.reverse { + transform: translateY(-20px); + animation: animatedOutletEnterReverse var(--page-transition-duration, 500ms) + ease forwards; +} + +.animated-outlet.exit.reverse { + animation: animatedOutletExitReverse var(--page-transition-duration, 500ms) + ease forwards; +} + +@keyframes animatedOutletEnterReverse { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes animatedOutletExitReverse { + to { + opacity: 0; + transform: translateY(20px); + } +} diff --git a/packages/client/src/components/AnimatedOutlet.jsx b/packages/client/src/components/AnimatedOutlet.jsx new file mode 100644 index 0000000..38c815f --- /dev/null +++ b/packages/client/src/components/AnimatedOutlet.jsx @@ -0,0 +1,184 @@ +import React, { + useRef, + useState, + useEffect, + useLayoutEffect, +} from "react"; +import { useOutlet, useLocation, useNavigationType } from "react-router-dom"; +import { useTheme } from "../contexts/ThemeContext.jsx"; +import "./AnimatedOutlet.css"; + +function AnimatedOutlet() { + const { pageTransitionDuration } = useTheme(); + const location = useLocation(); + const outlet = useOutlet(); + const navigationType = useNavigationType(); + const [outlet1, setOutlet1] = useState(outlet); + const [outlet2, setOutlet2] = useState(null); + const [activeOutlet, setActiveOutlet] = useState(1); + const [isTransitioning, setIsTransitioning] = useState(false); + const [isReversed, setIsReversed] = useState(false); + const timeoutRef = useRef(null); + const prevPathnameRef = useRef(location.pathname); + const isTransitioningRef = useRef(false); + const activeOutletRef = useRef(1); + /** Latest route to animate to after the in-flight transition finishes (coalesces rapid navigations). */ + const pendingNavigationRef = useRef(null); + const pageTransitionDurationRef = useRef(pageTransitionDuration); + pageTransitionDurationRef.current = pageTransitionDuration; + const containerRef = useRef(null); + const outlet1Ref = useRef(null); + const outlet2Ref = useRef(null); + + activeOutletRef.current = activeOutlet; + + useLayoutEffect(() => { + const container = containerRef.current; + if (!container) return; + + if (!isTransitioning) { + container.style.minHeight = ""; + return; + } + + const exitEl = activeOutlet === 1 ? outlet1Ref.current : outlet2Ref.current; + const enterEl = activeOutlet === 1 ? outlet2Ref.current : outlet1Ref.current; + if (!exitEl || !enterEl) return; + + const hOld = exitEl.scrollHeight; + const hNew = enterEl.scrollHeight; + + if (hNew > hOld) { + container.style.minHeight = `${hNew}px`; + } else { + container.style.minHeight = ""; + } + }, [isTransitioning, activeOutlet, outlet1, outlet2]); + + const completeTransition = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + + const current = activeOutletRef.current; + const nextActive = current === 1 ? 2 : 1; + if (current === 1) { + setOutlet1(null); + setActiveOutlet(2); + } else { + setOutlet2(null); + setActiveOutlet(1); + } + + const pending = pendingNavigationRef.current; + const shouldChain = + pending && pending.pathname !== prevPathnameRef.current; + + if (shouldChain) { + pendingNavigationRef.current = null; + prevPathnameRef.current = pending.pathname; + setIsReversed(pending.navigationType === "POP"); + if (nextActive === 1) { + setOutlet2(pending.outlet); + } else { + setOutlet1(pending.outlet); + } + isTransitioningRef.current = true; + setIsTransitioning(true); + timeoutRef.current = setTimeout( + completeTransition, + pageTransitionDurationRef.current, + ); + } else { + isTransitioningRef.current = false; + setIsTransitioning(false); + } + }; + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + }; + }, []); + + useEffect(() => { + if (location.pathname !== prevPathnameRef.current && outlet) { + if (isTransitioningRef.current) { + pendingNavigationRef.current = { + pathname: location.pathname, + outlet, + navigationType, + }; + return; + } + + const isBackward = navigationType === "POP"; + setIsReversed(isBackward); + + isTransitioningRef.current = true; + setIsTransitioning(true); + prevPathnameRef.current = location.pathname; + + const current = activeOutletRef.current; + if (current === 1) { + setOutlet2(outlet); + } else { + setOutlet1(outlet); + } + + timeoutRef.current = setTimeout( + completeTransition, + pageTransitionDurationRef.current, + ); + } + }, [location.pathname, outlet, navigationType]); + + const getOutlet1Class = () => { + if (!outlet1) return ""; + const baseClass = isReversed ? "reverse" : ""; + if (isTransitioning && activeOutlet === 1) + return `exit ${baseClass}`.trim(); + if (isTransitioning && activeOutlet === 2) + return `enter ${baseClass}`.trim(); + if (activeOutlet === 1) return "visible"; + return ""; + }; + + const getOutlet2Class = () => { + if (!outlet2) return ""; + const baseClass = isReversed ? "reverse" : ""; + if (isTransitioning && activeOutlet === 2) + return `exit ${baseClass}`.trim(); + if (isTransitioning && activeOutlet === 1) + return `enter ${baseClass}`.trim(); + if (activeOutlet === 2) return "visible"; + return ""; + }; + + return ( +
+ {outlet1 && ( +
+ {outlet1} +
+ )} + {outlet2 && ( +
+ {outlet2} +
+ )} +
+ ); +} + +export default AnimatedOutlet; diff --git a/packages/client/src/components/AnimatedText.css b/packages/client/src/components/AnimatedText.css new file mode 100644 index 0000000..452ea66 --- /dev/null +++ b/packages/client/src/components/AnimatedText.css @@ -0,0 +1,46 @@ +.animated-text-container { + position: relative; + overflow: visible; +} + +.animated-text { + white-space: nowrap; +} + +.animated-text-current { + opacity: 1; + transition: opacity 0s; + transform: translateY(0); + width: max-content; +} + +.animated-text-hidden { + opacity: 0; +} + +.animated-text-exiting { + position: absolute; + top: 0; + left: 0; + width: 100%; + opacity: 1; + transform: translateY(0); +} + +.animated-text-exiting-visible { + opacity: 0; + transform: translateY(-20px); +} + +.animated-text-entering { + opacity: 0; + position: absolute; + top: 0; + left: 0; + transform: translateY(20px); +} + +.animated-text-entering-visible { + opacity: 1; + transform: translateY(0); +} diff --git a/packages/client/src/components/AnimatedText.jsx b/packages/client/src/components/AnimatedText.jsx new file mode 100644 index 0000000..eca1769 --- /dev/null +++ b/packages/client/src/components/AnimatedText.jsx @@ -0,0 +1,198 @@ +import React, { useState, useEffect, useRef } from "react"; +import "./AnimatedText.css"; + +function AnimatedText({ + text, + transitionDuration = 500, + transitionDelay = 0, + style = {}, + animateWidth = true, + animateEnterenceExitWidth = true, +}) { + const [enteringText, setEnteringText] = useState(""); + const [currentText, setCurrentText] = useState(text); + const [exitingText, setExitingText] = useState(""); + const [isEntering, setIsEntering] = useState(false); + const [isExiting, setIsExiting] = useState(false); + const delay = 10; + const [isTransitioning, setIsTransitioning] = useState(false); + const isTransitioningRef = useRef(false); + const prevTextRef = useRef(text); + const currentTextRef = useRef(currentText); + const textRef = useRef(text); + const timeoutRefs = useRef([]); + const enteringTextRef = useRef(null); + const [textWidth, setTextWidth] = useState(0); + const initialLoad = useRef(true); + + // Helper function to clear all timeouts + const clearAllTimeouts = () => { + timeoutRefs.current.forEach((timeoutId) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }); + timeoutRefs.current = []; + }; + + useEffect(() => { + const prevText = prevTextRef.current; + + // Check if text changed + const textChanged = prevText != text; + + // Only trigger transition if text actually changed + if (textChanged) { + // Cancel any in-progress transition + clearAllTimeouts(); + + // Reset transition states immediately + setIsExiting(false); + setIsEntering(false); + setExitingText(""); + setEnteringText(""); + + // Only apply entrance delay when transitioning from "" to text + const entranceOnly = prevText === "" && text !== ""; + const effectiveDelay = entranceOnly ? transitionDelay : 0; + + // Get the actual current text (from ref to avoid stale state) + const actualCurrentText = currentTextRef.current || prevText; + + // Set exiting text to current text (starts visible) + setExitingText(actualCurrentText); + + // Update current text immediately to the new value + setCurrentText(text); + currentTextRef.current = text; + + // Set entering text to new text + setEnteringText(text); + + setIsTransitioning(true); + isTransitioningRef.current = true; + + // Start exit animation (no delay - exit is immediate) + const exitTimeout = setTimeout(() => { + setIsExiting(true); + }, delay); + timeoutRefs.current.push(exitTimeout); + + // Start enter animation (with delay only for entrance: "" to text) + const enterTimeout = setTimeout(() => { + setIsEntering(true); + }, delay + effectiveDelay); + timeoutRefs.current.push(enterTimeout); + + // Clear exiting text after transition completes + const cleanupTimeout = setTimeout( + () => { + // When animateEnterenceExitWidth is false, set width after effect has finished (before clearing ref) + if (!animateEnterenceExitWidth && enteringTextRef.current) { + setTextWidth(enteringTextRef.current.clientWidth); + } + setIsExiting(false); + setIsEntering(false); + setExitingText(""); + setEnteringText(""); + setIsTransitioning(false); + isTransitioningRef.current = false; + }, + transitionDuration + effectiveDelay + delay, + ); + timeoutRefs.current.push(cleanupTimeout); + + prevTextRef.current = text; + } else { + // Update text without transition for updates without change + currentTextRef.current = text; + setEnteringText(""); + setExitingText(""); + setIsEntering(false); + setIsExiting(false); + setIsTransitioning(false); + isTransitioningRef.current = false; + setCurrentText(text); + } + + // Cleanup function + return () => { + clearAllTimeouts(); + }; + }, [text, transitionDuration, transitionDelay, animateEnterenceExitWidth]); + + useEffect(() => { + if (enteringTextRef.current) { + if (animateWidth && isTransitioning) { + setTextWidth(enteringTextRef.current.clientWidth); + } + } + if ( + (!animateWidth || !animateEnterenceExitWidth) && + !isTransitioning && + textRef.current + ) { + setTextWidth(textRef.current.clientWidth); + } + if (initialLoad.current == true && textRef.current) { + initialLoad.current = false; + setTextWidth(textRef.current.clientWidth); + } + }, [enteringText, animateWidth, animateEnterenceExitWidth, isTransitioning]); + + // Keep currentTextRef in sync + useEffect(() => { + currentTextRef.current = currentText; + }, [currentText]); + + const transitionStyle = { + transition: `opacity ${transitionDuration}ms ease, transform ${transitionDuration}ms ease`, + }; + + return ( +
+
+ {currentText} +
+ {exitingText && ( +
+ {exitingText} +
+ )} + + {enteringText && ( +
+ {enteringText} +
+ )} +
+ ); +} + +export default AnimatedText; diff --git a/packages/client/src/components/Button.css b/packages/client/src/components/Button.css new file mode 100644 index 0000000..0f40dbf --- /dev/null +++ b/packages/client/src/components/Button.css @@ -0,0 +1,125 @@ +/* Matches tombutcher-ui .tb-button (components.css) — uses app :root --text / --bg */ + +.btn { + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + padding: 8px 14px; + background-color: transparent; + color: var(--text); + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 18px; + transition: + background-color 0.3s ease, + color 0.3s ease, + fill 0.3s ease, + stroke 0.3s ease, + transform 0.5s ease, + border-color 0.3s ease, + filter 0.3s ease; + display: inline-flex; + align-items: center; + justify-content: center; + margin: 0; + line-height: 1; + min-height: 30px; + font-weight: 500; + font-family: inherit; + font-size: 0.9rem; + position: relative; + transform-origin: center center; + transform: scale(1); + width: max-content; + cursor: pointer; + text-decoration: none; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn:not(:disabled):hover { + background-color: var(--text); + color: var(--bg); + transform: scale(1.02); +} + +.btn:not(:disabled):active { + background-color: var(--text); + color: var(--bg); + transform: scale(0.975); +} + +.btn > svg { + fill: var(--text); + stroke: var(--text); + height: 16px; + width: auto; + margin-right: 8px; + flex-shrink: 0; + transition: + fill 0.3s ease, + stroke 0.3s ease; +} + +.btn:not(:disabled):hover > svg { + fill: var(--bg); + stroke: var(--bg); +} + +/* Filled CTA — emphasis while keeping pill + motion language */ +.btn--primary { + background-color: var(--accent); + color: var(--on-accent); + border: var(--border-ui-width) solid transparent; + font-weight: 600; +} + +.btn--primary:not(:disabled):hover { + filter: brightness(1.06); + background-color: var(--accent); + color: var(--on-accent); +} + +.btn--danger:not(:disabled):hover { + background-color: var(--danger); + color: var(--on-warn); + border: var(--border-ui-width) solid transparent; +} + +.btn--primary:not(:disabled):active { + filter: brightness(0.98); + transform: scale(0.975); +} + +.btn--primary > svg { + fill: var(--on-accent); + stroke: var(--on-accent); +} + +.btn--primary:not(:disabled):hover > svg, +.btn--primary:not(:disabled):active > svg { + fill: var(--on-accent); + stroke: var(--on-accent); +} + +.btn--primary:disabled { + filter: none; +} + +/* Lighter outline; same hover invert as default */ +.btn--ghost { + border-color: color-mix(in srgb, var(--text) 8%, transparent); +} + +.btn--small { + padding: 6px 10px; + min-height: 26px; + font-size: 0.8rem; +} + +.btn--small > svg { + height: 14px; + margin-right: 6px; +} diff --git a/packages/client/src/components/Button.jsx b/packages/client/src/components/Button.jsx new file mode 100644 index 0000000..b3f6b18 --- /dev/null +++ b/packages/client/src/components/Button.jsx @@ -0,0 +1,47 @@ +import "./Button.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * @param {object} props + * @param {'default' | 'primary' | 'ghost'} [props.variant] + * @param {'middle' | 'small'} [props.size] + * @param {string} [props.href] — if set, renders `` + * @param {string} [props.className] + * @param {import('react').ReactNode} props.children + */ +export function Button({ + variant = "default", + size = "middle", + href, + className, + type = "button", + danger = false, + children, + ...rest +}) { + const classes = cx( + "btn", + variant === "primary" && "btn--primary", + variant === "ghost" && "btn--ghost", + size === "small" && "btn--small", + danger && "btn--danger", + className, + ); + + if (href) { + return ( + + {children} + + ); + } + + return ( + + ); +} diff --git a/packages/client/src/components/DateTimeInput.css b/packages/client/src/components/DateTimeInput.css new file mode 100644 index 0000000..2a1afe1 --- /dev/null +++ b/packages/client/src/components/DateTimeInput.css @@ -0,0 +1,184 @@ +.date-time-input { + display: block; + width: 100%; + min-width: 0; +} + +.date-time-input .popover-wrapper { + display: block; + width: 100%; +} + +.date-time-input__trigger { + cursor: pointer; + width: 100%; +} + +.date-time-input__trigger--readonly { + cursor: default; +} + +.date-time-input__field { + cursor: inherit; +} + +.date-time-input__icon { + display: inline-flex; + align-items: center; + color: var(--muted); + line-height: 0; + pointer-events: none; +} + +.popover-root.date-time-input__popover { + min-width: 280px; + max-width: min(320px, calc(100vw - 24px)); +} + +.date-time-input__panel { + padding: 4px 2px 2px; + min-width: 0; +} + +.date-time-input__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 10px; + padding: 0 4px; +} + +.date-time-input__month { + font-size: 0.9rem; + font-weight: 600; + color: var(--text); + flex: 1; + text-align: center; +} + +.date-time-input__nav { + flex: 0 0 auto; +} + +.date-time-input__weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 2px; + margin-bottom: 4px; + padding: 0 2px; +} + +.date-time-input__weekday { + text-align: center; + font-size: 0.7rem; + font-weight: 600; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.02em; +} + +.date-time-input__grid { + display: flex; + flex-direction: column; + gap: 2px; + margin-bottom: 12px; +} + +.date-time-input__row { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 2px; +} + +.date-time-input__day { + margin: 0; + padding: 0; + min-height: 32px; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text); + font-size: 0.85rem; + font-weight: 500; + font-family: inherit; + cursor: pointer; + transition: + background-color 0.12s ease, + color 0.12s ease; +} + +.date-time-input__day:hover:not(:disabled):not( + .date-time-input__day--disabled + ) { + background: color-mix(in srgb, var(--accent) 14%, transparent); +} + +.date-time-input__day--outside { + color: color-mix(in srgb, var(--muted) 85%, var(--text)); + opacity: 0.55; +} + +.date-time-input__day--today { + box-shadow: inset 0 0 0 2px var(--accent); +} + +.date-time-input__day--selected { + background: var(--accent); + color: var(--bg); +} + +.date-time-input__day--selected:hover:not(:disabled) { + background: color-mix(in srgb, var(--accent) 88%, var(--text)); + color: var(--bg); +} + +.date-time-input__day--disabled { + opacity: 0.35; + cursor: not-allowed; +} + +.date-time-input__time-row { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 6px 4px; + border-top: 1px solid var(--border-ui-color); +} + +.date-time-input__time-label { + font-size: 0.8rem; + font-weight: 600; + color: var(--muted); + white-space: nowrap; +} + +.date-time-input__time { + flex: 1 1 auto; + min-width: 0; + box-sizing: border-box; + font-family: inherit; + font-size: 0.9rem; + font-weight: 500; + color: var(--text); + background: var(--bg); + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 12px; + padding: 6px 10px; + outline: none; +} + +.date-time-input__time:focus-visible { + border-color: var(--accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); +} + +.date-time-input__time:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.date-time-input__footer { + display: flex; + justify-content: flex-start; +} diff --git a/packages/client/src/components/DateTimeInput.jsx b/packages/client/src/components/DateTimeInput.jsx new file mode 100644 index 0000000..465d5db --- /dev/null +++ b/packages/client/src/components/DateTimeInput.jsx @@ -0,0 +1,406 @@ +import { + forwardRef, + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, +} from "react"; +import dayjs from "dayjs"; +import { + IoCalendarOutline, + IoChevronBack, + IoChevronForward, + IoCloseCircle, +} from "react-icons/io5"; +import { Button } from "./Button.jsx"; +import { IconButton } from "./IconButton.jsx"; +import { Popover } from "./Popover.jsx"; +import "./Input.css"; +import "./DateTimeInput.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +const sizeClass = { + small: "sm", + middle: "md", + large: "lg", +}; + +/** Day.js has no `dayjs.invalid()` (that is Moment.js API). */ +const INVALID_DAYJS = dayjs(new Date(NaN)); + +/** + * @param {unknown} v + * @returns {import('dayjs').Dayjs} + */ +function toDayjs(v) { + if (v == null || v === "") return INVALID_DAYJS; + const d = dayjs(v); + return d.isValid() ? d : INVALID_DAYJS; +} + +/** + * Ant Design–like datetime field: Input-styled trigger, calendar + time in Popover. + * + * @param {object} props + * @param {Date | string | number | null} [props.value] + * @param {Date | string | number | null} [props.defaultValue] + * @param {(date: Date | null) => void} [props.onChange] + * @param {string} [props.format='YYYY-MM-DD HH:mm'] + * @param {string} [props.placeholder] + * @param {boolean} [props.disabled] + * @param {boolean} [props.readOnly] + * @param {boolean} [props.allowClear] + * @param {boolean} [props.bordered=true] + * @param {'large' | 'middle' | 'small'} [props.size='middle'] + * @param {'' | 'error' | 'warning'} [props.status] + * @param {(d: import('dayjs').Dayjs) => boolean} [props.disabledDate] + * @param {boolean} [props.open] + * @param {(open: boolean) => void} [props.onOpenChange] + * @param {string} [props.placement='bottomLeft'] + * @param {string} [props.className] + * @param {import('react').CSSProperties} [props.style] + * @param {string} [props.id] + * @param {string} [props.name] + * @param {string} [props.inputMode] + */ +export const DateTimeInput = forwardRef(function DateTimeInput( + { + value, + defaultValue, + onChange, + format = "YYYY-MM-DD HH:mm", + placeholder = "Select date and time", + disabled = false, + readOnly = false, + allowClear = false, + bordered = true, + size = "middle", + status, + disabledDate, + open: openProp, + onOpenChange, + placement = "bottomLeft", + className, + style, + id, + name, + showTime = true, + inputMode = "none", + ...rest + }, + ref, +) { + const uid = useId(); + const timeInputId = id ? `${id}-time` : `date-time-input-time-${uid}`; + const sz = sizeClass[size] ?? "md"; + const innerRef = useRef(null); + const setRefs = useCallback( + (node) => { + innerRef.current = node; + if (typeof ref === "function") ref(node); + else if (ref) ref.current = node; + }, + [ref], + ); + + const isControlled = value !== undefined; + const [innerValue, setInnerValue] = useState(() => + defaultValue != null ? toDayjs(defaultValue).toDate() : null, + ); + + useEffect(() => { + if (!isControlled && defaultValue !== undefined) { + const d = toDayjs(defaultValue); + setInnerValue(d.isValid() ? d.toDate() : null); + } + }, [defaultValue, isControlled]); + + const resolvedDate = useMemo(() => { + const raw = isControlled ? value : innerValue; + const d = toDayjs(raw); + return d.isValid() ? d : null; + }, [isControlled, value, innerValue]); + + const [uncontrolledOpen, setUncontrolledOpen] = useState(false); + const isOpenControlled = openProp !== undefined; + const open = isOpenControlled ? openProp : uncontrolledOpen; + + const setOpen = useCallback( + (next) => { + if (!isOpenControlled) setUncontrolledOpen(next); + onOpenChange?.(next); + }, + [isOpenControlled, onOpenChange], + ); + + const [viewMonth, setViewMonth] = useState(() => + (resolvedDate ?? dayjs()).startOf("month"), + ); + const [draft, setDraft] = useState(() => resolvedDate ?? dayjs()); + + useEffect(() => { + if (!open) return; + const base = resolvedDate ?? dayjs(); + setDraft(base); + setViewMonth(base.startOf("month")); + }, [open, resolvedDate]); + + const emitChange = useCallback( + (nextDayjs) => { + const next = nextDayjs.isValid() ? nextDayjs.toDate() : null; + if (!isControlled) setInnerValue(next); + onChange?.(next); + }, + [isControlled, onChange], + ); + + const handleClear = (e) => { + e.preventDefault(); + e.stopPropagation(); + if (disabled || readOnly) return; + emitChange(INVALID_DAYJS); + setOpen(false); + innerRef.current?.focus?.(); + }; + + const weeks = useMemo(() => { + const start = viewMonth.startOf("month").startOf("week"); + const rows = []; + let cursor = start; + for (let w = 0; w < 6; w++) { + const row = []; + for (let d = 0; d < 7; d++) { + row.push(cursor); + cursor = cursor.add(1, "day"); + } + rows.push(row); + } + return rows; + }, [viewMonth]); + + const weekdayLabels = useMemo(() => { + const start = dayjs().startOf("week"); + return Array.from({ length: 7 }, (_, i) => + start.add(i, "day").format("dd"), + ); + }, []); + + const displayText = resolvedDate ? resolvedDate.format(format) : ""; + + const timeStr = draft.isValid() ? draft.format("HH:mm") : "00:00"; + + const statusMod = + status === "error" ? "error" : status === "warning" ? "warning" : null; + + const showClear = + allowClear && + !disabled && + !readOnly && + resolvedDate != null && + resolvedDate.isValid(); + + const pickerDisabled = disabled || readOnly; + + const panel = ( +
e.preventDefault()} + > +
+ setViewMonth((m) => m.subtract(1, "month"))} + > + + + + {viewMonth.format("MMMM YYYY")} + + setViewMonth((m) => m.add(1, "month"))} + > + + +
+
+ {weekdayLabels.map((l) => ( + + {l} + + ))} +
+
+ {weeks.map((row, ri) => ( +
+ {row.map((cell) => { + const inMonth = cell.isSame(viewMonth, "month"); + const selected = draft.isValid() && cell.isSame(draft, "day"); + const isToday = cell.isSame(dayjs(), "day"); + const cellDisabled = Boolean(disabledDate?.(cell)); + return ( + + ); + })} +
+ ))} +
+ {showTime && ( +
+ + + { + const v = e.target.value; + if (!v) return; + const [hh, mm] = v.split(":").map((x) => parseInt(x, 10)); + if (!Number.isFinite(hh) || !Number.isFinite(mm)) return; + const base = draft.isValid() ? draft : dayjs(); + const next = base.hour(hh).minute(mm).second(0); + setDraft(next); + emitChange(next); + }} + /> +
+ )} +
+ +
+
+ ); + + const trigger = ( + + { + if (pickerDisabled) return; + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + setOpen(true); + } + }} + {...rest} + /> + + {showClear ? ( + + ) : null} + + + + + + ); + + return ( +
+ + {trigger} + +
+ ); +}); + +DateTimeInput.displayName = "DateTimeInput"; diff --git a/packages/client/src/components/Drawer.css b/packages/client/src/components/Drawer.css new file mode 100644 index 0000000..4fcb0bf --- /dev/null +++ b/packages/client/src/components/Drawer.css @@ -0,0 +1,458 @@ +/* Drawer panel + mask (Ant Design–style). Main content scale lives on .app-main. */ + +.drawer-root { + position: fixed; + inset: 0; + z-index: 1000; + pointer-events: none; + width: 100vw; + height: 100dvh; +} + +.drawer-root.drawer-root--active { + pointer-events: auto; +} + +.drawer-mask { + position: absolute; + inset: 0; + background: color-mix(in srgb, #000 45%, transparent); + opacity: 0; + transition: opacity 0.5s ease; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.drawer-root.drawer-root--visible .drawer-mask { + opacity: 1; +} + +.drawer-wrap { + position: absolute; + display: flex; + pointer-events: none; +} + +.drawer-wrap--right { + inset: 0 0 0 auto; + flex-direction: row; + justify-content: flex-end; +} + +.drawer-wrap--left { + inset: 0 auto 0 0; + flex-direction: row; + justify-content: flex-start; +} + +.drawer-wrap--top { + inset: 0 0 auto 0; + flex-direction: column; + justify-content: flex-start; +} + +.drawer-wrap--bottom { + inset: auto 0 0 0; + flex-direction: column; + justify-content: flex-end; +} + +.drawer-panel--has-header-backdrop { + position: relative; +} + +.drawer-header-backdrop { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + box-sizing: border-box; + pointer-events: none; + transition: height 0.35s cubic-bezier(0.33, 1, 0.68, 1); +} + +.drawer-header-backdrop-inner { + width: 100%; + height: 100%; + z-index: 1; +} + +.drawer-header-backdrop-blur { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 2; + filter: blur(6px); + transform: scale(1.05); + transition: opacity 0.35s cubic-bezier(0.33, 1, 0.68, 1); +} + +.drawer-panel { + pointer-events: auto; + display: flex; + flex-direction: column; + max-width: 100%; + max-height: 100%; + background: var(--surface); + color: var(--text); + box-shadow: -12px 0 40px color-mix(in srgb, #000 35%, transparent); + transition: transform 0.35s cubic-bezier(0.33, 1, 0.68, 1); +} + +.drawer-wrap--right .drawer-panel { + height: 100%; + border-right: none; + transform: translateX(100%); +} + +.drawer-wrap--left .drawer-panel { + height: 100%; + border-left: none; + box-shadow: 12px 0 40px color-mix(in srgb, #000 35%, transparent); + transform: translateX(-100%); +} + +.drawer-wrap--top .drawer-panel { + width: 100%; + border-top: none; + box-shadow: 0 12px 40px color-mix(in srgb, #000 35%, transparent); + transform: translateY(-100%); +} + +.drawer-wrap--bottom .drawer-panel { + width: 100%; + border-bottom: none; + box-shadow: 0 -12px 40px color-mix(in srgb, #000 35%, transparent); + transform: translateY(100%); +} + +.drawer-root.drawer-root--visible .drawer-wrap--right .drawer-panel, +.drawer-root.drawer-root--visible .drawer-wrap--left .drawer-panel, +.drawer-root.drawer-root--visible .drawer-wrap--top .drawer-panel, +.drawer-root.drawer-root--visible .drawer-wrap--bottom .drawer-panel { + transform: translate(0, 0); +} + +.drawer-header { + flex-shrink: 0; + display: flex; + top: 0; + left: 0; + right: 0; + position: absolute; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 4vw 4vw 0; + min-height: 52px; + z-index: 100; +} + +.drawer-panel--has-header-backdrop .drawer-header { + padding-bottom: 4vw; +} + +.drawer-panel--mobile .drawer-header { + padding: var(--app-main-padding-left) var(--app-main-padding-right) 0; +} + +.drawer-panel--has-header-backdrop.drawer-panel--mobile .drawer-header { + padding-bottom: var(--app-main-padding-left); +} + +.drawer-panel--has-header-backdrop .drawer-header .drawer-title, +.drawer-panel--has-header-backdrop .drawer-header .drawer-close { + color: #ffffff; + text-shadow: none; +} + +.drawer-footer { + flex-shrink: 0; + position: absolute; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: flex-start; + flex-wrap: wrap; + gap: 0.75rem; + padding: 0 4vw max(4vw, env(safe-area-inset-bottom, 0px)); + min-height: 52px; + z-index: 100; + box-sizing: border-box; +} + +.drawer-panel--mobile .drawer-footer { + padding-left: var(--app-main-padding-left); + padding-right: var(--app-main-padding-right); + padding-bottom: var(--app-main-padding-left); +} + +.drawer-title { + margin: 0; + font-size: 28px; + line-height: 1; + font-family: "Libre Baskerville", serif; + text-shadow: 0 0 20px var(--surface); + font-weight: 700; + letter-spacing: -0.02em; + flex: 1; + min-width: 0; +} + +.drawer-close { + appearance: none; + -webkit-appearance: none; + margin: 0; + padding: 0.35rem; + line-height: 1; + border: none; + border-radius: 10px; + background: transparent; + color: var(--text); + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + transition: + background-color 0.2s ease, + color 0.2s ease; +} + +.drawer-close:hover { + background: var(--link-hover); + color: var(--text); +} + +.drawer-close svg { + width: 24px; + height: 24px; +} + +.drawer-body { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; + padding: 0; +} + +.drawer-scroll-wrapper { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + position: relative; +} + +.drawer-scroll-top-gradient { + position: absolute; + inset: 0; + top: -24px; + left: 0; + right: 0; + z-index: 2; + opacity: 1; + pointer-events: none; + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255) 50%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255) 50%, + rgb(255 255 255 / 0%) 100% + ); +} + +.drawer-scroll-top-gradient--has-header-backdrop { + mask-image: linear-gradient( + to bottom, + rgb(255 255 255 / 0%) 0%, + rgb(255 255 255) 65%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to bottom, + rgb(255 255 255 / 0%) 0%, + rgb(255 255 255) 65%, + rgb(255 255 255 / 0%) 100% + ); + transition: top 0.35s cubic-bezier(0.33, 1, 0.68, 1); +} + +.drawer-scroll-backdrop-header-top-gradient { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 2; + opacity: 1; + pointer-events: none; + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255 / 0%) 100% + ); +} + +.drawer-scroll-bottom-gradient { + position: absolute; + top: unset; + bottom: -30px; + left: 0; + right: 0; + z-index: 2; + opacity: 1; + pointer-events: none; + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + mask-image: linear-gradient( + to top, + rgb(255 255 255) 0%, + rgb(255 255 255) 25%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to top, + rgb(255 255 255) 0%, + rgb(255 255 255) 25%, + rgb(255 255 255 / 0%) 100% + ); +} + +.drawer-scroll-vscroll { + flex: 1; + min-height: 0; + width: 100%; + max-height: none; + height: auto; + margin: 0; + padding: 0; + mask: linear-gradient( + 180deg, + transparent 0%, + black var(--drawer-scroll-top-gradient-height, 50px), + black var(--drawer-scroll-bottom-gradient-height, 50px), + transparent 100% + ); + -webkit-mask: linear-gradient( + 180deg, + transparent 0%, + black var(--drawer-scroll-top-gradient-height, 50px), + black var(--drawer-scroll-bottom-gradient-height, 50px), + transparent 100% + ); +} + +.drawer-scroll-vscroll .simplebar-vertical { + margin-left: 0.35rem; + width: var(--border-ui-width); + border-radius: 18px; + overflow: hidden; + opacity: 0; + transition: opacity 0.3s ease; + margin-top: var(--drawer-scroll-simplebar-track-margin-top, 0); + margin-bottom: var(--drawer-scroll-simplebar-track-margin-bottom, 0); + margin-right: calc(4vw / 2); +} + +.drawer-scroll-vscroll::before { + content: ""; + position: absolute; + right: 0; + top: 0; + bottom: 0; + z-index: 1; + opacity: 0; + transition: opacity 0.3s ease; + width: var(--border-ui-width); + border-radius: 18px; +} + +.drawer-scroll-vscroll.simplebar-scrolling::before { + opacity: 1; +} + +.drawer-scroll-vscroll.simplebar-scrolling .simplebar-vertical { + opacity: 1; +} + +.drawer-scroll-content { + display: flex; + flex-direction: column; + gap: 1.25rem; + box-sizing: border-box; + padding: 4vw; + min-height: 100%; + margin: 0; +} + +.drawer-scroll-start { + scroll-snap-align: start; + scroll-snap-stop: normal; + display: block; +} + +.drawer-scroll-vscroll .simplebar-scrollbar::before { + top: 0; + bottom: 0; + left: 0; + right: 0; + background: var(--border-ui-color); + border-radius: 18px; +} + +.drawer-scroll-vscroll .simplebar-scrollbar.simplebar-visible, +.drawer-scroll-vscroll .simplebar-scrollbar.simplebar-visible::before { + opacity: 1; +} + +.drawer-panel--mobile .drawer-scroll-content { + padding: var(--app-main-padding-left) var(--app-main-padding-right); +} + +.drawer-panel--mobile .drawer-scroll-vscroll .simplebar-vertical { + margin-right: calc(var(--app-main-padding-right) / 2); +} + +.drawer-body--flush .drawer-scroll-content { + padding: 0; + gap: 0; +} + +.app-shell-inner.app-shell--drawer-open { + transform: scale(0.94); +} + +.drawer-panel.drawer-panel--has-header-backdrop .drawer-footer { + bottom: unset; + color: #ffffff; + transition: + top 0.35s cubic-bezier(0.33, 1, 0.68, 1), + opacity 0.2s ease; +} diff --git a/packages/client/src/components/Drawer.jsx b/packages/client/src/components/Drawer.jsx new file mode 100644 index 0000000..3175d07 --- /dev/null +++ b/packages/client/src/components/Drawer.jsx @@ -0,0 +1,508 @@ +import { + useCallback, + useEffect, + useId, + useLayoutEffect, + useRef, + useState, +} from "react"; +import { createPortal } from "react-dom"; +import { IoClose } from "react-icons/io5"; +import SimpleBar from "simplebar-react"; +import "simplebar-react/dist/simplebar.min.css"; +import { useTheme } from "../contexts/ThemeContext.jsx"; +import "./Drawer.css"; + +const DRAWER_SCROLL_GRADIENT_HEIGHT = 40; +const SCROLL_BACKDROP_HEIGHT_DEBOUNCE_MS = 300; + +/** @type {number} */ +let appMainDrawerDepth = 0; +function syncAppMainDrawerClass() { + const shell = document.querySelector(".app-shell-inner"); + if (!shell) return; + shell.classList.toggle("app-shell--drawer-open", appMainDrawerDepth > 0); +} + +function pushAppMainDrawer() { + appMainDrawerDepth += 1; + syncAppMainDrawerClass(); +} + +function popAppMainDrawer() { + appMainDrawerDepth = Math.max(0, appMainDrawerDepth - 1); + syncAppMainDrawerClass(); +} + +const TRANSITION_MS = 350; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * Ant Design–style drawer: slides from an edge with a mask. While open, scales `.app-main`. + * + * @param {object} props + * @param {boolean} props.open + * @param {() => void} props.onClose + * @param {import('react').ReactNode} [props.title] + * @param {'left' | 'right' | 'top' | 'bottom'} [props.placement] + * @param {number | string} [props.width] — right/left drawer width + * @param {number | string} [props.height] — top/bottom drawer height + * @param {string} [props.className] — on panel + * @param {boolean} [props.maskClosable] + * @param {boolean} [props.closable] + * @param {boolean} [props.destroyOnClose] + * @param {import('react').ReactNode} [props.footer] — pinned below scroll, like header above + * @param {import('react').ReactNode} [props.headerBackdrop] — behind title/close, height `headerBackdropHeight`, centered; scroll content gets `padding-top` of that height + * @param {number | string} [props.headerBackdropHeight] + * @param {import('react').ReactNode} props.children + */ +export function Drawer({ + open, + onClose, + title, + placement = "right", + width = 378, + height = 256, + className, + maskClosable = true, + closable = true, + destroyOnClose = false, + footer, + headerBackdrop = undefined, + showHeaderBackdrop = false, + headerBackdropHeight = 120, + children, +}) { + const { isMobile } = useTheme(); + const effectivePlacement = isMobile ? "bottom" : placement; + const [mounted, setMounted] = useState(open); + const [visible, setVisible] = useState(open); + const closeTimerRef = useRef(null); + const scrollContentRef = useRef(null); + const animatedRevealTimeoutsRef = useRef([]); + const headerMeasureRef = useRef(null); + const footerMeasureRef = useRef(null); + const [headerClientHeight, setHeaderClientHeight] = useState(0); + const [footerClientHeight, setFooterClientHeight] = useState(0); + const numericBackdrop = typeof headerBackdropHeight === "number"; + const [scrolledBackdropHeaderHeight, setScrolledBackdropHeaderHeight] = + useState(() => + typeof headerBackdropHeight === "number" + ? headerBackdropHeight + : Number(headerBackdropHeight) || 120, + ); + /** Footer opacity vs scroll: 1 at top → 0 over the same range as backdrop height collapse. */ + const [footerScrollOpacity, setFooterScrollOpacity] = useState(1); + + const titleId = useId(); + + useEffect(() => { + if (!numericBackdrop) return; + setScrolledBackdropHeaderHeight(headerBackdropHeight); + }, [headerBackdropHeight, numericBackdrop]); + + useEffect(() => { + if (!visible || !showHeaderBackdrop) return; + let active = true; + let removeListener = () => {}; + + const maxH = headerBackdropHeight; + const minH = headerClientHeight; + const scrollRange = + minH > 0 && typeof maxH === "number" ? Math.max(0, maxH - minH) : 0; + + const attach = () => { + const content = scrollContentRef.current; + const wrapper = content?.closest(".simplebar-content-wrapper"); + if (!wrapper || !active) return; + + let scrollDebounceTimer = 0; + /** @type {number | null} */ + let queuedScrollTop = null; + + /** Apply header backdrop height and footer opacity from scroll position `y`. */ + const flushScrollDerivedUi = (y) => { + if (!numericBackdrop || scrollRange <= 0) { + setFooterScrollOpacity(1); + } else { + const t = Math.min(1, Math.max(0, y / scrollRange)); + setFooterScrollOpacity(1 - t); + } + if (numericBackdrop) { + const next = Math.max(minH, maxH - Math.min(y, scrollRange)); + setScrolledBackdropHeaderHeight(next); + } + }; + + const scheduleScrollDebounceTimeout = () => { + scrollDebounceTimer = window.setTimeout(() => { + scrollDebounceTimer = 0; + if (queuedScrollTop != null) { + const yq = queuedScrollTop; + queuedScrollTop = null; + flushScrollDerivedUi(yq); + scheduleScrollDebounceTimeout(); + } + }, SCROLL_BACKDROP_HEIGHT_DEBOUNCE_MS); + }; + + const onScroll = () => { + const y = wrapper.scrollTop; + if (!numericBackdrop) { + flushScrollDerivedUi(y); + return; + } + if (scrollDebounceTimer === 0) { + flushScrollDerivedUi(y); + scheduleScrollDebounceTimeout(); + } else { + queuedScrollTop = y; + } + }; + + flushScrollDerivedUi(wrapper.scrollTop); + wrapper.addEventListener("scroll", onScroll, { passive: true }); + removeListener = () => { + window.clearTimeout(scrollDebounceTimer); + scrollDebounceTimer = 0; + queuedScrollTop = null; + wrapper.removeEventListener("scroll", onScroll); + }; + }; + + let innerRaf = 0; + const outerRaf = requestAnimationFrame(() => { + innerRaf = requestAnimationFrame(attach); + }); + + return () => { + active = false; + cancelAnimationFrame(outerRaf); + cancelAnimationFrame(innerRaf); + removeListener(); + }; + }, [ + visible, + showHeaderBackdrop, + numericBackdrop, + headerBackdropHeight, + headerClientHeight, + children, + ]); + + useEffect(() => { + if (!visible || !showHeaderBackdrop) setFooterScrollOpacity(1); + }, [visible, showHeaderBackdrop]); + + const measureHeaderFooter = useCallback(() => { + setHeaderClientHeight(headerMeasureRef.current?.clientHeight ?? 0); + setFooterClientHeight(footerMeasureRef.current?.clientHeight ?? 0); + }, []); + + useLayoutEffect(() => { + if (!visible) return; + measureHeaderFooter(); + const headerEl = headerMeasureRef.current; + const footerEl = footerMeasureRef.current; + const ro = new ResizeObserver(() => measureHeaderFooter()); + if (headerEl) ro.observe(headerEl); + if (footerEl) ro.observe(footerEl); + return () => ro.disconnect(); + }, [visible, title, closable, footer != null, measureHeaderFooter]); + + const clearCloseTimer = useCallback(() => { + if (closeTimerRef.current != null) { + clearTimeout(closeTimerRef.current); + closeTimerRef.current = null; + } + }, []); + + useEffect(() => { + if (open) { + clearCloseTimer(); + setMounted(true); + const id = requestAnimationFrame(() => + setTimeout(() => setVisible(true), 10), + ); + return () => cancelAnimationFrame(id); + } + setVisible(false); + if (destroyOnClose) { + closeTimerRef.current = window.setTimeout(() => { + closeTimerRef.current = null; + setMounted(false); + }, TRANSITION_MS); + } + return () => clearCloseTimer(); + }, [open, destroyOnClose, clearCloseTimer]); + + useEffect(() => { + if (!visible) return; + pushAppMainDrawer(); + return () => popAppMainDrawer(); + }, [visible]); + + useEffect(() => { + if (!visible) return; + const prev = document.body.style.backgroundColor; + document.body.style.backgroundColor = "var(--surface)"; + return () => { + document.body.style.backgroundColor = prev; + }; + }, [visible]); + + useEffect(() => { + if (!visible || !onClose) return; + const onKeyDown = (e) => { + if (e.key === "Escape") onClose(); + }; + document.addEventListener("keydown", onKeyDown); + return () => document.removeEventListener("keydown", onKeyDown); + }, [visible, onClose]); + + useEffect(() => { + if (!visible) return; + + const clearAnimatedRevealTimers = () => { + animatedRevealTimeoutsRef.current.forEach((id) => clearTimeout(id)); + animatedRevealTimeoutsRef.current = []; + }; + + const stripVisibleFromAnimated = () => { + const root = scrollContentRef.current; + if (!root) return; + const animated = document.getElementsByClassName("animated"); + for (let i = 0; i < animated.length; i += 1) { + const el = animated[i]; + if (root.contains(el)) el.classList.remove("visible"); + } + }; + + const startId = window.setTimeout(() => { + const root = scrollContentRef.current; + if (!root) return; + const allAnimated = document.getElementsByClassName("animated"); + const inside = []; + for (let i = 0; i < allAnimated.length; i += 1) { + const el = allAnimated[i]; + if (root.contains(el)) inside.push(el); + } + for (let i = 0; i < inside.length; i += 1) { + const el = inside[i]; + el.style.zIndex = inside.length - i; + } + inside.forEach((el, index) => { + const id = window.setTimeout(() => { + el.classList.add("visible"); + }, index * 100); + animatedRevealTimeoutsRef.current.push(id); + }); + }, 10); + + return () => { + clearTimeout(startId); + clearAnimatedRevealTimers(); + stripVisibleFromAnimated(); + }; + }, [visible]); + + const onMaskClick = () => { + if (maskClosable) onClose?.(); + }; + + const w = typeof width === "number" ? `${width}px` : width; + const h = isMobile + ? "95dvh" + : typeof height === "number" + ? `${height}px` + : height; + const panelStyle = + effectivePlacement === "right" || effectivePlacement === "left" + ? { width: w } + : { height: h }; + + const backdropH = + typeof headerBackdropHeight === "number" + ? `${headerBackdropHeight}px` + : headerBackdropHeight; + + const backdropDisplayH = numericBackdrop + ? `${scrolledBackdropHeaderHeight}px` + : backdropH; + + const backdropLayoutPx = + showHeaderBackdrop && numericBackdrop + ? scrolledBackdropHeaderHeight + : typeof headerBackdropHeight === "number" + ? headerBackdropHeight + : parseFloat(String(headerBackdropHeight)) || 0; + + if (!mounted) return null; + + const wrapClass = `drawer-wrap drawer-wrap--${effectivePlacement}`; + const rootClass = cx( + "drawer-root", + visible && "drawer-root--visible", + visible && "drawer-root--active", + ); + + const node = ( +
+
+
+
+ {headerBackdrop != null && showHeaderBackdrop && ( +
+
+ {headerBackdrop} +
+
+ {headerBackdrop} +
+
+ )} + {(title != null || closable) && ( +
+ {title != null ? ( + typeof title === "string" ? ( +

+ {title} +

+ ) : ( +
+ {title} +
+ ) + ) : ( + + )} + {closable && ( + + )} +
+ )} + {showHeaderBackdrop && ( +
+ )} +
+
+
+
+ + +
+ {children} +
+
+
+
+ {footer != null && ( +
+ {footer} +
+ )} +
+
+
+ ); + + return createPortal(node, document.body); +} diff --git a/packages/client/src/components/Flex.css b/packages/client/src/components/Flex.css new file mode 100644 index 0000000..559abd2 --- /dev/null +++ b/packages/client/src/components/Flex.css @@ -0,0 +1,6 @@ +/* Base shell; direction, alignment, and gap come from inline style (Ant Design–style API). */ +.ta-flex { + display: flex; + min-width: 0; + box-sizing: border-box; +} diff --git a/packages/client/src/components/Flex.jsx b/packages/client/src/components/Flex.jsx new file mode 100644 index 0000000..756c8d0 --- /dev/null +++ b/packages/client/src/components/Flex.jsx @@ -0,0 +1,70 @@ +import "./Flex.css"; + +function mapJustifyAlign(value) { + if (value == null || value === "normal" || value === "stretch") return value; + if (value === "start") return "flex-start"; + if (value === "end") return "flex-end"; + return value; +} + +function gapToStyle(gap) { + if (gap == null) return undefined; + if (typeof gap === "number") return `${gap}px`; + if (Array.isArray(gap)) { + const [row, col] = gap; + const a = typeof row === "number" ? `${row}px` : row; + const b = col != null ? (typeof col === "number" ? `${col}px` : col) : a; + return `${a} ${b}`; + } + return gap; +} + +/** + * Flex container similar to Ant Design `Flex`: vertical, justify, align, gap, wrap. + * + * @param {object} props + * @param {boolean} [props.vertical] + * @param {boolean | 'wrap' | 'nowrap' | 'wrap-reverse'} [props.wrap] + * @param {import('react').CSSProperties['justifyContent'] | 'start' | 'end'} [props.justify] + * @param {import('react').CSSProperties['alignItems'] | 'start' | 'end'} [props.align] + * @param {number | string | [number | string, number | string]} [props.gap] + * @param {import('react').CSSProperties['flex']} [props.flex] + * @param {keyof JSX.IntrinsicElements} [props.component] + */ +export function Flex({ + vertical = false, + wrap = "nowrap", + justify = "normal", + align = "normal", + gap, + flex: flexProp, + component: Component = "div", + className = "", + style, + children, + ...rest +}) { + const flexWrap = + wrap === true ? "wrap" : wrap === false ? "nowrap" : wrap; + + const merged = { + flexDirection: vertical ? "column" : "row", + flexWrap, + justifyContent: + justify === "normal" ? undefined : mapJustifyAlign(justify), + alignItems: align === "normal" ? undefined : mapJustifyAlign(align), + gap: gapToStyle(gap), + flex: flexProp, + ...style, + }; + + return ( + + {children} + + ); +} diff --git a/packages/client/src/components/HistoryLineChart.css b/packages/client/src/components/HistoryLineChart.css new file mode 100644 index 0000000..ff45732 --- /dev/null +++ b/packages/client/src/components/HistoryLineChart.css @@ -0,0 +1,125 @@ +.history-line-chart { + display: flex; + flex-direction: column; + gap: 14px; + min-width: 0; +} + +.history-line-chart__toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.history-line-chart__period { + min-width: 200px; +} + +.history-line-chart__plot { + position: relative; + width: 100%; + min-width: 0; + /* Let --history-chart-height control size (drawer charts use small heights). */ + min-height: 240px; + height: var(--history-chart-height, 280px); + color: var(--muted); +} + +.history-line-chart__plot .recharts-wrapper { + max-width: 100%; +} + +.history-line-chart__x-tick { + font-family: var(--font); +} + +.history-line-chart__loading-overlay, +.history-line-chart__empty-overlay { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + pointer-events: none; + font-size: 14px; + color: var(--history-chart-muted, rgba(0, 0, 0, 0.55)); +} + +.history-line-chart__loading-overlay { + background: color-mix( + in srgb, + var(--history-chart-surface, #fff) 72%, + transparent + ); +} + +.history-line-chart__empty-overlay { + background: color-mix( + in srgb, + var(--history-chart-surface, #fff) 55%, + transparent + ); +} + +.history-line-chart__spinner { + width: 28px; + height: 28px; + border: 2px solid color-mix(in srgb, currentColor 22%, transparent); + border-top-color: currentColor; + border-radius: 50%; + animation: history-line-chart-spin 0.7s linear infinite; +} + +.history-line-chart__loading-label { + font-size: 13px; +} + +@keyframes history-line-chart-spin { + to { + transform: rotate(360deg); + } +} + +/* Match prior chart: slightly heavier grid lines */ +.history-line-chart__plot .recharts-cartesian-grid line { + stroke: var(--border); + stroke-width: 2px; +} + +.history-line-chart__state { + display: flex; + align-items: center; + justify-content: center; + min-height: var(--history-chart-height, 280px); + color: var(--history-chart-muted, rgba(0, 0, 0, 0.45)); + font-size: 14px; +} + +.history-line-chart__error { + color: var(--history-chart-error, #cf1322); +} + +/* Recharts portals the tooltip to document body */ +.recharts-tooltip-wrapper .history-line-chart__tooltip-panel { + font-family: var(--font); + font-size: 12px; + font-variant-numeric: tabular-nums; + line-height: 1.35; + color: var(--text); + background: var(--surface); + + border-radius: 8px; + padding: 8px 10px; + box-shadow: 0 10px 28px color-mix(in srgb, #000 45%, transparent); + white-space: nowrap; +} + +.recharts-wrapper, +*:focus-visible, +.recharts-wrapper, +*:focus-within { + outline: none; +} diff --git a/packages/client/src/components/HistoryLineChart.jsx b/packages/client/src/components/HistoryLineChart.jsx new file mode 100644 index 0000000..9a9f07a --- /dev/null +++ b/packages/client/src/components/HistoryLineChart.jsx @@ -0,0 +1,481 @@ +import React, { + useCallback, + useEffect, + useId, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + Area, + AreaChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; +import { useApi } from "../contexts/ApiContext.jsx"; +import { Select } from "./Select.jsx"; +import "./HistoryLineChart.css"; + +function parseHistoryPath(path) { + if (!path || typeof path !== "string") { + throw new Error("historyPath is required"); + } + const parts = path.trim().split(".").filter(Boolean); + if (parts.length < 2) { + throw new Error(`Invalid historyPath: ${path}`); + } + const root = parts[0].toLowerCase(); + + if (root === "patch") { + if (parts.length < 3) { + throw new Error("Expected patch.."); + } + return { + kind: "patch", + patchId: parts[1], + field: parts.slice(2).join("."), + }; + } + + if (root === "power" || root === "powerstate") { + const fieldParts = parts + .slice(1) + .map((p) => (p.toLowerCase() === "batter" ? "battery" : p)); + if (!fieldParts.length) { + throw new Error("Expected power."); + } + return { kind: "power", fieldParts }; + } + + if (root === "watertank") { + const fieldParts = parts.length > 1 ? parts.slice(1) : ["level"]; + return { kind: "waterTank", fieldParts }; + } + + throw new Error(`Unknown history path root: ${parts[0]}`); +} + +/** + * Series styling and tooltip unit by semantic property type (key = classifyHistoryPropertyType). + * Colors: temperature orange, humidity / level blue, default grey. + */ +const PROPERTY_TYPE_MAP = { + temperature: { color: "#ea580c", unit: "\u00B0C" }, + percent: { color: "#3CD3FE", unit: "%" }, + neutral: { color: "#9ca3af", unit: "" }, +}; + +function classifyHistoryPropertyType(path) { + if (!path || typeof path !== "string") return "neutral"; + const p = path.toLowerCase(); + if ( + p.includes("soiltemperature") || + p.includes("temperature") || + p.endsWith(".temp") + ) { + return "temperature"; + } + if ( + p.includes("soilhumidity") || + p.includes("humidity") || + p.includes(".level") || + p.endsWith("level") + ) { + return "percent"; + } + return "neutral"; +} + +function getHistorySeriesMeta(path, lineColorOverride) { + const key = classifyHistoryPropertyType(path); + const entry = PROPERTY_TYPE_MAP[key] ?? PROPERTY_TYPE_MAP.neutral; + return { + propertyType: key, + color: lineColorOverride ?? entry.color, + unit: entry.unit, + }; +} + +/** Safe display: formatted number + static unit suffix (no raw path / user strings). */ +function formatHistoryTooltipValue(raw, unitSuffix) { + const n = Number(raw); + if (!Number.isFinite(n)) return "\u2014"; + const formatted = new Intl.NumberFormat(undefined, { + maximumFractionDigits: 2, + minimumFractionDigits: 0, + }).format(n); + return unitSuffix ? `${formatted}${unitSuffix}` : formatted; +} + +function getNested(obj, pathParts) { + let x = obj; + for (const key of pathParts) { + x = x?.[key]; + } + return x; +} + +function rowsToChartPoints(rows, parsed) { + const out = []; + for (const row of rows) { + const t = new Date(row.sampledAt); + if (Number.isNaN(t.getTime())) continue; + + let raw; + if (parsed.kind === "power") { + raw = getNested(row.power, parsed.fieldParts); + } else if (parsed.kind === "waterTank") { + raw = getNested(row.waterTank, parsed.fieldParts); + } else { + raw = row[parsed.field]; + } + + const y = Number(raw); + if (!Number.isFinite(y)) continue; + out.push({ t, y }); + } + return out; +} + +const PERIODS = [ + { value: "30m", label: "Last 30 minutes", ms: 30 * 60 * 1000 }, + { value: "1h", label: "Last hour", ms: 60 * 60 * 1000 }, + { value: "4h", label: "Last 4 hours", ms: 4 * 60 * 60 * 1000 }, + { value: "8h", label: "Last 8 hours", ms: 8 * 60 * 60 * 1000 }, + { value: "12h", label: "Last 12 hours", ms: 12 * 60 * 60 * 1000 }, + { value: "1d", label: "Last day", ms: 24 * 60 * 60 * 1000 }, + { value: "1w", label: "Last week", ms: 7 * 24 * 60 * 60 * 1000 }, + { value: "1M", label: "Last month", ms: 30 * 24 * 60 * 60 * 1000 }, + { value: "1y", label: "Last year", ms: 365 * 24 * 60 * 60 * 1000 }, +]; + +function rangeForPeriod(periodValue) { + const p = PERIODS.find((x) => x.value === periodValue) ?? PERIODS[1]; + const end = new Date(); + const start = new Date(end.getTime() - p.ms); + return { start, end }; +} + +/** Bottom axis: time on first line, date on second (multi-line via tspans). */ +function formatPrimaryAxisTickTwoLines(periodValue, date) { + const entry = PERIODS.find((x) => x.value === periodValue) ?? PERIODS[1]; + const ms = entry.ms; + + if (ms >= 365 * 24 * 60 * 60 * 1000) { + const top = new Intl.DateTimeFormat(undefined, { month: "short" }).format( + date, + ); + const bottom = new Intl.DateTimeFormat(undefined, { + year: "numeric", + }).format(date); + return `${top}\n${bottom}`; + } + + const timeOpts = + ms <= 30 * 60 * 1000 + ? { hour: "numeric", minute: "2-digit", second: "2-digit" } + : { hour: "numeric", minute: "2-digit" }; + + const dateOpts = + ms <= 24 * 60 * 60 * 1000 + ? { weekday: "short", month: "short", day: "numeric" } + : { month: "short", day: "numeric", year: "numeric" }; + + const timeLine = new Intl.DateTimeFormat(undefined, timeOpts).format(date); + const dateLine = new Intl.DateTimeFormat(undefined, dateOpts).format(date); + return `${timeLine}\n${dateLine}`; +} + +function asDate(value) { + if (value instanceof Date) return value; + const d = new Date(value); + return Number.isNaN(d.getTime()) ? null : d; +} + +function HistoryTimeAxisTick({ x, y, payload, period }) { + const d = asDate(payload?.value); + if (!d) return null; + const lines = formatPrimaryAxisTickTwoLines(period, d) + .split("\n") + .map((l) => l.trim()) + .filter(Boolean); + return ( + + + {lines.map((line, i) => ( + + {line} + + ))} + + + ); +} + +function HistoryChartTooltip({ active, payload, unit }) { + if (!active || !payload?.length) return null; + const raw = payload[0]?.value; + return ( +
+ {formatHistoryTooltipValue(raw, unit)} +
+ ); +} + +function chartDataFromPoints(points) { + return points.map((p) => ({ t: p.t.getTime(), y: p.y })); +} + +/** Pixel size of an element; updates on resize (flex/drawer/scroll layouts need this for Recharts). */ +function useObservedSize(elementRef) { + const [size, setSize] = useState({ width: 0, height: 0 }); + useLayoutEffect(() => { + const el = elementRef.current; + if (!el) return undefined; + const measure = () => { + const r = el.getBoundingClientRect(); + setSize({ width: r.width, height: r.height }); + }; + measure(); + if (typeof ResizeObserver === "undefined") return undefined; + const ro = new ResizeObserver(measure); + ro.observe(el); + return () => ro.disconnect(); + }, []); + return size; +} + +/** + * @param {object} props + * @param {string} props.historyPath — e.g. `patch.${patchId}.soilHumidity`, `power.battery.voltage`, `waterTank.level` + * @param {string} [props.lineColor] — overrides automatic orange (temperature) / blue (humidity) / grey default + * @param {string} [props.className] + * @param {React.CSSProperties} [props.style] + * @param {number|string} [props.chartHeight=280] — number is treated as px; string passes through (e.g. `15vh`) + */ +export function HistoryLineChart({ + historyPath, + lineColor: lineColorProp, + className = "", + style, + chartHeight = "280px", +}) { + const { getHistory, isConnected } = useApi(); + const [period, setPeriod] = useState("1h"); + const [rows, setRows] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const parsed = useMemo(() => { + try { + return parseHistoryPath(historyPath); + } catch (e) { + return null; + } + }, [historyPath]); + + const parseError = useMemo(() => { + try { + parseHistoryPath(historyPath); + return null; + } catch (e) { + return e?.message || "Invalid history path"; + } + }, [historyPath]); + + const { color: lineColor, unit: valueUnit } = useMemo( + () => getHistorySeriesMeta(historyPath, lineColorProp), + [historyPath, lineColorProp], + ); + const fillGradientId = `hlc-area-${useId().replace(/:/g, "")}`; + const plotRef = useRef(null); + const plotSize = useObservedSize(plotRef); + + const load = useCallback(async () => { + if (!parsed) return; + const { start, end } = rangeForPeriod(period); + setLoading(true); + setError(null); + try { + const payload = { + kind: parsed.kind, + start: start.toISOString(), + end: end.toISOString(), + }; + if (parsed.kind === "patch") { + payload.patchId = parsed.patchId; + } + const data = await getHistory(payload); + setRows(Array.isArray(data) ? data : []); + } catch (e) { + setRows([]); + setError(e?.message || "Failed to load history"); + } finally { + setLoading(false); + } + }, [getHistory, parsed, period]); + + useEffect(() => { + if (!parsed || !isConnected) return; + load(); + }, [parsed, period, isConnected, load]); + + const points = useMemo(() => { + if (!parsed) return []; + return rowsToChartPoints(rows, parsed); + }, [rows, parsed]); + + const chartData = useMemo(() => chartDataFromPoints(points), [points]); + + const chartPixelW = Math.max(1, Math.floor(plotSize.width)); + const chartPixelH = Math.max(1, Math.floor(plotSize.height)); + const canRenderRecharts = + chartData.length > 0 && plotSize.width > 0 && plotSize.height > 0; + + const timeTickRenderer = useMemo( + () => (props) => , + [period], + ); + + const heightCss = + typeof chartHeight === "number" ? `${chartHeight}px` : chartHeight; + + const rootClass = ["history-line-chart", className].filter(Boolean).join(" "); + const plotStyle = { + ...style, + "--history-chart-height": heightCss, + }; + + if (parseError) { + return ( +
+
+ {parseError} +
+
+ ); + } + + return ( +
+
+ + {suffix || showClear ? ( + + {suffix} + {showClear ? ( + + ) : null} + + ) : null} + + ) : ( + + ); + + if (addonBefore != null || addonAfter != null) { + return ( + + {addonBefore != null ? ( + {addonBefore} + ) : null} + {inputEl} + {addonAfter != null ? ( + {addonAfter} + ) : null} + + ); + } + + return inputEl; +}); diff --git a/packages/client/src/components/Label.css b/packages/client/src/components/Label.css new file mode 100644 index 0000000..843b898 --- /dev/null +++ b/packages/client/src/components/Label.css @@ -0,0 +1,3 @@ +.label { + margin: 0 0 3px 0; +} diff --git a/packages/client/src/components/Label.jsx b/packages/client/src/components/Label.jsx new file mode 100644 index 0000000..82e6cba --- /dev/null +++ b/packages/client/src/components/Label.jsx @@ -0,0 +1,19 @@ +import "./Label.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * @param {object} props + * @param {import('react').ReactNode} props.label + * @param {'dt' | 'span' | 'label'} [props.as='dt'] + * @param {string} [props.className] + */ +export function Label({ label, as: Component = "dt", className, ...rest }) { + return ( + + {label} + + ); +} diff --git a/packages/client/src/components/LifecyclePhase.css b/packages/client/src/components/LifecyclePhase.css new file mode 100644 index 0000000..500a37d --- /dev/null +++ b/packages/client/src/components/LifecyclePhase.css @@ -0,0 +1,70 @@ +.lifecycle-phases-edit__row { + padding: 5px 0 5px 20px; +} + +.lifecycle-phases-edit__row-wrapper { + position: relative; + border-left: 2px solid + color-mix(in srgb, var(--border-ui-color) 70%, transparent); +} + +.lifecycle-phases-edit__phase-title { + font-size: 18px; + font-weight: 500; + line-height: 1.2; + margin: 0; + font-family: "Libre Baskerville", serif; +} + +.lifecycle-phases-edit__phase-timescale { + font-size: 18px; + font-weight: 400; + margin: 0; + line-height: 1; + font-style: italic; + font-family: "Libre Baskerville", serif; +} + +.lifecycle-phases-edit__remove { + position: absolute; + top: 1px; + right: 1px; +} + +.lifecycle-phases-edit__row-wrapper:last-of-type { + border-bottom: none; +} + +.lifecycle-phases-edit__configs { + width: 100%; +} + +.lifecycle-phases-edit__field { + flex: 1 1 7rem; + min-width: 0; +} + +.lifecycle-phases-edit__field--narrow { + flex-grow: 1; +} + +.lifecycle-phases--readonly .lifecycle-phases-edit__row-wrapper { + border-left: none; + flex-flow: row-reverse !important; + min-height: 91.5px; +} + +.lifecycle-phases--readonly .lifecycle-phases-edit__row { + padding: 0; + padding-left: 10px; + padding-bottom: 25px; +} + +.lifecycle-phases-edit__plant { + width: 130px; + margin-top: -5px; +} + +.lifecycle-phases--readonly .lifecycle-phases-edit__plant { + width: 90px; +} diff --git a/packages/client/src/components/LifecyclePhase.jsx b/packages/client/src/components/LifecyclePhase.jsx new file mode 100644 index 0000000..8b66472 --- /dev/null +++ b/packages/client/src/components/LifecyclePhase.jsx @@ -0,0 +1,371 @@ +import dayjs from "dayjs"; +import isoWeek from "dayjs/plugin/isoWeek"; +import utc from "dayjs/plugin/utc"; +import { useId } from "react"; +import { IoTrashOutline } from "react-icons/io5"; +import { Flex } from "./Flex.jsx"; +import { IconButton } from "./IconButton.jsx"; +import { Input } from "./Input.jsx"; +import { Label } from "./Label.jsx"; +import { Slider } from "./Slider.jsx"; +import { Plant } from "./allotment/Plant.jsx"; +import "./LifecyclePhase.css"; + +dayjs.extend(utc); +dayjs.extend(isoWeek); + +const MAX_CAL_WEEK = 52; +const MAX_PHASE_LEAVES = 6; +const MAX_PHASE_FLOWERS = 3; + +/** Jan 4 is always in ISO week 1; anchor a stable 0..52 grid for perennials. */ +const PERENNIAL_REF_YEAR = 2024; + +function perennialWeekGridStart() { + return dayjs.utc(`${PERENNIAL_REF_YEAR}-01-04`).startOf("isoWeek"); +} + +function dateAtPerennialWeekIndex(weekIndex) { + const w = Math.max( + 0, + Math.min(MAX_CAL_WEEK, Math.floor(Number(weekIndex)) || 0), + ); + return perennialWeekGridStart().add(w, "week"); +} + +function parsePlantedAtAnchor(raw) { + const s = typeof raw === "string" ? raw.trim() : ""; + if (!s) { + return dayjs.utc(`${PERENNIAL_REF_YEAR}-01-01T12:00:00Z`).startOf("day"); + } + if (/^\d{4}-\d{2}-\d{2}$/.test(s)) { + return dayjs.utc(`${s}T12:00:00Z`).startOf("day"); + } + const d = dayjs.utc(s); + return d.isValid() + ? d + : dayjs.utc(`${PERENNIAL_REF_YEAR}-01-01T12:00:00Z`).startOf("day"); +} + +/** + * @param {import('dayjs').Dayjs} d + */ +function earlyLateMonthLabel(d) { + if (!d?.isValid()) return ""; + const day = d.date(); + const month = d.format("MMMM"); + if (day <= 10) return `Early ${month}`; + if (day <= 20) return `Mid ${month}`; + return `Late ${month}`; +} + +/** + * @param {'biennial' | 'perennial'} variant + * @param {object} phase + * @param {number} weeks — span in weeks (same as the Weeks field) + * @param {string} [plantedAt] + */ +function phaseCalendarRangeLabel(variant, phase, weeks, plantedAt) { + const w = Math.max(0, Math.floor(Number(weeks)) || 0); + if (w <= 0) return null; + + let start; + let end; + + if (variant === "perennial") { + const startWeek = Math.floor(Number(phase?.startWeek) || 0); + start = dateAtPerennialWeekIndex(startWeek); + end = start.add(w, "week").subtract(1, "day"); + } else { + const base = parsePlantedAtAnchor(plantedAt); + const startOff = Math.floor(Number(phase?.startOffsetWeeks) || 0); + const endOff = Math.floor(Number(phase?.endOffsetWeeks) || 0); + start = base.add(startOff, "week"); + end = base.add(endOff, "week").subtract(1, "day"); + } + + if (!start.isValid() || !end.isValid() || end.isBefore(start)) return null; + + const left = earlyLateMonthLabel(start); + const right = earlyLateMonthLabel(end); + if (left && right && left === right) return left; + if (left && right) return `${left} – ${right}`; + return null; +} + +/** + * @param {object} props + * @param {number} props.phaseIndex + * @param {object} props.phase + * @param {'biennial' | 'perennial'} props.variant + * @param {number} props.weeks + * @param {string} props.nameFieldId + * @param {string} props.weeksFieldId + * @param {boolean} [props.editing=true] + * @param {boolean} [props.disabled] + * @param {boolean} props.removeDisabled + * @param {number|null|undefined} [props.soilHumidity] + * @param {string} [props.plantedAt] — YYYY-MM-DD or ISO; biennial calendar labels only + * @param {() => void} props.onRemove + * @param {(name: string) => void} props.onNameChange + * @param {(raw: string) => void} props.onWeeksChange + * @param {(n: number) => void} props.onGrowingPositionChange + * @param {(n: number) => void} props.onWitheringChange + * @param {(n: number) => void} props.onLeavesChange + * @param {(n: number) => void} props.onFlowersChange + * @param {(n: number) => void} props.onFruitingChange + */ +export function LifecyclePhase({ + phaseIndex, + phase, + variant, + weeks, + nameFieldId, + weeksFieldId, + editing = true, + disabled, + removeDisabled, + soilHumidity, + plantedAt, + produceIcon, + onRemove, + onNameChange, + onWeeksChange, + onGrowingPositionChange, + onWitheringChange, + onLeavesChange, + onFlowersChange, + onFruitingChange, +}) { + const sliderIds = useId(); + const growthLabelId = `${sliderIds}-growth`; + const witheringLabelId = `${sliderIds}-withering`; + const leavesLabelId = `${sliderIds}-leaves`; + const flowersLabelId = `${sliderIds}-flowers`; + const fruitingLabelId = `${sliderIds}-fruiting`; + const flowerCount = phase.flowers ?? 0; + + const timescaleLabel = phaseCalendarRangeLabel( + variant, + phase, + weeks, + plantedAt, + ); + + return ( + + {editing ? ( + + + + ) : null} + + + +

{phase.name}

+ {timescaleLabel ? ( +

+ {timescaleLabel} +

+ ) : null} +
+ {editing ? ( + <> + + + + + + + + + + + + + + + + + + + {flowerCount > 0 ? ( + + + + + ) : null} + + ) : null} +
+
+ +
+
+ ); +} diff --git a/packages/client/src/components/LifecyclePhases.css b/packages/client/src/components/LifecyclePhases.css new file mode 100644 index 0000000..a210b13 --- /dev/null +++ b/packages/client/src/components/LifecyclePhases.css @@ -0,0 +1,11 @@ +.lifecycle-phases-edit { + width: 100%; + display: flex; + flex-direction: column; + gap: 20px; +} + +.lifecycle-phases--readonly { + gap: 8px; + margin-left: -22px; +} diff --git a/packages/client/src/components/LifecyclePhases.jsx b/packages/client/src/components/LifecyclePhases.jsx new file mode 100644 index 0000000..bee9138 --- /dev/null +++ b/packages/client/src/components/LifecyclePhases.jsx @@ -0,0 +1,206 @@ +import { useId } from "react"; +import { Button } from "./Button.jsx"; +import { Flex } from "./Flex.jsx"; +import { LifecyclePhase } from "./LifecyclePhase.jsx"; +import "./LifecyclePhases.css"; + +const MAX_CAL_WEEK = 52; + +function biennialWeeksFromPhase(p) { + const s = p.startOffsetWeeks ?? 0; + const e = p.endOffsetWeeks ?? 0; + return Math.max(0, e - s); +} + +/** Span for display; when start > end, phase crosses the year in 0..52 space. */ +function perennialWeeksFromPhase(p) { + const s = p.startWeek ?? 0; + const e = p.endWeek ?? 0; + if (e >= s) return Math.max(0, e - s); + return Math.max(0, MAX_CAL_WEEK - s + e); +} + +function rebuildBiennialPhases(phases, weeksPerPhase) { + let cursor = 0; + return phases.map((p, i) => { + const w = Math.max(0, Math.floor(Number(weeksPerPhase[i]) || 0)); + const startOffsetWeeks = cursor; + const endOffsetWeeks = startOffsetWeeks + w; + cursor = endOffsetWeeks; + return { ...p, startOffsetWeeks, endOffsetWeeks }; + }); +} + +/** Sequential segments from week 0; each phase is [start, end) clamped to 0..52. */ +function rebuildPerennialPhases(phases, weeksPerPhase) { + let cursor = 0; + return phases.map((p, i) => { + const w = Math.max(0, Math.floor(Number(weeksPerPhase[i]) || 0)); + const startWeek = Math.min(MAX_CAL_WEEK, cursor); + const endWeek = Math.min(MAX_CAL_WEEK, startWeek + w); + cursor = endWeek; + return { ...p, startWeek, endWeek }; + }); +} + +/** + * @typedef {object} BiennialPhase + * @property {string} name + * @property {number} startOffsetWeeks + * @property {number} endOffsetWeeks + * @property {number} [growingPosition] — 0–100, Lottie growth stage + * @property {number} [withering] — 0–100, plant colour withering + * @property {number} [leaves] — 0–6, visible leaves on plant Lottie + * @property {number} [flowers] — 0–3, visible flowers on plant Lottie + * @property {number} [fruiting] — 0–100, fruit development when flowers > 0 + */ + +/** + * @typedef {object} PerennialPhase + * @property {string} name + * @property {number} startWeek + * @property {number} endWeek + * @property {number} [growingPosition] — 0–100 + * @property {number} [withering] — 0–100 + * @property {number} [leaves] — 0–6 + * @property {number} [flowers] — 0–3 + * @property {number} [fruiting] — 0–100 + */ + +/** + * @param {object} props + * @param {'biennial' | 'perennial'} props.variant + * @param {(BiennialPhase[] | PerennialPhase[])} props.phases + * @param {(next: BiennialPhase[] | PerennialPhase[]) => void} props.onChange + * @param {boolean} [props.editing=true] + * @param {boolean} [props.disabled] + * @param {string} [props.plantedAt] — YYYY-MM-DD or ISO; anchors biennial week offsets for calendar labels + */ +export function LifecyclePhases({ + variant, + phases, + onChange, + editing = true, + disabled, + soilHumidity, + plantedAt, + produceIcon, +}) { + const idPrefix = useId(); + const list = Array.isArray(phases) ? phases : []; + + const weeksRowForPhase = (p) => + variant === "biennial" + ? biennialWeeksFromPhase(p) + : perennialWeeksFromPhase(p); + + const addPhase = () => { + const weeks = list.map(weeksRowForPhase); + weeks.push(1); + const blank = + variant === "biennial" + ? { + name: "", + startOffsetWeeks: 0, + endOffsetWeeks: 1, + growingPosition: 0, + withering: 0, + leaves: 0, + flowers: 0, + fruiting: 0, + } + : { + name: "", + startWeek: 0, + endWeek: 1, + growingPosition: 0, + withering: 0, + leaves: 0, + flowers: 0, + fruiting: 0, + }; + const extended = [...list, blank]; + onChange( + variant === "biennial" + ? rebuildBiennialPhases(extended, weeks) + : rebuildPerennialPhases(extended, weeks), + ); + }; + + const removeAt = (index) => { + const weeks = list.map(weeksRowForPhase); + weeks.splice(index, 1); + const rest = list.filter((_, i) => i !== index); + onChange( + variant === "biennial" + ? rebuildBiennialPhases(rest, weeks) + : rebuildPerennialPhases(rest, weeks), + ); + }; + + const patchAt = (index, patch) => { + onChange(list.map((p, i) => (i === index ? { ...p, ...patch } : p))); + }; + + const setPhaseWeeks = (index, raw) => { + const n = parseInt(String(raw), 10); + const w = Number.isFinite(n) ? Math.max(0, n) : 0; + const weeks = list.map((p, i) => (i === index ? w : weeksRowForPhase(p))); + onChange( + variant === "biennial" + ? rebuildBiennialPhases(list, weeks) + : rebuildPerennialPhases(list, weeks), + ); + }; + + return ( + +
+ {list.map((phase, index) => ( + removeAt(index)} + onNameChange={(name) => patchAt(index, { name })} + onWeeksChange={(raw) => setPhaseWeeks(index, raw)} + onGrowingPositionChange={(n) => + patchAt(index, { growingPosition: n }) + } + onWitheringChange={(n) => patchAt(index, { withering: n })} + onLeavesChange={(n) => patchAt(index, { leaves: n })} + onFlowersChange={(n) => + patchAt( + index, + n === 0 ? { flowers: n, fruiting: 0 } : { flowers: n }, + ) + } + onFruitingChange={(n) => patchAt(index, { fruiting: n })} + /> + ))} +
+ {editing ? ( + + ) : null} +
+ ); +} diff --git a/packages/client/src/components/LoadingIcon.css b/packages/client/src/components/LoadingIcon.css new file mode 100644 index 0000000..8727d42 --- /dev/null +++ b/packages/client/src/components/LoadingIcon.css @@ -0,0 +1,19 @@ +.loading-icon { + display: inline-block; + width: 14px; + height: 14px; + animation: loading-spin 1s linear infinite; +} + +.loading-icon path { + fill: var(--loading-icon-fill, var(--text)); +} + +@keyframes loading-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/packages/client/src/components/LoadingIcon.jsx b/packages/client/src/components/LoadingIcon.jsx new file mode 100644 index 0000000..989b886 --- /dev/null +++ b/packages/client/src/components/LoadingIcon.jsx @@ -0,0 +1,21 @@ +import LoadingSvg from "../../assets/svg/Loading.svg?react"; +import "./LoadingIcon.css"; + +export function LoadingIcon({ className, size, color, style, ...props }) { + const mergedStyle = { + ...(size ? { width: size, height: size } : {}), + ...(color ? { "--loading-icon-fill": color } : {}), + ...style, + }; + function joinClassNames(...values) { + return values.filter(Boolean).join(" "); + } + + return ( + + ); +} diff --git a/packages/client/src/components/Metric.jsx b/packages/client/src/components/Metric.jsx new file mode 100644 index 0000000..af5e5f3 --- /dev/null +++ b/packages/client/src/components/Metric.jsx @@ -0,0 +1,10 @@ +import React from "react"; + +export function Metric({ title, children, className = "" }) { + return ( +
+ {title} + {children} +
+ ); +} diff --git a/packages/client/src/components/Metrics.css b/packages/client/src/components/Metrics.css new file mode 100644 index 0000000..54d219f --- /dev/null +++ b/packages/client/src/components/Metrics.css @@ -0,0 +1,5 @@ +.metrics.metrics--vertical > * { + flex: 0 0 auto; + width: 100%; + min-width: 0; +} diff --git a/packages/client/src/components/Metrics.jsx b/packages/client/src/components/Metrics.jsx new file mode 100644 index 0000000..3153506 --- /dev/null +++ b/packages/client/src/components/Metrics.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import { Flex } from "./Flex.jsx"; +import "./Metrics.css"; + +/** Container for {@link Metric} children. `layout` controls row vs column flex direction. */ +export function Metrics({ + layout = "horizontal", + gap = "1rem", + wrap, + children, + className = "", + style, + ...rest +}) { + const vertical = layout === "vertical"; + const mergedClass = [ + "metrics", + vertical ? "metrics--vertical" : "metrics--horizontal", + className, + ] + .filter(Boolean) + .join(" "); + + return ( + + {children} + + ); +} diff --git a/packages/client/src/components/PageScroll.css b/packages/client/src/components/PageScroll.css new file mode 100644 index 0000000..8f50427 --- /dev/null +++ b/packages/client/src/components/PageScroll.css @@ -0,0 +1,175 @@ +.page-scroll { + flex: 1; + min-height: 0; + position: relative; + width: 100%; +} + +.page-scroll-sticky { + position: absolute; + top: 0; + z-index: 4; + flex-shrink: 0; + width: 100%; + box-sizing: border-box; + padding: var(--app-main-padding-top) var(--app-main-padding-right) 0 + calc(var(--sidebar-width) + var(--app-main-padding-left)); +} + +.page-scroll-sticky .page-title { + text-shadow: 0 0 20px var(--bg); + font-size: 38px; + font-family: "Libre Baskerville", serif; +} + +.page-scroll-wrapper { + position: absolute; + flex: 1; + min-height: 0; + + display: flex; + flex-direction: column; + top: var(--app-main-padding-top); + left: 0; + right: 0; + bottom: 0; +} + +.page-scroll-top-gradient { + position: absolute; + inset: 0; + top: -50px; + left: 0; + right: 0; + z-index: 2; + opacity: 1; + pointer-events: none; + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255) 50%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to bottom, + rgb(255 255 255) 0%, + rgb(255 255 255) 50%, + rgb(255 255 255 / 0%) 100% + ); +} + +.page-scroll-bottom-gradient { + position: absolute; + top: unset; + bottom: 0; + left: 0; + right: 0; + z-index: 2; + opacity: 1; + pointer-events: none; + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + mask-image: linear-gradient( + to top, + rgb(255 255 255) 0%, + rgb(255 255 255) 25%, + rgb(255 255 255 / 0%) 100% + ); + -webkit-mask-image: linear-gradient( + to top, + rgb(255 255 255) 0%, + rgb(255 255 255) 25%, + rgb(255 255 255 / 0%) 100% + ); +} + +.page-scroll-vscroll { + flex: 1; + min-height: 0; + width: 100%; + max-height: none; + height: auto; + margin: 0; + padding: 0; + mask: linear-gradient( + 180deg, + transparent 0%, + black var(--page-scroll-top-gradient-height, 50px), + black var(--page-scroll-bottom-gradient-height, 50px), + transparent 100% + ); + -webkit-mask: linear-gradient( + 180deg, + transparent 0%, + black var(--page-scroll-top-gradient-height, 50px), + black var(--page-scroll-bottom-gradient-height, 50px), + transparent 100% + ); +} + +.page-scroll-vscroll .simplebar-vertical { + margin-left: 0.35rem; + width: var(--border-ui-width); + border-radius: 18px; + overflow: hidden; + opacity: 0; + transition: opacity 0.3s ease; + margin-top: var(--simplebar-track-margin-top, 0); + margin-bottom: var(--app-main-padding-bottom); + margin-right: calc(var(--app-main-padding-right) / 2); +} + +.page-scroll-vscroll::before { + content: ""; + position: absolute; + right: 0; + top: 0; + bottom: 0; + z-index: 1; + opacity: 0; + transition: opacity 0.3s ease; + width: var(--border-ui-width); + border-radius: 18px; +} + +.page-scroll-vscroll.simplebar-scrolling::before { + opacity: 1; +} + +.page-scroll-vscroll.simplebar-scrolling .simplebar-vertical { + opacity: 1; +} + +.page-scroll-content { + display: flex; + flex-direction: column; + gap: 1.25rem; + box-sizing: border-box; + padding: var(--simplebar-track-margin-top, 50px) var(--app-main-padding-right) + 2rem calc(var(--sidebar-width) + var(--app-main-padding-left)); + min-height: 100%; + margin: 0; + margin-bottom: var(--app-main-padding-bottom); +} + +.page-scroll-start { + scroll-snap-align: start; + scroll-snap-stop: normal; + display: block; +} + +.page-scroll-vscroll .simplebar-scrollbar::before { + top: 0; + bottom: 0; + left: 0; + right: 0; + background: var(--border-ui-color); + border-radius: 18px; +} + +.page-scroll-vscroll .simplebar-scrollbar.simplebar-visible, +.page-scroll-vscroll .simplebar-scrollbar.simplebar-visible::before { + opacity: 1; +} diff --git a/packages/client/src/components/PageScroll.jsx b/packages/client/src/components/PageScroll.jsx new file mode 100644 index 0000000..e368ff4 --- /dev/null +++ b/packages/client/src/components/PageScroll.jsx @@ -0,0 +1,88 @@ +import { useEffect, useRef, useState } from "react"; +import SimpleBar from "simplebar-react"; +import "simplebar-react/dist/simplebar.min.css"; +import "./PageScroll.css"; +import AnimatedText from "./AnimatedText.jsx"; + +const GRADIENT_HEIGHT = 80; + +export function PageScroll({ + title, + children, + className = "", + scrollable = true, +}) { + const simpleBarRef = useRef(null); + const [isScrollAtTop, setIsScrollAtTop] = useState(true); + + useEffect(() => { + const instance = simpleBarRef.current; + if (!instance) return; + + const handleScroll = () => { + const scrollElement = instance.getScrollElement(); + if (scrollElement) { + setIsScrollAtTop(scrollElement.scrollTop <= 0); + } + }; + + const scrollElement = instance.getScrollElement(); + if (!scrollElement) return; + + scrollElement.addEventListener("scroll", handleScroll); + handleScroll(); + + const resizeObserver = new ResizeObserver(() => { + handleScroll(); + }); + resizeObserver.observe(scrollElement); + + return () => { + scrollElement.removeEventListener("scroll", handleScroll); + resizeObserver.disconnect(); + }; + }, []); + + const wrapperStyle = { + marginTop: `${30}px`, + "--simplebar-track-margin-top": `${GRADIENT_HEIGHT}px`, + "--page-scroll-top-gradient-height": `${GRADIENT_HEIGHT}px`, + "--page-scroll-bottom-gradient-height": `calc(100% - ${GRADIENT_HEIGHT}px)`, + }; + + return ( +
+
+
+ +
+
+
+ {scrollable && ( + <> +
+
+ + )} + {scrollable ? ( + + +
{children}
+
+ ) : ( +
{children}
+ )} +
+
+ ); +} diff --git a/packages/client/src/components/PlantGrid.css b/packages/client/src/components/PlantGrid.css new file mode 100644 index 0000000..634dc4e --- /dev/null +++ b/packages/client/src/components/PlantGrid.css @@ -0,0 +1,118 @@ +.plant-grid { + display: flex; + flex-direction: column; + gap: 8px; + align-items: stretch; + width: 100%; + min-width: 0; +} + +.plant-grid__controls { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + gap: 10px; + min-width: 0; +} + +.plant-grid__col-controls, +.plant-grid__row-controls { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 3px; + min-width: 0; +} + +.plant-grid__col-controls .icon-btn > svg, +.plant-grid__row-controls .icon-btn > svg { + margin-right: 0; +} + +.plant-grid__stepper-value { + font-variant-numeric: tabular-nums; + font-weight: 600; + font-size: 0.95rem; + min-width: 14px; + text-align: center; + color: var(--text); +} + +.plant-grid__matrix-wrap { + min-height: 68px; + min-width: 0; + border: none; + padding: 16px 10px 0 10px; + overflow: visible; + display: flex; + align-items: center; +} + +.plant-grid__matrix-wrap.single-row { + margin-top: 0; +} + +.plant-grid__matrix-wrap.single-column { + margin-left: 0; +} + +.plant-grid__matrix-host { + position: relative; + width: 100%; + min-height: 50px; + background: color-mix(in srgb, var(--surface) 40%, var(--bg)); +} + +.plant-grid__matrix-wrap.single-row .plant-grid__matrix-host { + min-height: 0; + height: 2px; +} + +.plant-grid__matrix-wrap.single-column .plant-grid__matrix-host { + min-width: 0; + width: 2px; +} + +.plant-grid__matrix { + width: 100%; + min-height: 50px; + position: relative; + z-index: 0; + border-top: var(--border-ui-width) solid var(--border-ui-color); + border-left: var(--border-ui-width) solid var(--border-ui-color); +} + +.plant-grid__matrix-wrap.single-row .plant-grid__matrix { + border-left: none; +} + +.plant-grid__matrix-wrap.single-column .plant-grid__matrix { + border-top: none; +} + +.plant-grid__intersections { + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + margin: 1px; +} + +.plant-grid__intersection-icon { + position: absolute; + color: var(--accent); + width: 20px; + height: 20px; + flex-shrink: 0; + transform: translate(-50%, -100%); + margin-top: 2px; + /* horizontal center on intersection; bottom of icon sits on the intersection */ +} + +.plant-grid__cell { + min-width: 0; + min-height: 0; +} diff --git a/packages/client/src/components/PlantGrid.jsx b/packages/client/src/components/PlantGrid.jsx new file mode 100644 index 0000000..08d63e7 --- /dev/null +++ b/packages/client/src/components/PlantGrid.jsx @@ -0,0 +1,154 @@ +import { MdAdd, MdRemove } from "react-icons/md"; +import { PiPlantFill } from "react-icons/pi"; +import { Label } from "./Label.jsx"; +import { IconButton } from "./IconButton.jsx"; +import "./PlantGrid.css"; + +function clamp(n, min, max) { + return Math.min(max, Math.max(min, n)); +} + +/** + * Visual rows×columns grid with button-style lines, plant icons at line intersections, and steppers. + * + * @param {object} props + * @param {number} props.rows + * @param {number} props.columns + * @param {(n: number) => void} props.onRowsChange + * @param {(n: number) => void} props.onColumnsChange + * @param {number} [props.min] + * @param {number} [props.max] + * @param {boolean} [props.disabled] + */ +export function PlantGrid({ + rows, + columns, + onRowsChange, + onColumnsChange, + min = 1, + max = 24, + disabled = false, +}) { + const r = clamp(Math.round(Number(rows)) || min, min, max); + const c = clamp(Math.round(Number(columns)) || min, min, max); + + const singleRow = r === 1; + const singleColumn = c === 1; + + const edge = "var(--border-ui-width) solid var(--border-ui-color)"; + const cellRows = Math.max(0, r - 1); + const cellCols = Math.max(0, c - 1); + const cells = []; + for (let row = 0; row < cellRows; row++) { + for (let col = 0; col < cellCols; col++) { + cells.push( +
, + ); + } + } + + const intersectionIcons = []; + for (let j = 0; j <= r - 1; j++) { + for (let i = 0; i <= c - 1; i++) { + // Align to grid-line crossings: c columns ⇒ c lines at 0 … 100%, not c equal bands. + const leftPct = c <= 1 ? 50 : (i / (c - 1)) * 100; + const topPct = r <= 1 ? 50 : (j / (r - 1)) * 100; + intersectionIcons.push( + , + ); + } + } + + return ( +
+
+
+
{intersectionIcons}
+
+ {cells} +
+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/packages/client/src/components/Popover.css b/packages/client/src/components/Popover.css new file mode 100644 index 0000000..4df23b2 --- /dev/null +++ b/packages/client/src/components/Popover.css @@ -0,0 +1,200 @@ +.popover-wrapper { + position: relative; + display: inline-block; +} + +.popover-root { + position: absolute; + z-index: 1050; + border-radius: 25px; + box-shadow: + 0 6px 35px 0 rgba(0, 0, 0, 0.3), + 0 3px 6px -4px rgba(0, 0, 0, 0.6); + display: flex; + flex-direction: column; + opacity: 0; + visibility: hidden; + transition: + opacity 0.2s ease, + transform 0.2s ease, + visibility 0.2s; + min-width: 177px; + max-width: min(360px, calc(100vw - 24px)); + box-sizing: border-box; +} + +.popover-root::before { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + background: color-mix(in srgb, var(--surface) 72%, transparent); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); + border-radius: 25px; +} + +.drawer-root .popover-root::before { + background: var(--surface); + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +.popover-root.popover-open { + opacity: 1; + visibility: visible; +} + +.popover-inner { + margin: 5px; + border-radius: 9px; + padding: 4px 4px; +} + +.popover-title { + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px solid var(--border-ui-color); + color: var(--text); + font-size: 14px; + font-weight: 500; + line-height: 1.5; +} + +.popover-inner-content { + color: color-mix(in srgb, var(--text) 85%, transparent); + font-size: 14px; + line-height: 1.5; +} + +.popover-top { + bottom: 100%; + left: 50%; + transform: translateX(-50%) translateY(8px); + margin-bottom: 10px; +} + +.popover-top.popover-open { + transform: translateX(-50%) translateY(0); +} + +.popover-topLeft { + bottom: 100%; + left: 0; + transform: translateY(8px); + margin-bottom: 10px; +} + +.popover-topLeft.popover-open { + transform: translateY(0); +} + +.popover-topRight { + bottom: 100%; + right: 0; + transform: translateY(8px); + margin-bottom: 10px; +} + +.popover-topRight.popover-open { + transform: translateY(0); +} + +.popover-bottom { + top: 100%; + left: 50%; + transform: translateX(-50%) translateY(-8px); + margin-top: 10px; +} + +.popover-bottom.popover-open { + transform: translateX(-50%) translateY(0); +} + +.popover-bottomLeft { + top: 100%; + left: 0; + transform: translateY(-8px); + margin-top: 10px; +} + +.popover-bottomLeft.popover-open { + transform: translateY(0); +} + +.popover-bottomRight { + top: 100%; + right: 0; + transform: translateY(-8px); + margin-top: 10px; +} + +.popover-bottomRight.popover-open { + transform: translateY(0); +} + +.popover-left { + right: 100%; + top: 50%; + transform: translateY(-50%) translateX(8px); + margin-right: 10px; +} + +.popover-left.popover-open { + transform: translateY(-50%) translateX(0); +} + +.popover-leftTop { + right: 100%; + top: 0; + transform: translateX(8px); + margin-right: 10px; +} + +.popover-leftTop.popover-open { + transform: translateX(0); +} + +.popover-leftBottom { + right: 100%; + bottom: 0; + transform: translateX(8px); + margin-right: 10px; +} + +.popover-leftBottom.popover-open { + transform: translateX(0); +} + +.popover-right { + left: 100%; + top: 50%; + transform: translateY(-50%) translateX(-8px); + margin-left: 10px; +} + +.popover-right.popover-open { + transform: translateY(-50%) translateX(0); +} + +.popover-rightTop { + left: 100%; + top: 0; + transform: translateX(-8px); + margin-left: 10px; +} + +.popover-rightTop.popover-open { + transform: translateX(0); +} + +.popover-rightBottom { + left: 100%; + bottom: 0; + transform: translateX(-8px); + margin-left: 10px; +} + +.popover-rightBottom.popover-open { + transform: translateX(0); +} diff --git a/packages/client/src/components/Popover.jsx b/packages/client/src/components/Popover.jsx new file mode 100644 index 0000000..2337ab8 --- /dev/null +++ b/packages/client/src/components/Popover.jsx @@ -0,0 +1,207 @@ +import { + useState, + useRef, + useEffect, + useCallback, + cloneElement, +} from "react"; +import "./Popover.css"; + +/** + * @param {object} props + * @param {import('react').ReactElement} props.children — single element; receives trigger handlers and ref + * @param {import('react').ReactNode} props.content + * @param {import('react').ReactNode} [props.title] + * @param {string | string[]} [props.trigger] + * @param {string} [props.placement] + * @param {boolean} [props.disabled] + * @param {boolean} [props.open] — controlled (Ant-style); falls back to `visible` + * @param {boolean} [props.visible] — controlled (alias) + * @param {(open: boolean) => void} [props.onOpenChange] + * @param {(visible: boolean) => void} [props.onVisibleChange] + * @param {string} [props.className] + * @param {string} [props.overlayClassName] + * @param {boolean} [props.defaultOpen] + */ +export function Popover({ + children, + content, + title, + trigger = ["hover"], + placement = "top", + disabled = false, + open: openProp, + visible: visibleProp, + onOpenChange, + onVisibleChange, + className = "", + overlayClassName = "", + defaultOpen = false, + ...props +}) { + const controlledValue = + openProp !== undefined ? openProp : visibleProp; + const isControlled = controlledValue !== undefined; + const [internalVisible, setInternalVisible] = useState(defaultOpen); + const visible = isControlled ? controlledValue : internalVisible; + + const setVisible = useCallback( + (next) => { + if (!isControlled) setInternalVisible(next); + onOpenChange?.(next); + onVisibleChange?.(next); + }, + [isControlled, onOpenChange, onVisibleChange], + ); + + const triggers = Array.isArray(trigger) ? trigger : [trigger]; + const wrapperRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event) => { + if ( + wrapperRef.current && + !wrapperRef.current.contains(event.target) + ) { + setVisible(false); + } + }; + + if (visible && triggers.includes("click")) { + document.addEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [visible, triggers, setVisible]); + + const handleMouseEnter = useCallback(() => { + if (!disabled && triggers.includes("hover")) { + setVisible(true); + } + }, [disabled, triggers, setVisible]); + + const handleMouseLeave = useCallback(() => { + if (triggers.includes("hover")) { + setVisible(false); + } + }, [triggers, setVisible]); + + const handleClick = useCallback( + (event) => { + if (disabled) return; + + if (triggers.includes("click")) { + event.preventDefault(); + event.stopPropagation(); + setVisible(!visible); + } + }, + [disabled, triggers, visible, setVisible], + ); + + const handleFocus = useCallback(() => { + if (!disabled && triggers.includes("focus")) { + setVisible(true); + } + }, [disabled, triggers, setVisible]); + + const handleBlur = useCallback(() => { + if (triggers.includes("focus")) { + setVisible(false); + } + }, [triggers, setVisible]); + + const handleContextMenu = useCallback( + (event) => { + if (disabled) return; + + if (triggers.includes("contextMenu")) { + event.preventDefault(); + setVisible(true); + } + }, + [disabled, triggers, setVisible], + ); + + const handleKeyDown = useCallback( + (event) => { + if (disabled) return; + + if (event.key === "Escape") { + setVisible(false); + } + }, + [disabled, setVisible], + ); + + const triggerElement = cloneElement(children, { + ref: (node) => { + const r = children.ref; + if (typeof r === "function") r(node); + else if (r) r.current = node; + }, + onClick: (e) => { + handleClick(e); + children.props?.onClick?.(e); + }, + onMouseEnter: (e) => { + handleMouseEnter(); + children.props?.onMouseEnter?.(e); + }, + onMouseLeave: (e) => { + handleMouseLeave(); + children.props?.onMouseLeave?.(e); + }, + onFocus: (e) => { + handleFocus(); + children.props?.onFocus?.(e); + }, + onBlur: (e) => { + handleBlur(); + children.props?.onBlur?.(e); + }, + onContextMenu: (e) => { + handleContextMenu(e); + children.props?.onContextMenu?.(e); + }, + onKeyDown: (e) => { + handleKeyDown(e); + children.props?.onKeyDown?.(e); + }, + "aria-haspopup": "true", + "aria-expanded": visible, + }); + + const placementClass = `popover-${placement}`; + + return ( +
+ {triggerElement} +
+
+ {title ?
{title}
: null} +
{content}
+
+
+
+ ); +} + +Popover.displayName = "Popover"; diff --git a/packages/client/src/components/ProduceIcon.jsx b/packages/client/src/components/ProduceIcon.jsx new file mode 100644 index 0000000..70da8f1 --- /dev/null +++ b/packages/client/src/components/ProduceIcon.jsx @@ -0,0 +1,66 @@ +import AppleSvg from "../../assets/svg/produce/apple.svg?react"; +import BeetSvg from "../../assets/svg/produce/beet.svg?react"; +import CabbageSvg from "../../assets/svg/produce/cabbage.svg?react"; +import CarrotSvg from "../../assets/svg/produce/carrot.svg?react"; +import CherrySvg from "../../assets/svg/produce/cherry.svg?react"; +import CornSvg from "../../assets/svg/produce/corn.svg?react"; +import CucumberSvg from "../../assets/svg/produce/cucumber.svg?react"; +import KiwiSvg from "../../assets/svg/produce/kiwi.svg?react"; +import LeekSvg from "../../assets/svg/produce/leek.svg?react"; +import LettuceSvg from "../../assets/svg/produce/lettuce.svg?react"; +import PeasSvg from "../../assets/svg/produce/peas.svg?react"; +import PlumSvg from "../../assets/svg/produce/plum.svg?react"; +import PotatoSvg from "../../assets/svg/produce/potato.svg?react"; +import RadishSvg from "../../assets/svg/produce/radish.svg?react"; +import RaspberrySvg from "../../assets/svg/produce/raspberry.svg?react"; +import StrawberrySvg from "../../assets/svg/produce/strawberry.svg?react"; +import TomatoSvg from "../../assets/svg/produce/tomato.svg?react"; +import { DEFAULT_PRODUCE_ICON, PRODUCE_ICON_IDS } from "./produceIconIds.js"; + +const PRODUCE_SVGS = { + apple: AppleSvg, + beet: BeetSvg, + cabbage: CabbageSvg, + carrot: CarrotSvg, + cherry: CherrySvg, + corn: CornSvg, + cucumber: CucumberSvg, + kiwi: KiwiSvg, + leek: LeekSvg, + lettuce: LettuceSvg, + peas: PeasSvg, + plum: PlumSvg, + potato: PotatoSvg, + radish: RadishSvg, + raspberry: RaspberrySvg, + strawberry: StrawberrySvg, + tomato: TomatoSvg, +}; + +/** + * @param {object} props + * @param {string} [props.icon] — id matching `produceIconIds` (e.g. `tomato`) + * @param {string} [props.className] + * @param {import('react').CSSProperties} [props.style] + * @param {string} [props.title] + * @param {string} [props.role] + */ +export function ProduceIcon({ icon, className, style, title, role, ...rest }) { + const id = + typeof icon === "string" && PRODUCE_ICON_IDS.includes(icon.trim()) + ? icon.trim() + : DEFAULT_PRODUCE_ICON; + const Svg = PRODUCE_SVGS[id] ?? PRODUCE_SVGS[DEFAULT_PRODUCE_ICON]; + return ( + + + + ); +} diff --git a/packages/client/src/components/ProduceIconSelect.css b/packages/client/src/components/ProduceIconSelect.css new file mode 100644 index 0000000..b9ad1a5 --- /dev/null +++ b/packages/client/src/components/ProduceIconSelect.css @@ -0,0 +1,86 @@ +.produce-icon-select { + display: flex; + align-items: flex-start; +} + +.produce-icon-select__anchor { + display: inline-flex; + line-height: 0; +} + +/* 64×64 icon button: themed like .btn, same border-radius as .btn (18px) */ +.produce-icon-select__trigger.btn { + width: 95px; + height: 95px; + min-height: 64px; + min-width: 64px; + padding: 0; + border-radius: 18px; + background-color: var(--bg); +} + +.produce-icon-select__trigger.btn:hover { + background-color: var(--bg); + border-color: color-mix(in srgb, var(--accent) 45%, var(--border-ui-color)); +} + +.produce-icon-select__trigger .produce-icon__svg { + width: 58px; + height: 58px; + display: block; +} + +.produce-icon-select__grid { + display: grid; + grid-template-columns: repeat(6, 44px); + gap: 8px; + padding: 2px; +} + +.produce-icon-select__option { + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + margin: 0; + padding: 0; + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 18px; + background: transparent; + color: var(--text); + cursor: pointer; + transition: + background-color 0.3s ease, + color 0.3s ease, + border-color 0.3s ease, + transform 0.2s ease; +} + +.produce-icon-select__option .produce-icon__svg { + width: 28px; + height: 28px; + display: block; +} + +.produce-icon-select__option:hover { + background-color: var(--border-ui-color); + color: var(--bg); + transform: scale(1.03); +} + +.produce-icon-select__option:active { + transform: scale(0.98); +} + +.produce-icon-select__option--selected { + border-color: var(--accent); + box-shadow: 0 0 0 1px var(--accent); +} + +.produce-icon-select__option--selected:hover { + border-color: var(--accent); +} diff --git a/packages/client/src/components/ProduceIconSelect.jsx b/packages/client/src/components/ProduceIconSelect.jsx new file mode 100644 index 0000000..6a7cc74 --- /dev/null +++ b/packages/client/src/components/ProduceIconSelect.jsx @@ -0,0 +1,80 @@ +import { useState } from "react"; +import { Button } from "./Button.jsx"; +import { Popover } from "./Popover.jsx"; +import { ProduceIcon } from "./ProduceIcon.jsx"; +import { DEFAULT_PRODUCE_ICON, PRODUCE_ICON_IDS } from "./produceIconIds.js"; +import "./ProduceIconSelect.css"; + +function optionLabel(id) { + return id.charAt(0).toUpperCase() + id.slice(1); +} + +/** + * @param {object} props + * @param {string} props.value + * @param {(next: string) => void} props.onChange + * @param {boolean} [props.disabled] + * @param {string} [props.id] + */ +export function ProduceIconSelect({ value, onChange, disabled, id }) { + const [open, setOpen] = useState(false); + const safe = + typeof value === "string" && PRODUCE_ICON_IDS.includes(value) + ? value + : DEFAULT_PRODUCE_ICON; + + const content = ( +
+ {PRODUCE_ICON_IDS.map((iconId) => ( + + ))} +
+ ); + + return ( +
+ + + + + +
+ ); +} diff --git a/packages/client/src/components/Select.css b/packages/client/src/components/Select.css new file mode 100644 index 0000000..77e507e --- /dev/null +++ b/packages/client/src/components/Select.css @@ -0,0 +1,219 @@ +/* Custom select — behaviour from HomePanel Select; visuals aligned with Input / theme tokens */ + +.select-wrap { + position: relative; + display: inline-block; + width: 100%; +} + +.select-trigger { + box-sizing: border-box; + margin: 0; + padding: 8px 12px 8px 14px; + color: var(--text); + font-family: inherit; + font-size: 0.9rem; + line-height: 1; + font-weight: 500; + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; + min-width: 0; + min-height: 30px; + background: var(--bg); + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 18px; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; + cursor: pointer; + text-align: left; +} + +.select-trigger:hover:not(:disabled) { + border-color: color-mix(in srgb, var(--accent) 45%, var(--border-ui-color)); +} + +.select-trigger:focus-visible { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); +} + +.select-trigger:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.select-trigger--open { + border-color: var(--accent); +} + +.select-value { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.select-search-input { + width: 100%; + background: transparent; + border: none; + color: var(--text); + font-family: inherit; + font-size: 0.9rem; + line-height: 1; + font-weight: 500; + padding: 0; + margin: 0; + outline: none; +} + +.select-search-input::placeholder { + color: var(--muted); + opacity: 1; +} + +.select-placeholder { + color: var(--muted); +} + +.select-chevron { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 18px; + height: 18px; + color: var(--muted); + transition: transform 0.2s ease; +} + +.select-trigger--open .select-chevron { + transform: rotate(180deg); +} + +.select-dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 1050; + margin-top: 6px; + border-radius: 18px; + background: var(--surface); + color: var(--text); + box-shadow: 0 8px 24px color-mix(in srgb, #000 22%, transparent); + max-height: 256px; + display: flex; + flex-direction: column; + overflow: hidden; + opacity: 0; + transform: translateY(-6px); + visibility: hidden; + transition: + opacity 0.2s ease, + transform 0.2s ease, + visibility 0.2s; +} + +.drawer-panel .select-dropdown { + z-index: 1500; +} + +.select-dropdown--open { + opacity: 1; + transform: translateY(0); + visibility: visible; +} + +.select-options { + flex: 1; + min-height: 0; + margin: 6px; + max-height: 220px; + border-radius: 12px; +} + +.select-options .simplebar-scrollbar::before { + background: color-mix(in srgb, var(--muted) 55%, var(--border-ui-color)); + border-radius: 4px; +} + +.select-options .simplebar-scrollbar.simplebar-visible::before { + opacity: 1; +} + +.select-options:hover .simplebar-scrollbar::before { + background: color-mix(in srgb, var(--muted) 75%, var(--text)); +} + +.select-options .simplebar-track.simplebar-vertical { + width: 8px; + right: 2px; +} + +.select-options .simplebar-content-wrapper { + border-radius: 10px; +} + +.select-option { + display: flex; + align-items: center; + padding: 8px 12px; + color: var(--text); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: background 0.15s ease; + gap: 10px; + border-radius: 12px; + margin-bottom: 4px; +} + +.select-option:last-child { + margin-bottom: 0; +} + +.select-option:hover:not(.select-option--disabled) { + background: color-mix(in srgb, var(--text) 8%, transparent); +} + +.select-option--selected { + background: color-mix(in srgb, var(--accent) 18%, transparent); +} + +.select-option--disabled { + color: var(--muted); + opacity: 0.55; + cursor: not-allowed; +} + +.select-option--disabled:hover { + background: transparent; +} + +.select-option-label { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.select-selected-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.select-empty { + padding: 16px; + color: var(--muted); + text-align: center; + font-size: 0.9rem; +} diff --git a/packages/client/src/components/Select.jsx b/packages/client/src/components/Select.jsx new file mode 100644 index 0000000..005794e --- /dev/null +++ b/packages/client/src/components/Select.jsx @@ -0,0 +1,221 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { IoChevronDown } from "react-icons/io5"; +import SimpleBar from "simplebar-react"; +import "simplebar-react/dist/simplebar.min.css"; +import "./Select.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * Custom select with optional search. onChange receives a synthetic event + * `{ target: { value, name } }` like a native select element. + * + * @param {object} props + * @param {string} props.value + * @param {(e: { target: { value: string, name?: string } }) => void} [props.onChange] + * @param {{ value: string, label: string, disabled?: boolean, search?: string }[]} [props.options] + * @param {string} [props.placeholder] + * @param {boolean} [props.disabled] + * @param {string} [props.id] + * @param {boolean} [props.searchable] + * @param {(option: object) => import('react').ReactNode} [props.renderOption] + * @param {(option: object) => import('react').ReactNode} [props.renderSelected] + * @param {string} [props.className] + * @param {string} [props.name] + */ +export function Select({ + value, + onChange, + options = [], + placeholder = "Select an option", + disabled = false, + id, + searchable = false, + renderOption, + renderSelected, + className = "", + name, +}) { + const [isOpen, setIsOpen] = useState(false); + const [searchTerm, setSearchTerm] = useState(""); + const wrapperRef = useRef(null); + const searchInputRef = useRef(null); + + const selectedOption = options.find((opt) => opt.value === value); + + const filteredOptions = + searchable && searchTerm + ? options.filter( + (opt) => + opt.label.toLowerCase().includes(searchTerm.toLowerCase()) || + opt?.search?.toLowerCase()?.includes(searchTerm.toLowerCase()), + ) + : options; + + useEffect(() => { + const handleClickOutside = (event) => { + if (wrapperRef.current && !wrapperRef.current.contains(event.target)) { + setIsOpen(false); + setSearchTerm(""); + } + }; + + if (isOpen) { + document.addEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen]); + + useEffect(() => { + if (isOpen && searchable && searchInputRef.current) { + searchInputRef.current.focus(); + } + }, [isOpen, searchable]); + + const handleKeyDown = useCallback( + (event) => { + if (disabled) return; + + switch (event.key) { + case "Enter": + case " ": + if (!isOpen) { + event.preventDefault(); + setIsOpen(true); + } + break; + case "Escape": + setIsOpen(false); + setSearchTerm(""); + break; + case "ArrowDown": + if (!isOpen) { + event.preventDefault(); + setIsOpen(true); + } + break; + default: + break; + } + }, + [disabled, isOpen], + ); + + const handleTriggerClick = () => { + if (!disabled) { + setIsOpen(!isOpen); + if (isOpen) { + setSearchTerm(""); + } + } + }; + + const handleOptionClick = (option) => { + if (option.disabled) return; + + onChange?.({ + target: { + value: option.value, + name, + }, + }); + setIsOpen(false); + setSearchTerm(""); + }; + + const handleSearchChange = (e) => { + setSearchTerm(e.target.value); + }; + + const renderSelectedContent = () => { + if (searchable && isOpen) { + return ( + e.stopPropagation()} + onFocus={(e) => e.stopPropagation()} + /> + ); + } + + if (!selectedOption) { + return {placeholder}; + } + + if (renderSelected) { + return renderSelected(selectedOption); + } + + return ( + {selectedOption.label} + ); + }; + + const renderOptionContent = (option) => { + if (renderOption) { + return renderOption(option); + } + + return {option.label}; + }; + + return ( +
+ + +
+ + {filteredOptions.length === 0 ? ( +
No options found
+ ) : ( + filteredOptions.map((option) => ( +
handleOptionClick(option)} + role="option" + aria-selected={option.value === value} + > + {renderOptionContent(option)} +
+ )) + )} +
+
+
+ ); +} diff --git a/packages/client/src/components/Sidebar.css b/packages/client/src/components/Sidebar.css new file mode 100644 index 0000000..aabc8a6 --- /dev/null +++ b/packages/client/src/components/Sidebar.css @@ -0,0 +1,265 @@ +.sidebar { + position: fixed; + left: 8vh; + top: 10vh; + bottom: 10vh; + z-index: 30; + display: flex; + flex-direction: column; + flex-shrink: 0; + box-sizing: border-box; +} + +.sidebar-container { + display: flex; + flex-direction: column; + padding: 20px 25px 25px 25px; + height: 100%; + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 35px; + background: color-mix(in srgb, var(--bg) 70%, transparent); + backdrop-filter: blur(10px); +} + +.sidebar-mobile-header { + right: 0; + margin-right: 10vw; + top: calc(10vh - 20px); + position: absolute; + z-index: 1000; + opacity: 0; + transition: + opacity 0.25s ease, + scale 0.35s cubic-bezier(0.33, 1, 0.68, 1); +} + +.sidebar-mobile-header--visible { + opacity: 1; +} + +.sidebar-brand { + margin-bottom: 10px; +} + +.sidebar-title { + margin: 0; + font-size: 28px; + text-transform: uppercase; + font-weight: 800; +} + +.sidebar-nav { + position: relative; + display: flex; + flex-direction: column; + gap: 5px; + margin: 0 -10px; +} + +.sidebar-nav-indicator { + position: absolute; + z-index: 0; + box-sizing: border-box; + border-radius: 15px; + background: color-mix(in srgb, var(--accent) 14%, transparent); + pointer-events: none; + transition: + top 0.32s cubic-bezier(0.4, 0, 0.2, 1), + left 0.32s cubic-bezier(0.4, 0, 0.2, 1), + width 0.32s cubic-bezier(0.4, 0, 0.2, 1), + height 0.32s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.2s ease; +} + +@media (prefers-reduced-motion: reduce) { + .sidebar-nav-indicator { + transition-duration: 0.01ms; + } +} + +.sidebar-link { + position: relative; + z-index: 1; + display: flex; + align-items: center; + gap: 8px; + padding: 7px 11px; + border-radius: 15px; + color: var(--text); + text-decoration: none; + font-size: 0.95rem; + transition: + background 0.15s, + color 0.15s; +} + +.sidebar-link-icon-box { + box-sizing: border-box; + flex-shrink: 0; + width: 18px; + min-width: 18px; + display: inline-flex; + align-items: center; + justify-content: center; + align-self: center; +} + +.sidebar-link-icon { + flex-shrink: 0; + width: 18px; + height: 18px; + opacity: 0.88; +} + +.sidebar-link-text { + font-size: 14px; + font-weight: 400; +} + +.sidebar-link--active .sidebar-link-icon { + opacity: 1; +} + +.sidebar-link:hover:not(.sidebar-link--active) { + background: var(--link-hover); +} + +.sidebar-link--active, +.sidebar-link--active:hover { + color: var(--accent); +} + +.sidebar-divider { + height: 0; + margin: 1rem 0; + border: 0; + border-top: var(--border-ui-width) solid var(--border-ui-color); +} + +.sidebar-footer { + margin-top: auto; + padding-top: 1.5rem; + border-top: var(--border-ui-width) solid var(--border-ui-color); +} + +.sidebar-footer .auth { + align-items: flex-start; +} + +.sidebar.sidebar--mobile-bottom { + left: calc(var(--app-main-padding-left) / 2); + right: calc(var(--app-main-padding-right) / 2); + + margin-bottom: calc(var(--app-main-padding-right) / 2); + bottom: 0; + top: auto; + width: auto; + height: auto; +} + +.sidebar.sidebar--mobile-bottom .sidebar-container { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + grid-template-rows: auto auto; + align-items: center; + padding: 12px; + height: auto; + border-radius: 40px; +} + +.sidebar.sidebar--mobile-bottom .sidebar-brand { + grid-column: 1; + grid-row: 1; + margin-bottom: 0; + min-width: 0; +} + +.sidebar.sidebar--mobile-bottom .sidebar-title { + font-size: 0.8rem; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar.sidebar--mobile-bottom .sidebar-brand .sub { + margin-top: 0.2rem; + font-size: 0.68rem; +} + +.sidebar.sidebar--mobile-bottom .sidebar-nav { + grid-column: 1 / -1; + grid-row: 2; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + gap: 4px; + margin: 0; +} + +.sidebar.sidebar--mobile-bottom .sidebar-nav .sidebar-link-icon-box { + width: 38px; + min-width: 38px; +} + +.sidebar.sidebar--mobile-bottom .sidebar-nav .sidebar-link-icon { + width: 38px; + height: 38px; +} + +.sidebar.sidebar--mobile-bottom .sidebar-nav-indicator { + border-radius: 25px; +} + +.sidebar.sidebar--mobile-bottom .sidebar-link { + flex: 1 1 0; + min-width: 0; + flex-direction: column; + justify-content: center; + gap: 4px; + padding: 8px 4px; + border-radius: 25px; + text-align: center; +} + +.sidebar.sidebar--mobile-bottom .sidebar-link-text { + font-size: 0.62rem; + font-weight: 500; + line-height: 1.15; + display: -webkit-box; + line-clamp: 2; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.sidebar.sidebar--mobile-bottom .sidebar-footer { + grid-column: 2; + grid-row: 1; + margin-top: 0; + padding-top: 0; + border-top: none; + justify-self: end; + align-self: center; +} + +.sidebar.sidebar--mobile-bottom .sidebar-footer .auth { + align-items: center; + flex-direction: row; + flex-wrap: nowrap; + gap: 8px; +} + +.sidebar.sidebar--mobile-bottom .sidebar-footer .user { + display: none; +} + +.sidebar.sidebar--mobile-bottom .sidebar-footer .btn { + padding: 6px 10px; + font-size: 0.75rem; + white-space: nowrap; +} + +.sidebar.sidebar--mobile-bottom.sidebar--drawer-open { + transform-origin: 50% 100%; +} diff --git a/packages/client/src/components/Sidebar.jsx b/packages/client/src/components/Sidebar.jsx new file mode 100644 index 0000000..2239f72 --- /dev/null +++ b/packages/client/src/components/Sidebar.jsx @@ -0,0 +1,226 @@ +import { useCallback, useLayoutEffect, useRef, useState } from "react"; +import { matchPath, NavLink, useLocation } from "react-router-dom"; +import { + MdElectricBolt, + MdGridView, + MdWaterDrop, + MdPerson, +} from "react-icons/md"; +import { HiClock } from "react-icons/hi2"; +import { BiSolidBolt, BiWater } from "react-icons/bi"; +import { FaHandHoldingWater } from "react-icons/fa"; +import { PiPlantFill } from "react-icons/pi"; +import { FaGlassWaterDroplet } from "react-icons/fa6"; +import { Button } from "./Button.jsx"; +import { Flex } from "./Flex.jsx"; +import { useAuth } from "../contexts/AuthContext.jsx"; +import { useApi } from "../contexts/ApiContext.jsx"; +import { useTheme } from "../contexts/ThemeContext.jsx"; +import "./Sidebar.css"; + +// Each link: optional iconSize / mobileIconSize (px). Desktop: 18px-wide box, default 18×18 icon. Mobile bottom nav: 38px box, default 38×38. Smaller glyphs center in the box. +const MAIN_LINKS = [ + { path: "/patches", label: "Patches", Icon: PiPlantFill, mobileIconSize: 42 }, + { + path: "/water-tank", + label: "Water tank", + Icon: FaGlassWaterDroplet, + iconSize: 17, + mobileIconSize: 34, + }, + { + path: "/power", + label: "Power", + Icon: BiSolidBolt, + iconSize: 22, + mobileIconSize: 42, + }, + { + path: "/watering-sessions", + label: "Watering sessions", + Icon: MdWaterDrop, + mobileIconSize: 40, + }, + { + path: "/water-schedules", + label: "Watering schedules", + Icon: HiClock, + mobileIconSize: 40, + }, +]; + +function getSidebarLinkIconStyle(link, isMobile) { + const px = isMobile ? link.mobileIconSize : link.iconSize; + if (px == null) return undefined; + return { width: px, height: px }; +} + +export function Sidebar() { + const { user } = useAuth(); + const { isConnected } = useApi(); + const { isMobile } = useTheme(); + const location = useLocation(); + const pathname = + location.pathname.length > 1 && location.pathname.endsWith("/") + ? location.pathname.slice(0, -1) + : location.pathname; + const isPatchesPage = pathname === "/patches"; + + const activeNavIndex = MAIN_LINKS.findIndex( + ({ path }) => matchPath({ path, end: false }, pathname) != null, + ); + + const navRef = useRef(null); + const linkRefs = useRef([]); + const [navIndicator, setNavIndicator] = useState({ + top: 0, + left: 0, + width: 0, + height: 0, + visible: false, + }); + + const updateNavIndicator = useCallback(() => { + const nav = navRef.current; + if (!nav) return; + if (activeNavIndex < 0) { + setNavIndicator((s) => ({ ...s, visible: false })); + return; + } + const link = linkRefs.current[activeNavIndex]; + if (!link) return; + const nr = nav.getBoundingClientRect(); + const lr = link.getBoundingClientRect(); + setNavIndicator({ + top: lr.top - nr.top + nav.scrollTop, + left: lr.left - nr.left + nav.scrollLeft, + width: lr.width, + height: lr.height, + visible: true, + }); + }, [activeNavIndex]); + + useLayoutEffect(() => { + updateNavIndicator(); + }, [updateNavIndicator, pathname, isMobile]); + + useLayoutEffect(() => { + const nav = navRef.current; + if (!nav || typeof ResizeObserver === "undefined") return; + const ro = new ResizeObserver(() => updateNavIndicator()); + ro.observe(nav); + for (const el of linkRefs.current) { + if (el) ro.observe(el); + } + window.addEventListener("resize", updateNavIndicator); + return () => { + ro.disconnect(); + window.removeEventListener("resize", updateNavIndicator); + }; + }, [updateNavIndicator]); + + const footerContent = ( + + {user === undefined ? null : user ? ( + <> + + + + + {!isMobile && {user.name || user.sub}} + + + + ) : ( + + )} + + ); + + return ( + <> + {isMobile && ( +
+ {footerContent} +
+ )} + + + ); +} diff --git a/packages/client/src/components/Slider.css b/packages/client/src/components/Slider.css new file mode 100644 index 0000000..8b0ba8b --- /dev/null +++ b/packages/client/src/components/Slider.css @@ -0,0 +1,93 @@ +.slider-wrap { + position: relative; + width: 100%; + height: 100%; + padding: 2px 7px; +} + +.slider { + box-sizing: border-box; + position: relative; + width: 100%; + height: 12px; + touch-action: none; + cursor: default; +} + +.slider--disabled { + opacity: 0.45; + cursor: not-allowed; + pointer-events: none; +} + +.slider__rail { + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + height: 4px; + border-radius: 2px; + background-color: color-mix(in srgb, var(--text) 12%, transparent); +} + +.slider__track { + position: absolute; + top: 50%; + transform: translateY(-50%); + height: 4px; + border-radius: 2px; + background-color: var(--accent); +} + +.slider__rail-hit { + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + height: 16px; + margin-top: -8px; + border-radius: 4px; + cursor: pointer; +} + +.slider--disabled .slider__rail-hit { + cursor: not-allowed; +} + +.slider__handle { + position: absolute; + top: 50%; + width: 14px; + height: 14px; + margin-top: -7px; + margin-left: -7px; + border: 2px solid var(--accent); + border-radius: 50%; + background-color: var(--bg); + box-shadow: 0 1px 2px color-mix(in srgb, var(--text) 12%, transparent); + cursor: grab; + z-index: 1; + transition: + border-color 0.15s ease, + box-shadow 0.15s ease; +} + +.slider:not(.slider--disabled) .slider__handle:hover { + border-color: color-mix(in srgb, var(--accent) 85%, var(--text)); +} + +.slider__handle:focus { + outline: none; +} + +.slider__handle:focus-visible { + box-shadow: + 0 0 0 2px var(--bg), + 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent); +} + +.slider:not(.slider--disabled) .slider__handle:active { + cursor: grabbing; +} diff --git a/packages/client/src/components/Slider.jsx b/packages/client/src/components/Slider.jsx new file mode 100644 index 0000000..a1657a1 --- /dev/null +++ b/packages/client/src/components/Slider.jsx @@ -0,0 +1,230 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import "./Slider.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +function clamp(n, min, max) { + return Math.min(max, Math.max(min, n)); +} + +/** + * @param {number} clientX + * @param {DOMRect} rect + * @param {number} min + * @param {number} max + * @param {number} step + */ +function valueFromPointer(clientX, rect, min, max, step) { + if (rect.width <= 0) return min; + const t = (clientX - rect.left) / rect.width; + const raw = min + t * (max - min); + if (step > 0) { + const steps = Math.round((raw - min) / step); + return clamp(min + steps * step, min, max); + } + return clamp(raw, min, max); +} + +/** + * Ant Design–like single-value horizontal slider (no antd/rc-slider). + * + * @param {object} props + * @param {number} [props.min=0] + * @param {number} [props.max=100] + * @param {number} props.value + * @param {(n: number) => void} props.onChange + * @param {number} [props.step=1] + * @param {boolean} [props.disabled] + * @param {string} [props.className] + * @param {string} [props.id] — forwarded to the handle for label association + * @param {string} [props['aria-labelledby']] + * @param {string} [props['aria-label']] + */ +export function Slider({ + min = 0, + max = 100, + value, + onChange, + step = 1, + disabled = false, + className, + id, + "aria-labelledby": ariaLabelledBy, + "aria-label": ariaLabel, +}) { + const wrapRef = useRef(null); + const draggingRef = useRef(false); + const [focused, setFocused] = useState(false); + + const lo = Math.min(min, max); + const hi = Math.max(min, max); + const safeStep = step > 0 ? step : 1; + + const clamped = useMemo(() => { + const n = Number(value); + if (!Number.isFinite(n)) return lo; + if (safeStep > 0) { + const steps = Math.round((n - lo) / safeStep); + return clamp(lo + steps * safeStep, lo, hi); + } + return clamp(n, lo, hi); + }, [value, lo, hi, safeStep]); + + const percent = hi === lo ? 0 : ((clamped - lo) / (hi - lo)) * 100; + + const emitFromClientX = useCallback( + (clientX) => { + const el = wrapRef.current; + if (!el || disabled) return; + const rect = el.getBoundingClientRect(); + const next = valueFromPointer(clientX, rect, lo, hi, safeStep); + if (next !== clamped) onChange(next); + }, + [clamped, disabled, hi, lo, onChange, safeStep], + ); + + const onRailPointerDown = useCallback( + (e) => { + if (disabled || e.button !== 0) return; + e.preventDefault(); + e.currentTarget.setPointerCapture(e.pointerId); + draggingRef.current = true; + emitFromClientX(e.clientX); + }, + [disabled, emitFromClientX], + ); + + const onRailPointerMove = useCallback( + (e) => { + if (!draggingRef.current || disabled) return; + emitFromClientX(e.clientX); + }, + [disabled, emitFromClientX], + ); + + const onRailPointerUp = useCallback((e) => { + if (draggingRef.current) { + try { + e.currentTarget.releasePointerCapture(e.pointerId); + } catch { + /* ignore */ + } + } + draggingRef.current = false; + }, []); + + const onHandlePointerDown = useCallback( + (e) => { + e.stopPropagation(); + if (disabled || e.button !== 0) return; + e.preventDefault(); + const rail = wrapRef.current?.querySelector(".slider__rail-hit"); + if (rail) { + rail.setPointerCapture(e.pointerId); + draggingRef.current = true; + } + emitFromClientX(e.clientX); + }, + [disabled, emitFromClientX], + ); + + const onKeyDown = useCallback( + (e) => { + if (disabled) return; + let delta = 0; + const page = hi - lo; + const big = Math.max(safeStep, page / 10); + switch (e.key) { + case "ArrowRight": + case "ArrowUp": + delta = safeStep; + break; + case "ArrowLeft": + case "ArrowDown": + delta = -safeStep; + break; + case "Home": + e.preventDefault(); + onChange(lo); + return; + case "End": + e.preventDefault(); + onChange(hi); + return; + case "PageUp": + delta = big; + break; + case "PageDown": + delta = -big; + break; + default: + return; + } + e.preventDefault(); + const next = clamp(clamped + delta, lo, hi); + if (safeStep > 0) { + const steps = Math.round((next - lo) / safeStep); + onChange(clamp(lo + steps * safeStep, lo, hi)); + } else { + onChange(next); + } + }, + [clamped, disabled, hi, lo, onChange, safeStep], + ); + + useEffect(() => { + if (!focused) return; + const onDocPointerUp = () => { + draggingRef.current = false; + }; + document.addEventListener("pointerup", onDocPointerUp); + document.addEventListener("pointercancel", onDocPointerUp); + return () => { + document.removeEventListener("pointerup", onDocPointerUp); + document.removeEventListener("pointercancel", onDocPointerUp); + }; + }, [focused]); + + return ( +
+
+
+
+
+
setFocused(true)} + onBlur={() => setFocused(false)} + /> +
+
+ ); +} diff --git a/packages/client/src/components/Switch.css b/packages/client/src/components/Switch.css new file mode 100644 index 0000000..e030e8e --- /dev/null +++ b/packages/client/src/components/Switch.css @@ -0,0 +1,167 @@ +/* Ant Design–like switch; uses app :root tokens (App.css) */ + +.switch { + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + margin: 0; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + vertical-align: middle; + flex-shrink: 0; + line-height: 1; + font: inherit; + color: inherit; +} + +.switch:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.switch__track { + position: relative; + display: block; + box-sizing: border-box; + min-width: 44px; + width: 44px; + height: 22px; + border-radius: 100px; + background: color-mix(in srgb, var(--text) 22%, transparent); + transition: + background-color 0.2s ease, + min-width 0.2s ease, + width 0.2s ease; +} + +.switch--checked .switch__track { + background: var(--accent); +} + +.switch--with-inner .switch__track { + min-width: 52px; + width: auto; + padding-inline: 2px; +} + +.switch--small.switch--with-inner .switch__track { + min-width: 40px; +} + +.switch--disabled { + cursor: not-allowed; + opacity: 0.45; +} + +.switch--disabled .switch__track { + cursor: not-allowed; +} + +.switch--loading { + cursor: default; +} + +.switch--small .switch__track { + min-width: 28px; + width: 28px; + height: 16px; +} + +.switch__handle { + position: absolute; + top: 2px; + left: 2px; + width: 18px; + height: 18px; + border-radius: 50%; + background: var(--bg); + box-shadow: 0 1px 3px color-mix(in srgb, #000 35%, transparent); + transition: left 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; +} + +.switch--small .switch__handle { + width: 12px; + height: 12px; + top: 2px; + left: 2px; +} + +.switch--checked .switch__handle { + left: calc(100% - 20px); +} + +.switch--small.switch--checked .switch__handle { + left: calc(100% - 14px); +} + +.switch__loading-icon { + flex-shrink: 0; +} + +.switch--checked .switch__loading-icon path { + fill: var(--accent); +} + +.switch:not(.switch--checked) .switch__loading-icon path { + fill: var(--text); +} + +.switch__inner { + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + overflow: hidden; + border-radius: inherit; +} + +.switch__inner-text { + position: absolute; + inset: 0; + display: flex; + align-items: center; + font-size: 12px; + font-weight: 500; + line-height: 1; + white-space: nowrap; + opacity: 0; + transition: opacity 0.2s ease; + color: var(--on-accent); +} + +.switch--small .switch__inner-text { + font-size: 11px; +} + +.switch__inner-text--checked { + padding-left: 8px; + padding-right: 22px; + justify-content: flex-start; +} + +.switch__inner-text--unchecked { + padding-left: 22px; + padding-right: 8px; + justify-content: flex-end; + color: color-mix(in srgb, var(--text) 88%, transparent); +} + +.switch__inner-text--visible { + opacity: 1; +} + +.switch--small .switch__inner-text--checked { + padding-left: 5px; + padding-right: 16px; +} + +.switch--small .switch__inner-text--unchecked { + padding-left: 16px; + padding-right: 5px; +} diff --git a/packages/client/src/components/Switch.jsx b/packages/client/src/components/Switch.jsx new file mode 100644 index 0000000..fd34855 --- /dev/null +++ b/packages/client/src/components/Switch.jsx @@ -0,0 +1,133 @@ +import { useCallback, useState } from "react"; +import { LoadingIcon } from "./LoadingIcon"; +import "./Switch.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * Ant Design–like toggle switch (no antd). Controlled or uncontrolled. + * + * @param {object} props + * @param {boolean} [props.checked] — controlled + * @param {boolean} [props.defaultChecked=false] — uncontrolled initial + * @param {(checked: boolean, e: import('react').MouseEvent) => void} [props.onChange] + * @param {(e: import('react').MouseEvent) => void} [props.onClick] + * @param {boolean} [props.disabled] + * @param {boolean} [props.loading] + * @param {'default' | 'small'} [props.size='default'] + * @param {import('react').ReactNode} [props.checkedChildren] + * @param {import('react').ReactNode} [props.unCheckedChildren] + * @param {string} [props.className] + * @param {string} [props.id] + */ +export function Switch({ + checked, + defaultChecked = false, + onChange, + onClick, + disabled = false, + loading = false, + size = "default", + checkedChildren, + unCheckedChildren, + className, + id, + ...rest +}) { + const controlled = checked !== undefined; + const [innerChecked, setInnerChecked] = useState(defaultChecked); + const mergedChecked = controlled ? Boolean(checked) : innerChecked; + + const mergedDisabled = disabled || loading; + + const toggle = useCallback( + (e) => { + if (mergedDisabled) return; + const next = !mergedChecked; + if (!controlled) setInnerChecked(next); + onChange?.(next, e); + }, + [controlled, mergedChecked, mergedDisabled, onChange], + ); + + const handleClick = useCallback( + (e) => { + onClick?.(e); + if (e.defaultPrevented) return; + toggle(e); + }, + [onClick, toggle], + ); + + const hasInner = checkedChildren != null || unCheckedChildren != null; + + return ( + + ); +} diff --git a/packages/client/src/components/Table.css b/packages/client/src/components/Table.css new file mode 100644 index 0000000..278a8ed --- /dev/null +++ b/packages/client/src/components/Table.css @@ -0,0 +1,34 @@ +.ta-table-wrap { + width: 100%; + overflow-x: auto; +} + +.ta-table { + width: 100%; + border-collapse: collapse; + font-size: 0.9rem; +} + +.ta-table th, +.ta-table td { + padding: 0.65rem 1rem; + text-align: left; + border-bottom: var(--border-ui-width) solid var(--border-ui-color); +} + +.ta-table th { + font-size: 0.75rem; + font-weight: 600; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.05em; + background: rgba(0, 0, 0, 0.2); +} + +.ta-table tbody tr:last-child td { + border-bottom: none; +} + +.ta-table tbody tr:hover td { + background: rgba(255, 255, 255, 0.03); +} diff --git a/packages/client/src/components/Table.jsx b/packages/client/src/components/Table.jsx new file mode 100644 index 0000000..d2a66c4 --- /dev/null +++ b/packages/client/src/components/Table.jsx @@ -0,0 +1,21 @@ +import React from "react"; +import "./Table.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +/** + * @param {object} props + * @param {import('react').ReactNode} [props.children] + * @param {string} [props.className] + */ +export function Table({ className, children, ...rest }) { + return ( +
+ + {children} +
+
+ ); +} diff --git a/packages/client/src/components/TemperatureHumidityDisplay.css b/packages/client/src/components/TemperatureHumidityDisplay.css new file mode 100644 index 0000000..dedd813 --- /dev/null +++ b/packages/client/src/components/TemperatureHumidityDisplay.css @@ -0,0 +1,13 @@ +.temperature-humidity-display { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +} + +.temperature-humidity-stat { + display: flex; + flex-direction: row; + align-items: center; + gap: 3px; +} diff --git a/packages/client/src/components/TemperatureHumidityDisplay.jsx b/packages/client/src/components/TemperatureHumidityDisplay.jsx new file mode 100644 index 0000000..877daa7 --- /dev/null +++ b/packages/client/src/components/TemperatureHumidityDisplay.jsx @@ -0,0 +1,31 @@ +import React from "react"; +import { MdThermostat, MdWaterDrop } from "react-icons/md"; +import "./TemperatureHumidityDisplay.css"; + +export function TemperatureHumidityDisplay({ + soilTemperature, + soilHumidity, + coloredIcons = true, +}) { + const thermostatStyle = coloredIcons + ? { color: "#FF9230" } + : { color: "currentColor" }; + const waterStyle = coloredIcons + ? { color: "#3CD3FE", marginRight: "2px" } + : { color: "currentColor", marginRight: "2px" }; + + return ( +
+
+ + {soilTemperature != null + ? `${soilTemperature.toFixed(1)}°C` + : "—"} +
+
+ + {soilHumidity != null ? `${Math.round(soilHumidity)}%` : "—"} +
+
+ ); +} diff --git a/packages/client/src/components/TextArea.css b/packages/client/src/components/TextArea.css new file mode 100644 index 0000000..3c65548 --- /dev/null +++ b/packages/client/src/components/TextArea.css @@ -0,0 +1,246 @@ +@import "./Input.css"; + +/* Ant Design–style multiline field; shares Input border, radius, focus ring */ + +.textarea-affix-wrapper { + display: inline-flex; + align-items: flex-start; + width: 100%; + min-width: 0; + padding-block: 0; + gap: 8px; + box-sizing: border-box; + font-family: inherit; + font-size: 0.9rem; + line-height: 1.5715; + font-weight: 500; + color: var(--text); + background-color: var(--bg); + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 18px; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; +} + +.textarea-affix-wrapper:hover:not(.textarea-affix-wrapper--disabled) { + border-color: color-mix(in srgb, var(--accent) 45%, var(--border-ui-color)); +} + +.textarea-affix-wrapper:focus-within:not(.textarea-affix-wrapper--disabled) { + border-color: var(--accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); +} + +.textarea-affix-wrapper--sm { + font-size: 0.8rem; + line-height: 1.5715; +} + +.textarea-affix-wrapper--sm .textarea-affix-inner { + padding: 6px 10px; + min-height: 52px; +} + +.textarea-affix-wrapper--md .textarea-affix-inner { + padding: 8px 14px; + min-height: 64px; +} + +.textarea-affix-wrapper--lg { + font-size: 1rem; +} + +.textarea-affix-wrapper--lg .textarea-affix-inner { + padding: 10px 16px; + min-height: 76px; +} + +.textarea-affix-wrapper--borderless { + border-color: transparent; + background-color: color-mix(in srgb, var(--text) 4%, transparent); +} + +.textarea-affix-wrapper--borderless:hover:not(.textarea-affix-wrapper--disabled) { + background-color: color-mix(in srgb, var(--text) 7%, transparent); +} + +.textarea-affix-wrapper--borderless:focus-within:not(.textarea-affix-wrapper--disabled) { + background-color: var(--bg); +} + +.textarea-affix-wrapper--error { + border-color: color-mix(in srgb, #ff4d4f 70%, var(--border-ui-color)); +} + +.textarea-affix-wrapper--error:focus-within { + border-color: #ff4d4f; + box-shadow: 0 0 0 2px color-mix(in srgb, #ff4d4f 22%, transparent); +} + +.textarea-affix-wrapper--warning { + border-color: color-mix(in srgb, #faad14 65%, var(--border-ui-color)); +} + +.textarea-affix-wrapper--warning:focus-within { + border-color: #faad14; + box-shadow: 0 0 0 2px color-mix(in srgb, #faad14 22%, transparent); +} + +.textarea-affix-wrapper--disabled { + opacity: 0.55; + cursor: not-allowed; + background-color: color-mix(in srgb, var(--text) 5%, var(--bg)); +} + +.textarea-affix-inner { + flex: 1 1 auto; + min-width: 0; + width: 100%; + border: none; + outline: none; + background: transparent; + color: inherit; + font: inherit; + line-height: inherit; + resize: vertical; + margin: 0; + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + display: block; +} + +.textarea-affix-inner::placeholder { + color: var(--muted); + opacity: 1; +} + +.textarea-affix-wrapper--disabled .textarea-affix-inner { + cursor: not-allowed; + color: var(--muted); +} + +.textarea-affix-wrapper .input-suffix { + align-self: flex-start; + margin-top: 6px; +} + +.textarea-affix-wrapper--sm .input-suffix { + margin-top: 4px; +} + +.textarea-affix-wrapper--lg .input-suffix { + margin-top: 8px; +} + +/* Standalone textarea (no affix) — same look as .input */ +.textarea { + box-sizing: border-box; + font-family: inherit; + font-size: 0.9rem; + line-height: 1.5715; + font-weight: 500; + color: var(--text); + background-color: var(--bg); + border: var(--border-ui-width) solid var(--border-ui-color); + border-radius: 18px; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; + display: block; + width: 100%; + min-width: 0; + margin: 0; + vertical-align: top; + outline: none; + appearance: none; + -webkit-appearance: none; + resize: vertical; +} + +.textarea::placeholder { + color: var(--muted); + opacity: 1; +} + +.textarea:hover:not(:disabled):not(:read-only) { + border-color: color-mix(in srgb, var(--accent) 45%, var(--border-ui-color)); +} + +.textarea:focus-visible { + border-color: var(--accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); +} + +.textarea--borderless { + border-color: transparent; + background-color: color-mix(in srgb, var(--text) 4%, transparent); +} + +.textarea--borderless:hover:not(:disabled):not(:read-only) { + background-color: color-mix(in srgb, var(--text) 7%, transparent); +} + +.textarea--borderless:focus-visible { + background-color: var(--bg); +} + +.textarea--sm { + font-size: 0.8rem; + padding: 6px 10px; + min-height: 52px; +} + +.textarea--md { + font-size: 0.9rem; + padding: 8px 14px; + min-height: 64px; +} + +.textarea--lg { + font-size: 1rem; + padding: 10px 16px; + min-height: 76px; +} + +.textarea--error { + border-color: color-mix(in srgb, #ff4d4f 70%, var(--border-ui-color)); +} + +.textarea--error:focus-visible { + border-color: #ff4d4f; + box-shadow: 0 0 0 2px color-mix(in srgb, #ff4d4f 22%, transparent); +} + +.textarea--warning { + border-color: color-mix(in srgb, #faad14 65%, var(--border-ui-color)); +} + +.textarea--warning:focus-visible { + border-color: #faad14; + box-shadow: 0 0 0 2px color-mix(in srgb, #faad14 22%, transparent); +} + +.textarea:disabled { + opacity: 0.55; + cursor: not-allowed; + background-color: color-mix(in srgb, var(--text) 5%, var(--bg)); +} + +.textarea:read-only:not(:disabled) { + cursor: default; +} + +.textarea-count { + display: block; + text-align: right; + font-size: 0.75rem; + font-weight: 500; + color: var(--muted); + margin-top: 4px; +} + +.textarea-count--error { + color: #ff4d4f; +} diff --git a/packages/client/src/components/TextArea.jsx b/packages/client/src/components/TextArea.jsx new file mode 100644 index 0000000..96985cd --- /dev/null +++ b/packages/client/src/components/TextArea.jsx @@ -0,0 +1,269 @@ +import { + forwardRef, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from "react"; +import { IoCloseCircle } from "react-icons/io5"; +import "./TextArea.css"; + +function cx(...parts) { + return parts.filter(Boolean).join(" "); +} + +const sizeClass = { + small: "sm", + middle: "md", + large: "lg", +}; + +/** + * Ant Design–like controlled/uncontrolled textarea (Input.TextArea style). + * + * @param {object} props + * @param {string} [props.value] + * @param {string} [props.defaultValue] + * @param {(e: import('react').ChangeEvent) => void} [props.onChange] + * @param {(e: import('react').KeyboardEvent) => void} [props.onPressEnter] + * @param {string} [props.placeholder] + * @param {boolean} [props.disabled] + * @param {boolean} [props.readOnly] + * @param {boolean} [props.allowClear] + * @param {boolean} [props.bordered=true] + * @param {'large' | 'middle' | 'small'} [props.size='middle'] + * @param {'' | 'error' | 'warning'} [props.status] + * @param {number} [props.rows=4] + * @param {boolean | { minRows?: number, maxRows?: number }} [props.autoSize] + * @param {boolean} [props.showCount] + * @param {number} [props.maxLength] + * @param {import('react').ReactNode} [props.suffix] + * @param {string} [props.className] + */ +export const TextArea = forwardRef(function TextArea( + { + value, + defaultValue, + onChange, + onPressEnter, + placeholder, + disabled = false, + readOnly = false, + allowClear = false, + bordered = true, + size = "middle", + status, + rows = 4, + autoSize = false, + showCount = false, + maxLength, + suffix, + className, + name, + id, + style, + onKeyDown: onKeyDownProp, + ...rest + }, + ref, +) { + const sz = sizeClass[size] ?? "md"; + const innerRef = useRef(null); + const setRefs = useCallback( + (node) => { + innerRef.current = node; + if (typeof ref === "function") ref(node); + else if (ref) ref.current = node; + }, + [ref], + ); + + const isControlled = value !== undefined; + const [innerValue, setInnerValue] = useState(() => defaultValue ?? ""); + + useEffect(() => { + if (!isControlled && defaultValue !== undefined) { + setInnerValue(defaultValue); + } + }, [defaultValue, isControlled]); + + const displayValue = isControlled ? value ?? "" : innerValue; + + const emitChange = useCallback( + (next, event) => { + if (!isControlled) setInnerValue(next); + if (onChange && event) { + const target = Object.assign({}, event.target, { value: next }); + const synthetic = Object.assign({}, event, { + target, + currentTarget: target, + }); + onChange(synthetic); + } + }, + [isControlled, onChange], + ); + + const handleChange = (e) => { + emitChange(e.target.value, e); + }; + + const handleClear = (e) => { + e.preventDefault(); + const el = innerRef.current; + if (el && !disabled && !readOnly) { + const synthetic = { + type: "change", + target: { value: "" }, + currentTarget: { value: "" }, + }; + emitChange("", synthetic); + el.focus(); + } + }; + + const handleKeyDown = (e) => { + onKeyDownProp?.(e); + if ( + onPressEnter && + e.key === "Enter" && + !e.nativeEvent.isComposing && + !e.shiftKey + ) { + e.preventDefault(); + onPressEnter(e); + } + }; + + const minRows = + autoSize && typeof autoSize === "object" ? autoSize.minRows : undefined; + const maxRows = + autoSize && typeof autoSize === "object" ? autoSize.maxRows : undefined; + + useLayoutEffect(() => { + if (!autoSize || !innerRef.current) return; + const el = innerRef.current; + const cs = getComputedStyle(el); + const lineHeight = parseFloat(cs.lineHeight) || 22; + const padY = + parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom) || 16; + const minH = + minRows != null + ? minRows * lineHeight + padY + : parseFloat(cs.minHeight) || 64; + const maxH = + maxRows != null ? maxRows * lineHeight + padY : Number.POSITIVE_INFINITY; + el.style.height = "auto"; + const next = Math.min(maxH, Math.max(minH, el.scrollHeight)); + el.style.height = `${next}px`; + el.style.overflowY = el.scrollHeight > maxH ? "auto" : "hidden"; + }, [autoSize, displayValue, minRows, maxRows]); + + const statusMod = + status === "error" + ? "error" + : status === "warning" + ? "warning" + : null; + + const showAffix = Boolean(suffix || allowClear); + const showClear = + allowClear && + !disabled && + !readOnly && + String(displayValue).length > 0; + + const shared = { + ref: setRefs, + id, + name, + disabled, + readOnly, + placeholder, + maxLength: maxLength != null ? maxLength : undefined, + value: displayValue, + onChange: handleChange, + onKeyDown: handleKeyDown, + rows: autoSize ? 1 : rows, + "aria-invalid": status === "error" || undefined, + ...rest, + }; + + const countStr = + maxLength != null + ? `${String(displayValue).length} / ${maxLength}` + : `${String(displayValue).length}`; + + const field = showAffix ? ( + +