Repository: stack-auth/stack-auth
Pull request #665: Update packages, add bundle analyzer, add declarationMap
Author: login: similiarity
Created: 2025-05-09T03:24:16Z | Merged: 2025-05-17T00:07:45Z

<!--

Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Update dependencies, add bundle analyzer, and enhance `ProfileImageEditor` with `react-easy-crop`.
> 
>   - **Dependencies**:
>     - Update `lucide-react` to `^0.508.0` in `apps/dashboard/package.json`, `packages/stack-emails/package.json`, and `packages/stack-ui/package.json`.
>     - Replace `react-avatar-editor` with `react-easy-crop` in `packages/react/package.json`, `packages/stack/package.json`, and `packages/template/package-template.json`.
>     - Add `@next/bundle-analyzer` to `examples/demo/package.json`.
>   - **Build Configuration**:
>     - Add bundle analyzer configuration to `examples/demo/next.config.js`.
>     - Add `analyze` script to `examples/demo/package.json`.
>   - **Component Changes**:
>     - Replace `AvatarEditor` with `Cropper` in `ProfileImageEditor` in `packages/template/src/components/profile-image-editor.tsx`.
>     - Update `Calendar` component styles in `packages/stack-ui/src/components/ui/calendar.tsx`.
>   - **Utility Functions**:
>     - Update `isBase32`, `isBase64`, and `isBase64Url` test cases in `bytes.tsx` to handle specific edge cases.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup> for ae4574dba0a35785910e84e0e37679edd7f6fc96. You can [customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->


Diff:
diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json
index fecbc7cffd..2de18bd90d 100644
--- a/apps/dashboard/package.json
+++ b/apps/dashboard/package.json
@@ -33,7 +33,7 @@
     "dotenv-cli": "^7.3.0",
     "geist": "^1",
     "lodash": "^4.17.21",
-    "lucide-react": "^0.378.0",
+    "lucide-react": "^0.508.0",
     "next": "15.2.3",
     "next-themes": "^0.2.1",
     "posthog-js": "^1.235.0",
diff --git a/examples/demo/next.config.js b/examples/demo/next.config.js
index a08dcdcd5f..7b04c466f6 100644
--- a/examples/demo/next.config.js
+++ b/examples/demo/next.config.js
@@ -1,2 +1,8 @@
-/** @type {import("next").NextConfig} */
-module.exports = {};
+/** @type {import('next').NextConfig} */
+const nextConfig = {}
+ 
+const withBundleAnalyzer = require('@next/bundle-analyzer')({
+  enabled: process.env.ANALYZE === 'true',
+})
+ 
+module.exports = withBundleAnalyzer(nextConfig)
diff --git a/examples/demo/package.json b/examples/demo/package.json
index d027d11d0a..8544002206 100644
--- a/examples/demo/package.json
+++ b/examples/demo/package.json
@@ -9,7 +9,8 @@
     "dev": "next dev --turbopack --port 8103",
     "build": "next build",
     "start": "next start --port 8103",
-    "lint": "next lint"
+    "lint": "next lint",
+    "analyze": "ANALYZE=true pnpm build"
   },
   "dependencies": {
     "@emotion/react": "^11.13.3",
@@ -26,6 +27,7 @@
   "devDependencies": {
     "@types/react": "19.0.12",
     "@types/react-dom": "19.0.4",
+    "@next/bundle-analyzer": "15.2.3",
     "autoprefixer": "^10.4.20",
     "postcss": "^8.4.47",
     "rimraf": "^5.0.10",
diff --git a/packages/react/package.json b/packages/react/package.json
index 66cb1ac0e3..0d9a85f4a3 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -53,7 +53,7 @@
     "oauth4webapi": "^2.10.3",
     "@oslojs/otp": "^1.1.0",
     "qrcode": "^1.5.4",
-    "react-avatar-editor": "^13.0.2",
+    "react-easy-crop": "^5.4.1",
     "react-hook-form": "^7.51.4",
     "rimraf": "^5.0.5",
     "tailwindcss-animate": "^1.0.7",
diff --git a/packages/stack-emails/package.json b/packages/stack-emails/package.json
index a00050bd90..21515b747f 100644
--- a/packages/stack-emails/package.json
+++ b/packages/stack-emails/package.json
@@ -53,7 +53,7 @@
     "react-email": "2.1.0",
     "@react-email/components": "^0.0.14",
     "@react-email/render": "^0.0.12",
-    "lucide-react": "^0.378.0",
+    "lucide-react": "^0.508.0",
     "react-colorful": "^5.6.1",
     "zustand": "^4.5.2",
     "zod": "^3.23.8",
diff --git a/packages/stack-sc/tsconfig.json b/packages/stack-sc/tsconfig.json
index 67525231b1..50d3ef75c4 100644
--- a/packages/stack-sc/tsconfig.json
+++ b/packages/stack-sc/tsconfig.json
@@ -4,6 +4,7 @@
     "outDir": "dist",
     "rootDir": "src",
     "declaration": true,
+    "declarationMap": true,
     "target": "ES2021",
     "lib": ["DOM", "DOM.Iterable", "ES2021", "ES2022.Error"],
     "module": "ES2015",
diff --git a/packages/stack-shared/src/utils/bytes.tsx b/packages/stack-shared/src/utils/bytes.tsx
index c96c188cb2..4ab232aebd 100644
--- a/packages/stack-shared/src/utils/bytes.tsx
+++ b/packages/stack-shared/src/utils/bytes.tsx
@@ -214,9 +214,9 @@ export function isBase32(input: string): boolean {
   return true;
 }
 import.meta.vitest?.test("isBase32", ({ expect }) => {
-  expect(isBase32("ABCDEFGHIJKLMNOPQRSTVWXYZ234567")).toBe(true);
+  expect(isBase32("0123456789ABCDEFGHJKMNPQRSTVWXYZ")).toBe(true);
+  expect(isBase32("0OIJ")).toBe(false); // O and I are not allowed
   expect(isBase32("ABC DEF")).toBe(true); // Spaces are allowed
-  expect(isBase32("abc")).toBe(false); // Lowercase not in Crockford alphabet
   expect(isBase32("ABC!")).toBe(false); // Special characters not allowed
   expect(isBase32("")).toBe(true);
 });
@@ -229,20 +229,22 @@ export function isBase64(input: string): boolean {
 import.meta.vitest?.test("isBase64", ({ expect }) => {
   expect(isBase64("SGVsbG8gV29ybGQ=")).toBe(true);
   expect(isBase64("SGVsbG8gV29ybGQ")).toBe(false); // No padding
-  expect(isBase64("SGVsbG8gV29ybGQ==")).toBe(true);
+  expect(isBase64("SGVsbG8gV29ybGQ==")).toBe(false); // Wrong padding
   expect(isBase64("SGVsbG8!V29ybGQ=")).toBe(false); // Invalid character
   expect(isBase64("")).toBe(true);
 });
 
 export function isBase64Url(input: string): boolean {
+  if (input === "") {
+    return true;
+  }
   const regex = /^[0-9a-zA-Z_-]+$/;
   return regex.test(input);
 }
 import.meta.vitest?.test("isBase64Url", ({ expect }) => {
-  expect(isBase64Url("SGVsbG8gV29ybGQ")).toBe(false); // Space is not valid
-  expect(isBase64Url("SGVsbG8_V29ybGQ")).toBe(false); // Invalid character
+  expect(isBase64Url("SGVsbG8gV2 9ybGQ")).toBe(false); // Space is not valid
+  expect(isBase64Url("SGVsbG8_V29ybGQ")).toBe(true); // _ is a valid character
   expect(isBase64Url("SGVsbG8-V29ybGQ")).toBe(true); // - is valid
   expect(isBase64Url("SGVsbG8_V29ybGQ=")).toBe(false); // = not allowed
-  expect(isBase64Url("SGVsbG8_V2 9ybGQ")).toBe(false); // space not allowed
   expect(isBase64Url("")).toBe(true); // Empty string is valid
 });
diff --git a/packages/stack-shared/tsconfig.json b/packages/stack-shared/tsconfig.json
index 6a98bd1aba..df0bb61e92 100644
--- a/packages/stack-shared/tsconfig.json
+++ b/packages/stack-shared/tsconfig.json
@@ -4,6 +4,7 @@
     "outDir": "dist",
     "rootDir": "src",
     "declaration": true,
+    "declarationMap": true,
     "target": "ES2021",
     "lib": ["DOM", "DOM.Iterable", "ES2021", "ES2022.Error"],
     "module": "ES2020",
diff --git a/packages/stack-ui/package.json b/packages/stack-ui/package.json
index baeb554d6c..290f531429 100644
--- a/packages/stack-ui/package.json
+++ b/packages/stack-ui/package.json
@@ -79,8 +79,8 @@
     "date-fns": "^3.6.0",
     "export-to-csv": "^1.4.0",
     "input-otp": "^1.4.1",
-    "lucide-react": "^0.378.0",
-    "react-day-picker": "^8.10.1",
+    "lucide-react": "^0.508.0",
+    "react-day-picker": "^9.6.7",
     "react-hook-form": "^7.53.1",
     "react-resizable-panels": "^2.1.6",
     "tailwind-merge": "^2.5.4"
diff --git a/packages/stack-ui/src/components/ui/calendar.tsx b/packages/stack-ui/src/components/ui/calendar.tsx
index 0d2bf5b7c3..3c2e7db3f2 100644
--- a/packages/stack-ui/src/components/ui/calendar.tsx
+++ b/packages/stack-ui/src/components/ui/calendar.tsx
@@ -1,7 +1,7 @@
 "use client";
 
-import React from "react";
 import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
+import React from "react";
 import { DayPicker } from "react-day-picker";
 
 import { cn } from "../../lib/utils";
@@ -20,48 +20,53 @@ function Calendar({
       showOutsideDays={showOutsideDays}
       className={cn("stack-scope p-3", className)}
       classNames={{
-        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
+        months: "flex flex-col sm:flex-row",
         month: "space-y-4",
-        caption: "flex justify-center pt-1 relative items-center",
+        month_caption: "flex justify-center pt-1 relative items-center",
         caption_label: "text-sm font-medium",
-        nav: "space-x-1 flex items-center",
-        nav_button: cn(
+        nav: "absolute left-0 right-0",
+        button_previous: cn(
+          buttonVariants({ variant: "outline" }),
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-3 z-10"
+        ),
+        button_next: cn(
           buttonVariants({ variant: "outline" }),
-          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-3 z-10"
         ),
-        nav_button_previous: "absolute left-1",
-        nav_button_next: "absolute right-1",
-        table: "w-full border-collapse space-y-1",
-        head_row: "flex",
-        head_cell:
+        month_grid: "w-full border-collapse space-y-1",
+        weekdays: "flex",
+        weekday:
           "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
-        row: "flex w-full mt-2",
-        cell: cn(
-          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
+        week: "flex w-full mt-2",
+        day: cn(
+          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].outside)]:bg-accent/50 [&:has([aria-selected].range_end)]:rounded-r-md",
           props.mode === "range"
-            ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+            ? "[&:has(>.range_end)]:rounded-r-md [&:has(>.range_start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
             : "[&:has([aria-selected])]:rounded-md"
         ),
-        day: cn(
+        day_button: cn(
           buttonVariants({ variant: "ghost" }),
-          "h-8 w-8 p-0 font-normal aria-selected:opacity-100"
+          "h-8 w-8 p-0 font-normal aria-selected:opacity-100 hover:bg-primary hover:text-primary-foreground"
         ),
-        day_range_start: "day-range-start",
-        day_range_end: "day-range-end",
-        day_selected:
-          "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
-        day_today: "bg-accent text-accent-foreground",
-        day_outside:
-          "day-outside text-muted-foreground opacity-50  aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
-        day_disabled: "text-muted-foreground opacity-50",
-        day_range_middle:
+        range_start: "range_start",
+        range_end: "range_end",
+        selected:
+          "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground rounded-md",
+        outside:
+          "opacity-50",
+        disabled: "text-muted-foreground opacity-50",
+        range_middle:
           "aria-selected:bg-accent aria-selected:text-accent-foreground",
-        day_hidden: "invisible",
+        hidden: "invisible",
         ...classNames,
       }}
       components={{
-        IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
-        IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
+        Chevron: (props) => {
+          if (props.orientation === "left") {
+            return <ChevronLeftIcon className="h-4 w-4" />;
+          }
+          return <ChevronRightIcon className="h-4 w-4" />;
+        },
       }}
       {...props}
     />
diff --git a/packages/stack-ui/tsconfig.json b/packages/stack-ui/tsconfig.json
index 6a98bd1aba..df0bb61e92 100644
--- a/packages/stack-ui/tsconfig.json
+++ b/packages/stack-ui/tsconfig.json
@@ -4,6 +4,7 @@
     "outDir": "dist",
     "rootDir": "src",
     "declaration": true,
+    "declarationMap": true,
     "target": "ES2021",
     "lib": ["DOM", "DOM.Iterable", "ES2021", "ES2022.Error"],
     "module": "ES2020",
diff --git a/packages/stack/package.json b/packages/stack/package.json
index 339f09c201..be5f34aeca 100644
--- a/packages/stack/package.json
+++ b/packages/stack/package.json
@@ -54,7 +54,7 @@
     "oauth4webapi": "^2.10.3",
     "@oslojs/otp": "^1.1.0",
     "qrcode": "^1.5.4",
-    "react-avatar-editor": "^13.0.2",
+    "react-easy-crop": "^5.4.1",
     "react-hook-form": "^7.51.4",
     "rimraf": "^5.0.5",
     "tailwindcss-animate": "^1.0.7",
diff --git a/packages/template/package-template.json b/packages/template/package-template.json
index 82adac3d3e..ee6aedc134 100644
--- a/packages/template/package-template.json
+++ b/packages/template/package-template.json
@@ -87,7 +87,7 @@
     "@oslojs/otp": "^1.1.0",
     "qrcode": "^1.5.4",
     "//": "NEXT_LINE_PLATFORM react-like",
-    "react-avatar-editor": "^13.0.2",
+    "react-easy-crop": "^5.4.1",
     "//": "NEXT_LINE_PLATFORM react-like",
     "react-hook-form": "^7.51.4",
     "rimraf": "^5.0.5",
diff --git a/packages/template/package.json b/packages/template/package.json
index ba6e354e24..409a930c07 100644
--- a/packages/template/package.json
+++ b/packages/template/package.json
@@ -59,7 +59,7 @@
     "oauth4webapi": "^2.10.3",
     "@oslojs/otp": "^1.1.0",
     "qrcode": "^1.5.4",
-    "react-avatar-editor": "^13.0.2",
+    "react-easy-crop": "^5.4.1",
     "react-hook-form": "^7.51.4",
     "rimraf": "^5.0.5",
     "tailwindcss-animate": "^1.0.7",
diff --git a/packages/template/src/components/profile-image-editor.tsx b/packages/template/src/components/profile-image-editor.tsx
index b30d4b9cef..533e20455e 100644
--- a/packages/template/src/components/profile-image-editor.tsx
+++ b/packages/template/src/components/profile-image-editor.tsx
@@ -3,8 +3,8 @@ import { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/
 import { Button, Slider, Typography } from '@stackframe/stack-ui';
 import imageCompression from 'browser-image-compression';
 import { Upload } from 'lucide-react';
-import { ComponentProps, useRef, useState } from 'react';
-import AvatarEditor from 'react-avatar-editor';
+import { ComponentProps, useCallback, useState } from 'react';
+import Cropper, { Area } from 'react-easy-crop';
 import { useTranslation } from '../lib/translations';
 import { UserAvatar } from './elements/user-avatar';
 
@@ -18,22 +18,81 @@ export async function checkImageUrl(url: string){
   }
 }
 
+const createImage = (url: string): Promise<HTMLImageElement> =>
+  new Promise((resolve, reject) => {
+    const image = new Image();
+    image.addEventListener('load', () => resolve(image));
+    image.addEventListener('error', (error) => reject(error));
+    image.setAttribute('crossOrigin', 'anonymous');
+    image.src = url;
+  });
+
+export async function getCroppedImg(imageSrc: string, pixelCrop: Area): Promise<string | null> {
+  const image = await createImage(imageSrc);
+  const canvas = document.createElement('canvas');
+  const ctx = canvas.getContext('2d');
+
+  if (!ctx) {
+    return null;
+  }
+
+  const safeCrop = {
+    x: Math.max(0, pixelCrop.x),
+    y: Math.max(0, pixelCrop.y),
+    width: Math.max(1, pixelCrop.width),
+    height: Math.max(1, pixelCrop.height),
+  };
+
+  canvas.width = safeCrop.width;
+  canvas.height = safeCrop.height;
+
+  ctx.drawImage(
+    image,
+    safeCrop.x,
+    safeCrop.y,
+    safeCrop.width,
+    safeCrop.height,
+    0,
+    0,
+    safeCrop.width,
+    safeCrop.height
+  );
+
+  return canvas.toDataURL('image/jpeg');
+}
+
 export function ProfileImageEditor(props: {
   user: NonNullable<ComponentProps<typeof UserAvatar>['user']>,
   onProfileImageUrlChange: (profileImageUrl: string | null) => void | Promise<void>,
 }) {
   const { t } = useTranslation();
-  const cropRef = useRef<AvatarEditor>(null);
-  const [slideValue, setSlideValue] = useState(1);
   const [rawUrl, setRawUrl] = useState<string | null>(null);
   const [error, setError] = useState<string | null>(null);
+  const [crop, setCrop] = useState({ x: 0, y: 0 });
+  const [zoom, setZoom] = useState(1);
+  const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
 
   function reset() {
-    setSlideValue(1);
     setRawUrl(null);
     setError(null);
+    setCrop({ x: 0, y: 0 });
+    setZoom(1);
+    setCroppedAreaPixels(null);
   }
 
+  const onCropChange = useCallback((crop: { x: number, y: number }) => {
+    setCrop(crop);
+  }, []);
+
+  const onCropComplete = useCallback((croppedArea: Area, croppedAreaPixels: Area) => {
+    setCroppedAreaPixels(croppedAreaPixels);
+  }, []);
+
+  const onZoomChange = useCallback((zoom: number) => {
+    setZoom(zoom);
+  }, []);
+
+
   function upload() {
     const input = document.createElement('input');
     input.type = 'file';
@@ -74,40 +133,46 @@ export function ProfileImageEditor(props: {
 
   return (
     <div className='flex flex-col items-center gap-4'>
-      <AvatarEditor
-        ref={cropRef}
-        image={rawUrl || props.user.profileImageUrl || ""}
-        borderRadius={1000}
-        color={[0, 0, 0, 0.72]}
-        scale={slideValue}
-        rotate={0}
-        border={20}
-        className='border'
-      />
+      <div className='relative w-64 h-64'>
+        <Cropper
+          image={rawUrl || props.user.profileImageUrl || ""}
+          crop={crop}
+          zoom={zoom}
+          aspect={1}
+          cropShape="round"
+          showGrid={false}
+          onCropChange={onCropChange}
+          onCropComplete={onCropComplete}
+          onZoomChange={onZoomChange}
+        />
+      </div>
       <Slider
         min={1}
-        max={5}
+        max={3}
         step={0.1}
-        defaultValue={[slideValue]}
-        value={[slideValue]}
-        onValueChange={(v) => setSlideValue(v[0])}
+        value={[zoom]}
+        onValueChange={(v) => onZoomChange(v[0])}
       />
 
       <div className='flex flex-row gap-2'>
         <Button
           onClick={async () => {
-            if (cropRef.current && rawUrl) {
-              const croppedUrl = cropRef.current.getImage().toDataURL('image/jpeg');
-              const compressedFile = await imageCompression(
-                await imageCompression.getFilefromDataUrl(croppedUrl, 'profile-image'),
-                {
-                  maxSizeMB: 0.1,
-                  fileType: "image/jpeg",
-                }
-              );
-              const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);
-              await props.onProfileImageUrlChange(compressedUrl);
-              reset();
+            if (rawUrl && croppedAreaPixels) {
+              const croppedImageUrl = await getCroppedImg(rawUrl, croppedAreaPixels);
+              if (croppedImageUrl) {
+                const compressedFile = await imageCompression(
+                  await imageCompression.getFilefromDataUrl(croppedImageUrl, 'profile-image'),
+                  {
+                    maxSizeMB: 0.1,
+                    fileType: "image/jpeg",
+                  }
+                );
+                const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);
+                await props.onProfileImageUrlChange(compressedUrl);
+                reset();
+              } else {
+                setError(t('Could not crop image.'));
+              }
             }
           }}
         >
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 759cc37c7d..5420dc2564 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -326,8 +326,8 @@ importers:
         specifier: ^4.17.21
         version: 4.17.21
       lucide-react:
-        specifier: ^0.378.0
-        version: 0.378.0(react@19.0.0)
+        specifier: ^0.508.0
+        version: 0.508.0(react@19.0.0)
       next:
         specifier: 15.2.3
         version: 15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -565,6 +565,9 @@ importers:
         specifier: ^5.3.0
         version: 5.3.0(react@19.0.0)
     devDependencies:
+      '@next/bundle-analyzer':
+        specifier: 15.2.3
+        version: 15.2.3
       '@types/react':
         specifier: ^18.2.0
         version: 18.3.12
@@ -1017,9 +1020,9 @@ importers:
       qrcode:
         specifier: ^1.5.4
         version: 1.5.4
-      react-avatar-editor:
-        specifier: ^13.0.2
-        version: 13.0.2(@babel/core@7.26.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      react-easy-crop:
+        specifier: ^5.4.1
+        version: 5.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react-hook-form:
         specifier: ^7.51.4
         version: 7.53.1(react@18.3.1)
@@ -1038,7 +1041,7 @@ importers:
     devDependencies:
       '@quetzallabs/i18n':
         specifier: ^0.1.19
-        version: 0.1.19(next@15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
+        version: 0.1.19(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
       '@types/color':
         specifier: ^3.0.6
         version: 3.0.6
@@ -1141,9 +1144,9 @@ importers:
       qrcode:
         specifier: ^1.5.4
         version: 1.5.4
-      react-avatar-editor:
-        specifier: ^13.0.2
-        version: 13.0.2(@babel/core@7.26.9)(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
+      react-easy-crop:
+        specifier: ^5.4.1
+        version: 5.4.1(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
       react-hook-form:
         specifier: ^7.51.4
         version: 7.53.1(react@18.2.0)
@@ -1162,7 +1165,7 @@ importers:
     devDependencies:
       '@quetzallabs/i18n':
         specifier: ^0.1.19
-        version: 0.1.19(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))
+        version: 0.1.19(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))
       '@types/color':
         specifier: ^3.0.6
         version: 3.0.6
@@ -1195,7 +1198,7 @@ importers:
         version: 9.0.2
       next:
         specifier: ^14.1.0
-        version: 14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
+        version: 14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
       postcss:
         specifier: ^8.4.38
         version: 8.4.47
@@ -1239,8 +1242,8 @@ importers:
         specifier: ^4.7.8
         version: 4.7.8
       lucide-react:
-        specifier: ^0.378.0
-        version: 0.378.0(react@18.2.0)
+        specifier: ^0.508.0
+        version: 0.508.0(react@18.2.0)
       react-colorful:
         specifier: ^5.6.1
         version: 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -1476,11 +1479,11 @@ importers:
         specifier: ^1.4.1
         version: 1.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       lucide-react:
-        specifier: ^0.378.0
-        version: 0.378.0(react@18.2.0)
+        specifier: ^0.508.0
+        version: 0.508.0(react@18.2.0)
       react-day-picker:
-        specifier: ^8.10.1
-        version: 8.10.1(date-fns@3.6.0)(react@18.2.0)
+        specifier: ^9.6.7
+        version: 9.6.7(react@18.2.0)
       react-hook-form:
         specifier: ^7.53.1
         version: 7.53.1(react@18.2.0)
@@ -1563,9 +1566,9 @@ importers:
       qrcode:
         specifier: ^1.5.4
         version: 1.5.4
-      react-avatar-editor:
-        specifier: ^13.0.2
-        version: 13.0.2(@babel/core@7.26.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      react-easy-crop:
+        specifier: ^5.4.1
+        version: 5.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react-hook-form:
         specifier: ^7.51.4
         version: 7.53.1(react@18.3.1)
@@ -1584,7 +1587,7 @@ importers:
     devDependencies:
       '@quetzallabs/i18n':
         specifier: ^0.1.19
-        version: 0.1.19(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
+        version: 0.1.19(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
       '@types/color':
         specifier: ^3.0.6
         version: 3.0.6
@@ -1617,7 +1620,7 @@ importers:
         version: 9.0.2
       next:
         specifier: ^14.1.0
-        version: 14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       postcss:
         specifier: ^8.4.38
         version: 8.4.47
@@ -1697,11 +1700,6 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/helper-define-polyfill-provider@0.6.3':
-    resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
   '@babel/helper-member-expression-to-functions@7.24.8':
     resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==}
     engines: {node: '>=6.9.0'}
@@ -1732,10 +1730,6 @@ packages:
     resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-plugin-utils@7.26.5':
-    resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-replace-supers@7.25.0':
     resolution: {integrity: sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==}
     engines: {node: '>=6.9.0'}
@@ -1818,12 +1812,6 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-runtime@7.25.9':
-    resolution: {integrity: sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-
   '@babel/runtime@7.24.7':
     resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
     engines: {node: '>=6.9.0'}
@@ -1923,6 +1911,9 @@ packages:
   '@changesets/write@0.3.2':
     resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==}
 
+  '@date-fns/tz@1.2.0':
+    resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
+
   '@discoveryjs/json-ext@0.5.7':
     resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
     engines: {node: '>=10.0.0'}
@@ -6521,21 +6512,6 @@ packages:
     resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
     engines: {node: '>=10', npm: '>=6'}
 
-  babel-plugin-polyfill-corejs2@0.4.12:
-    resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
-  babel-plugin-polyfill-corejs3@0.10.6:
-    resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
-  babel-plugin-polyfill-regenerator@0.6.3:
-    resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
@@ -7064,9 +7040,6 @@ packages:
     resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
     engines: {node: '>= 0.8'}
 
-  core-js-compat@3.40.0:
-    resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==}
-
   core-js@3.41.0:
     resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
 
@@ -7214,6 +7187,9 @@ packages:
     resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
     engines: {node: '>= 0.4'}
 
+  date-fns-jalali@4.1.0-0:
+    resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
+
   date-fns@2.30.0:
     resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
     engines: {node: '>=0.11'}
@@ -7221,6 +7197,9 @@ packages:
   date-fns@3.6.0:
     resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
 
+  date-fns@4.1.0:
+    resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
+
   debounce@1.2.1:
     resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
 
@@ -9124,6 +9103,11 @@ packages:
     peerDependencies:
       react: ^16.5.1 || ^17.0.0 || ^18.0.0
 
+  lucide-react@0.508.0:
+    resolution: {integrity: sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg==}
+    peerDependencies:
+      react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
   lz-string@1.5.0:
     resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
     hasBin: true
@@ -9571,6 +9555,9 @@ packages:
     resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
     engines: {node: '>=14.16'}
 
+  normalize-wheel@1.0.1:
+    resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==}
+
   now-and-later@3.0.0:
     resolution: {integrity: sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==}
     engines: {node: '>= 10.13.0'}
@@ -10177,23 +10164,17 @@ packages:
     resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
     hasBin: true
 
-  react-avatar-editor@13.0.2:
-    resolution: {integrity: sha512-a4ajbi7lwDh98kgEtSEeKMu0vs0CHTczkq4Xcxr1EiwMFH1GlgHCEtwGU8q/H5W8SeLnH4KPK8LUjEEaZXklxQ==}
-    peerDependencies:
-      react: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-
   react-colorful@5.6.1:
     resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
     peerDependencies:
       react: '>=16.8.0'
       react-dom: '>=16.8.0'
 
-  react-day-picker@8.10.1:
-    resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
+  react-day-picker@9.6.7:
+    resolution: {integrity: sha512-rCSt6X8FXQWpjykns/azRXjJk3cMSzkzGbDEXuEveFGNZgOjZULdJQ5wsu8Zfyo8ZgPBoYCBKQ5wRrgJfhJGbg==}
+    engines: {node: '>=18'}
     peerDependencies:
-      date-fns: ^2.28.0 || ^3.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: '>=16.8.0'
 
   react-dom@18.2.0:
     resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
@@ -10210,6 +10191,12 @@ packages:
     peerDependencies:
       react: ^19.0.0
 
+  react-easy-crop@5.4.1:
+    resolution: {integrity: sha512-Djtsi7bWO75vkKYkVxNRrJWY69pXLahIAkUN0mmt9cXNnaq2tpG59ctSY6P7ipJgBc7COJDRMRuwb2lYwtACNQ==}
+    peerDependencies:
+      react: '>=16.4.0'
+      react-dom: '>=16.4.0'
+
   react-email@2.1.0:
     resolution: {integrity: sha512-fTt85ca1phsBu57iy32wn4LTR37rOzDZoY2AOWVq3JQYVwk6GlBdUuQWif2cudkwWINL9COf9kRMS4/QWtKtAQ==}
     engines: {node: '>=18.0.0'}
@@ -12106,17 +12093,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.9)':
-    dependencies:
-      '@babel/core': 7.26.9
-      '@babel/helper-compilation-targets': 7.26.5
-      '@babel/helper-plugin-utils': 7.26.5
-      debug: 4.4.0
-      lodash.debounce: 4.0.8
-      resolve: 1.22.8
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/helper-member-expression-to-functions@7.24.8':
     dependencies:
       '@babel/traverse': 7.26.9
@@ -12164,8 +12140,6 @@ snapshots:
 
   '@babel/helper-plugin-utils@7.25.9': {}
 
-  '@babel/helper-plugin-utils@7.26.5': {}
-
   '@babel/helper-replace-supers@7.25.0(@babel/core@7.26.0)':
     dependencies:
       '@babel/core': 7.26.0
@@ -12256,18 +12230,6 @@ snapshots:
       '@babel/core': 7.26.9
       '@babel/helper-plugin-utils': 7.25.9
 
-  '@babel/plugin-transform-runtime@7.25.9(@babel/core@7.26.9)':
-    dependencies:
-      '@babel/core': 7.26.9
-      '@babel/helper-module-imports': 7.25.9
-      '@babel/helper-plugin-utils': 7.26.5
-      babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.9)
-      babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.9)
-      babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.9)
-      semver: 6.3.1
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/runtime@7.24.7':
     dependencies:
       regenerator-runtime: 0.14.1
@@ -12488,6 +12450,8 @@ snapshots:
       human-id: 1.0.2
       prettier: 2.8.8
 
+  '@date-fns/tz@1.2.0': {}
+
   '@discoveryjs/json-ext@0.5.7': {}
 
   '@emnapi/runtime@1.2.0':
@@ -14223,7 +14187,7 @@ snapshots:
 
   '@protobufjs/utf8@1.1.0': {}
 
-  '@quetzallabs/i18n@0.1.19(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))':
+  '@quetzallabs/i18n@0.1.19(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))':
     dependencies:
       '@babel/parser': 7.25.6
       '@babel/traverse': 7.25.6
@@ -14231,7 +14195,7 @@ snapshots:
       dotenv: 10.0.0
       i18next: 21.10.0
       i18next-parser: 9.0.2
-      next-intl: 3.19.1(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))(react@18.3.1)
+      next-intl: 3.19.1(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))(react@18.3.1)
       path: 0.12.7
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
@@ -14240,7 +14204,7 @@ snapshots:
       - next
       - supports-color
 
-  '@quetzallabs/i18n@0.1.19(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
+  '@quetzallabs/i18n@0.1.19(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
     dependencies:
       '@babel/parser': 7.25.6
       '@babel/traverse': 7.25.6
@@ -14248,7 +14212,7 @@ snapshots:
       dotenv: 10.0.0
       i18next: 21.10.0
       i18next-parser: 9.0.2
-      next-intl: 3.19.1(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
+      next-intl: 3.19.1(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
       path: 0.12.7
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
@@ -14257,7 +14221,7 @@ snapshots:
       - next
       - supports-color
 
-  '@quetzallabs/i18n@0.1.19(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))':
+  '@quetzallabs/i18n@0.1.19(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
     dependencies:
       '@babel/parser': 7.25.6
       '@babel/traverse': 7.25.6
@@ -14265,7 +14229,7 @@ snapshots:
       dotenv: 10.0.0
       i18next: 21.10.0
       i18next-parser: 9.0.2
-      next-intl: 3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))(react@18.3.1)
+      next-intl: 3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
       path: 0.12.7
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
@@ -14274,7 +14238,7 @@ snapshots:
       - next
       - supports-color
 
-  '@quetzallabs/i18n@0.1.19(next@15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
+  '@quetzallabs/i18n@0.1.19(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))':
     dependencies:
       '@babel/parser': 7.25.6
       '@babel/traverse': 7.25.6
@@ -14282,7 +14246,7 @@ snapshots:
       dotenv: 10.0.0
       i18next: 21.10.0
       i18next-parser: 9.0.2
-      next-intl: 3.19.1(next@15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
+      next-intl: 3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))(react@18.3.1)
       path: 0.12.7
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
@@ -17141,30 +17105,6 @@ snapshots:
       cosmiconfig: 7.1.0
       resolve: 1.22.8
 
-  babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.9):
-    dependencies:
-      '@babel/compat-data': 7.26.8
-      '@babel/core': 7.26.9
-      '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.9)
-      semver: 6.3.1
-    transitivePeerDependencies:
-      - supports-color
-
-  babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.9):
-    dependencies:
-      '@babel/core': 7.26.9
-      '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.9)
-      core-js-compat: 3.40.0
-    transitivePeerDependencies:
-      - supports-color
-
-  babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.9):
-    dependencies:
-      '@babel/core': 7.26.9
-      '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.9)
-    transitivePeerDependencies:
-      - supports-color
-
   balanced-match@1.0.2: {}
 
   bare-events@2.4.2:
@@ -17750,10 +17690,6 @@ snapshots:
       depd: 2.0.0
       keygrip: 1.1.0
 
-  core-js-compat@3.40.0:
-    dependencies:
-      browserslist: 4.24.4
-
   core-js@3.41.0: {}
 
   core-util-is@1.0.3: {}
@@ -17915,12 +17851,16 @@ snapshots:
       es-errors: 1.3.0
       is-data-view: 1.0.1
 
+  date-fns-jalali@4.1.0-0: {}
+
   date-fns@2.30.0:
     dependencies:
       '@babel/runtime': 7.24.7
 
   date-fns@3.6.0: {}
 
+  date-fns@4.1.0: {}
+
   debounce@1.2.1: {}
 
   debounce@2.0.0: {}
@@ -20293,7 +20233,11 @@ snapshots:
     dependencies:
       react: 18.3.1
 
-  lucide-react@0.378.0(react@19.0.0):
+  lucide-react@0.508.0(react@18.2.0):
+    dependencies:
+      react: 18.2.0
+
+  lucide-react@0.508.0(react@19.0.0):
     dependencies:
       react: 19.0.0
 
@@ -20504,35 +20448,35 @@ snapshots:
 
   neo-async@2.6.2: {}
 
-  next-intl@3.19.1(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))(react@18.3.1):
+  next-intl@3.19.1(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0))(react@18.3.1):
     dependencies:
       '@formatjs/intl-localematcher': 0.5.4
       negotiator: 0.6.3
-      next: 14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
+      next: 14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0)
       react: 18.3.1
       use-intl: 3.19.1(react@18.3.1)
 
-  next-intl@3.19.1(next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
+  next-intl@3.19.1(next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
     dependencies:
       '@formatjs/intl-localematcher': 0.5.4
       negotiator: 0.6.3
-      next: 14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      next: 14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react: 18.3.1
       use-intl: 3.19.1(react@18.3.1)
 
-  next-intl@3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))(react@18.3.1):
+  next-intl@3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
     dependencies:
       '@formatjs/intl-localematcher': 0.5.4
       negotiator: 0.6.3
-      next: 15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1)
+      next: 15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react: 18.3.1
       use-intl: 3.19.1(react@18.3.1)
 
-  next-intl@3.19.1(next@15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
+  next-intl@3.19.1(next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1))(react@18.3.1):
     dependencies:
       '@formatjs/intl-localematcher': 0.5.4
       negotiator: 0.6.3
-      next: 15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      next: 15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1)
       react: 18.3.1
       use-intl: 3.19.1(react@18.3.1)
 
@@ -20657,33 +20601,7 @@ snapshots:
       - '@babel/core'
       - babel-plugin-macros
 
-  next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
-    dependencies:
-      '@next/env': 14.2.5
-      '@swc/helpers': 0.5.5
-      busboy: 1.6.0
-      caniuse-lite: 1.0.30001678
-      graceful-fs: 4.2.11
-      postcss: 8.4.31
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      styled-jsx: 5.1.1(@babel/core@7.26.0)(react@18.3.1)
-    optionalDependencies:
-      '@next/swc-darwin-arm64': 14.2.5
-      '@next/swc-darwin-x64': 14.2.5
-      '@next/swc-linux-arm64-gnu': 14.2.5
-      '@next/swc-linux-arm64-musl': 14.2.5
-      '@next/swc-linux-x64-gnu': 14.2.5
-      '@next/swc-linux-x64-musl': 14.2.5
-      '@next/swc-win32-arm64-msvc': 14.2.5
-      '@next/swc-win32-ia32-msvc': 14.2.5
-      '@next/swc-win32-x64-msvc': 14.2.5
-      '@opentelemetry/api': 1.9.0
-    transitivePeerDependencies:
-      - '@babel/core'
-      - babel-plugin-macros
-
-  next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0):
+  next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0):
     dependencies:
       '@next/env': 14.2.5
       '@swc/helpers': 0.5.5
@@ -20693,7 +20611,7 @@ snapshots:
       postcss: 8.4.31
       react: 18.2.0
       react-dom: 18.3.1(react@18.2.0)
-      styled-jsx: 5.1.1(@babel/core@7.26.9)(react@18.2.0)
+      styled-jsx: 5.1.1(@babel/core@7.26.0)(react@18.2.0)
     optionalDependencies:
       '@next/swc-darwin-arm64': 14.2.5
       '@next/swc-darwin-x64': 14.2.5
@@ -20709,7 +20627,7 @@ snapshots:
       - '@babel/core'
       - babel-plugin-macros
 
-  next@14.2.5(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  next@14.2.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@next/env': 14.2.5
       '@swc/helpers': 0.5.5
@@ -20719,7 +20637,7 @@ snapshots:
       postcss: 8.4.31
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      styled-jsx: 5.1.1(@babel/core@7.26.9)(react@18.3.1)
+      styled-jsx: 5.1.1(@babel/core@7.26.0)(react@18.3.1)
     optionalDependencies:
       '@next/swc-darwin-arm64': 14.2.5
       '@next/swc-darwin-x64': 14.2.5
@@ -20788,7 +20706,7 @@ snapshots:
       - '@babel/core'
       - babel-plugin-macros
 
-  next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1):
+  next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@next/env': 15.2.3
       '@swc/counter': 0.1.3
@@ -20797,7 +20715,7 @@ snapshots:
       caniuse-lite: 1.0.30001696
       postcss: 8.4.31
       react: 18.3.1
-      react-dom: 19.0.0(react@18.3.1)
+      react-dom: 18.3.1(react@18.3.1)
       styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1)
     optionalDependencies:
       '@next/swc-darwin-arm64': 15.2.3
@@ -20814,7 +20732,7 @@ snapshots:
       - '@babel/core'
       - babel-plugin-macros
 
-  next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
+  next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@18.3.1))(react@18.3.1):
     dependencies:
       '@next/env': 15.2.3
       '@swc/counter': 0.1.3
@@ -20822,9 +20740,9 @@ snapshots:
       busboy: 1.6.0
       caniuse-lite: 1.0.30001696
       postcss: 8.4.31
-      react: 19.0.0
-      react-dom: 19.0.0(react@19.0.0)
-      styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.0.0)
+      react: 18.3.1
+      react-dom: 19.0.0(react@18.3.1)
+      styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1)
     optionalDependencies:
       '@next/swc-darwin-arm64': 15.2.3
       '@next/swc-darwin-x64': 15.2.3
@@ -20840,7 +20758,7 @@ snapshots:
       - '@babel/core'
       - babel-plugin-macros
 
-  next@15.2.3(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  next@15.2.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
     dependencies:
       '@next/env': 15.2.3
       '@swc/counter': 0.1.3
@@ -20848,9 +20766,9 @@ snapshots:
       busboy: 1.6.0
       caniuse-lite: 1.0.30001696
       postcss: 8.4.31
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      styled-jsx: 5.1.6(@babel/core@7.26.9)(react@18.3.1)
+      react: 19.0.0
+      react-dom: 19.0.0(react@19.0.0)
+      styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.0.0)
     optionalDependencies:
       '@next/swc-darwin-arm64': 15.2.3
       '@next/swc-darwin-x64': 15.2.3
@@ -20905,6 +20823,8 @@ snapshots:
 
   normalize-url@8.0.1: {}
 
+  normalize-wheel@1.0.1: {}
+
   now-and-later@3.0.0:
     dependencies:
       once: 1.4.0
@@ -21577,36 +21497,16 @@ snapshots:
       minimist: 1.2.8
       strip-json-comments: 2.0.1
 
-  react-avatar-editor@13.0.2(@babel/core@7.26.9)(react-dom@18.3.1(react@18.2.0))(react@18.2.0):
-    dependencies:
-      '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.9)
-      '@babel/runtime': 7.26.0
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.3.1(react@18.2.0)
-    transitivePeerDependencies:
-      - '@babel/core'
-      - supports-color
-
-  react-avatar-editor@13.0.2(@babel/core@7.26.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
-    dependencies:
-      '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.9)
-      '@babel/runtime': 7.26.0
-      prop-types: 15.8.1
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    transitivePeerDependencies:
-      - '@babel/core'
-      - supports-color
-
   react-colorful@5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
     dependencies:
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
 
-  react-day-picker@8.10.1(date-fns@3.6.0)(react@18.2.0):
+  react-day-picker@9.6.7(react@18.2.0):
     dependencies:
-      date-fns: 3.6.0
+      '@date-fns/tz': 1.2.0
+      date-fns: 4.1.0
+      date-fns-jalali: 4.1.0-0
       react: 18.2.0
 
   react-dom@18.2.0(react@18.2.0):
@@ -21637,6 +21537,20 @@ snapshots:
       react: 19.0.0
       scheduler: 0.25.0
 
+  react-easy-crop@5.4.1(react-dom@18.3.1(react@18.2.0))(react@18.2.0):
+    dependencies:
+      normalize-wheel: 1.0.1
+      react: 18.2.0
+      react-dom: 18.3.1(react@18.2.0)
+      tslib: 2.8.1
+
+  react-easy-crop@5.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+    dependencies:
+      normalize-wheel: 1.0.1
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+      tslib: 2.8.1
+
   react-email@2.1.0(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(eslint@9.21.0(jiti@2.4.2)):
     dependencies:
       '@radix-ui/colors': 1.0.1
@@ -22667,20 +22581,6 @@ snapshots:
     optionalDependencies:
       '@babel/core': 7.26.0
 
-  styled-jsx@5.1.1(@babel/core@7.26.9)(react@18.2.0):
-    dependencies:
-      client-only: 0.0.1
-      react: 18.2.0
-    optionalDependencies:
-      '@babel/core': 7.26.9
-
-  styled-jsx@5.1.1(@babel/core@7.26.9)(react@18.3.1):
-    dependencies:
-      client-only: 0.0.1
-      react: 18.3.1
-    optionalDependencies:
-      '@babel/core': 7.26.9
-
   styled-jsx@5.1.6(@babel/core@7.26.0)(react@18.2.0):
     dependencies:
       client-only: 0.0.1
@@ -22702,13 +22602,6 @@ snapshots:
     optionalDependencies:
       '@babel/core': 7.26.0
 
-  styled-jsx@5.1.6(@babel/core@7.26.9)(react@18.3.1):
-    dependencies:
-      client-only: 0.0.1
-      react: 18.3.1
-    optionalDependencies:
-      '@babel/core': 7.26.9
-
   stylis@4.2.0: {}
 
   sucrase@3.35.0:


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

Updated package dependencies and added development tooling improvements across the stack-auth monorepo, with significant changes to the Calendar component.

- Updated `lucide-react` from 0.378.0 to 0.508.0 across multiple packages
- Added bundle analysis capabilities via `@next/bundle-analyzer` in dashboard and demo apps
- Upgraded `react-day-picker` from v8 to v9.6.7 with breaking changes in `/packages/stack-ui/src/components/ui/calendar.tsx`
- Added `declarationMap` support in TypeScript configs for improved debugging
- Modified Calendar component styling with new absolute positioning for navigation buttons and outside date handling

<sub>💡 (2/5) Greptile learns from your feedback when you react with 👍/👎!</sub>

<sub>9 file(s) reviewed, 2 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=stack-auth_665)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T03:26:15Z
user: login: ellipsis-dev[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T03:26:37Z
user: login: ellipsis-dev[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T04:34:26Z
user: login: similiarity
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T17:39:16Z
user: login: fomalhautb
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T19:56:30Z
user: login: N2D4
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:13:51Z

Review comments:
diff_hunk: @@ -20,48 +20,54 @@ function Calendar({
       showOutsideDays={showOutsideDays}
       className={cn("stack-scope p-3", className)}
       classNames={{
-        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
+        months: "flex flex-col sm:flex-row",
         month: "space-y-4",
-        caption: "flex justify-center pt-1 relative items-center",
+        month_caption: "flex justify-center pt-1 relative items-center",
         caption_label: "text-sm font-medium",
-        nav: "space-x-1 flex items-center",
-        nav_button: cn(
+        nav: "absolute left-0 right-0",
+        button_previous: cn(
+          buttonVariants({ variant: "outline" }),
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-3 z-10"
+        ),
+        button_next: cn(
           buttonVariants({ variant: "outline" }),
-          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-3 z-10"
         ),
-        nav_button_previous: "absolute left-1",
-        nav_button_next: "absolute right-1",
-        table: "w-full border-collapse space-y-1",
-        head_row: "flex",
-        head_cell:
+        month_grid: "w-full border-collapse space-y-1",
+        weekdays: "flex",
+        weekday:
           "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
-        row: "flex w-full mt-2",
-        cell: cn(
-          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
+        week: "flex w-full mt-2",
+        day: cn(
+          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].outside)]:bg-accent/50 [&:has([aria-selected].range_end)]:rounded-r-md",
+          "[&:has([data-outside])]:bg-red-500",
path: packages/stack-ui/src/components/ui/calendar.tsx
user: login: greptile-apps[bot]
body: logic: This new style rule makes outside dates have a red background, which seems incorrect and inconsistent with the opacity-based styling defined on line 57.

```suggestion

```
created_at: 2025-05-09T03:25:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,48 +20,54 @@ function Calendar({
       showOutsideDays={showOutsideDays}
       className={cn("stack-scope p-3", className)}
       classNames={{
-        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
+        months: "flex flex-col sm:flex-row",
         month: "space-y-4",
-        caption: "flex justify-center pt-1 relative items-center",
+        month_caption: "flex justify-center pt-1 relative items-center",
         caption_label: "text-sm font-medium",
-        nav: "space-x-1 flex items-center",
-        nav_button: cn(
+        nav: "absolute left-0 right-0",
+        button_previous: cn(
+          buttonVariants({ variant: "outline" }),
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-3 z-10"
+        ),
+        button_next: cn(
           buttonVariants({ variant: "outline" }),
-          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-3 z-10"
         ),
path: packages/stack-ui/src/components/ui/calendar.tsx
user: login: greptile-apps[bot]
body: style: Navigation buttons are now absolutely positioned which could cause layout issues if the parent container is not properly sized. Consider adding a min-height to the nav container.
created_at: 2025-05-09T03:25:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,48 +20,54 @@ function Calendar({
       showOutsideDays={showOutsideDays}
       className={cn("stack-scope p-3", className)}
       classNames={{
-        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
+        months: "flex flex-col sm:flex-row",
         month: "space-y-4",
-        caption: "flex justify-center pt-1 relative items-center",
+        month_caption: "flex justify-center pt-1 relative items-center",
         caption_label: "text-sm font-medium",
-        nav: "space-x-1 flex items-center",
-        nav_button: cn(
+        nav: "absolute left-0 right-0",
+        button_previous: cn(
+          buttonVariants({ variant: "outline" }),
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-3 z-10"
+        ),
+        button_next: cn(
           buttonVariants({ variant: "outline" }),
-          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-3 z-10"
         ),
-        nav_button_previous: "absolute left-1",
-        nav_button_next: "absolute right-1",
-        table: "w-full border-collapse space-y-1",
-        head_row: "flex",
-        head_cell:
+        month_grid: "w-full border-collapse space-y-1",
+        weekdays: "flex",
+        weekday:
           "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
-        row: "flex w-full mt-2",
-        cell: cn(
-          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
+        week: "flex w-full mt-2",
+        day: cn(
+          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].outside)]:bg-accent/50 [&:has([aria-selected].range_end)]:rounded-r-md",
+          "[&:has([data-outside])]:bg-red-500",
path: packages/stack-ui/src/components/ui/calendar.tsx
user: login: ellipsis-dev[bot]
body: The styling rule `[&:has([data-outside])]:bg-red-500` seems unexpected. It applies a red background to elements with a data attribute. Confirm if this is intentional or a leftover debugging style.
```suggestion

```

created_at: 2025-05-09T03:26:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -74,40 +133,46 @@ export function ProfileImageEditor(props: {
 
   return (
     <div className='flex flex-col items-center gap-4'>
-      <AvatarEditor
-        ref={cropRef}
-        image={rawUrl || props.user.profileImageUrl || ""}
-        borderRadius={1000}
-        color={[0, 0, 0, 0.72]}
-        scale={slideValue}
-        rotate={0}
-        border={20}
-        className='border'
-      />
+      <div className='relative w-64 h-64'>
+        <Cropper
+          image={rawUrl || props.user.profileImageUrl || ""}
+          crop={crop}
+          zoom={zoom}
+          aspect={1}
+          cropShape="round"
+          showGrid={false}
+          onCropChange={onCropChange}
+          onCropComplete={onCropComplete}
+          onZoomChange={onZoomChange}
+        />
+      </div>
       <Slider
         min={1}
-        max={5}
+        max={3}
         step={0.1}
-        defaultValue={[slideValue]}
-        value={[slideValue]}
-        onValueChange={(v) => setSlideValue(v[0])}
+        value={[zoom]}
+        onValueChange={(v) => onZoomChange(v[0])}
       />
 
       <div className='flex flex-row gap-2'>
         <Button
           onClick={async () => {
path: packages/template/src/components/profile-image-editor.tsx
user: login: ellipsis-dev[bot]
body: Wrap the async operations in the `Save` button's `onClick` handler in a try-catch (or use `runAsynchronouslyWithAlert`) to catch potential errors from cropping or compression.

<sup>This comment was generated because it violated a code review rule: [mrule_xbtTjv6ytIN2S5sn](https://app.ellipsis.dev/stack-auth/code-review/rules?id=mrule_xbtTjv6ytIN2S5sn).</sup>
created_at: 2025-05-09T04:34:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -74,40 +133,46 @@ export function ProfileImageEditor(props: {
 
   return (
     <div className='flex flex-col items-center gap-4'>
-      <AvatarEditor
-        ref={cropRef}
-        image={rawUrl || props.user.profileImageUrl || ""}
-        borderRadius={1000}
-        color={[0, 0, 0, 0.72]}
-        scale={slideValue}
-        rotate={0}
-        border={20}
-        className='border'
-      />
+      <div className='relative w-64 h-64'>
+        <Cropper
+          image={rawUrl || props.user.profileImageUrl || ""}
+          crop={crop}
+          zoom={zoom}
+          aspect={1}
+          cropShape="round"
+          showGrid={false}
+          onCropChange={onCropChange}
+          onCropComplete={onCropComplete}
+          onZoomChange={onZoomChange}
+        />
+      </div>
       <Slider
         min={1}
-        max={5}
+        max={3}
         step={0.1}
-        defaultValue={[slideValue]}
-        value={[slideValue]}
-        onValueChange={(v) => setSlideValue(v[0])}
+        value={[zoom]}
+        onValueChange={(v) => onZoomChange(v[0])}
       />
 
       <div className='flex flex-row gap-2'>
         <Button
           onClick={async () => {
path: packages/template/src/components/profile-image-editor.tsx
user: login: similiarity
body: The original also didn't have it, should I add it?

created_at: 2025-05-13T17:39:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-09T03:24:20Z
body: [vc]: #hzBss4deacLeLt9juaKTKy15v0/TAXjUD6aqS3w3pwY=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJzdGFjay1iYWNrZW5kIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvYmFja2VuZCIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9teS10ZWFtLTIwYTc5NTFmL3N0YWNrLWJhY2tlbmQvSFNobmtHY0xqY2ZNYUhONEpmaE5SMnF0M1FUWSIsInByZXZpZXdVcmwiOiJzdGFjay1iYWNrZW5kLWdpdC11cGRhdGUtcGFja2FnZXMtbXktdGVhbS0yMGE3OTUxZi52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InN0YWNrLWJhY2tlbmQtZ2l0LXVwZGF0ZS1wYWNrYWdlcy1teS10ZWFtLTIwYTc5NTFmLnZlcmNlbC5hcHAifX0seyJuYW1lIjoic3RhY2stZGVtbyIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy9kZW1vIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL215LXRlYW0tMjBhNzk1MWYvc3RhY2stZGVtby9HSGhhZHJ2N2pvNzRNdXpOWUpTZURTWEh6REVoIiwicHJldmlld1VybCI6InN0YWNrLWRlbW8tZ2l0LXVwZGF0ZS1wYWNrYWdlcy1teS10ZWFtLTIwYTc5NTFmLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoic3RhY2stZGVtby1naXQtdXBkYXRlLXBhY2thZ2VzLW15LXRlYW0tMjBhNzk1MWYudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJzdGFjay1kYXNoYm9hcmQiLCJyb290RGlyZWN0b3J5IjoiYXBwcy9kYXNoYm9hcmQiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbXktdGVhbS0yMGE3OTUxZi9zdGFjay1kYXNoYm9hcmQvSkJCbTJUbzhWOGdzdEJlRFlqQXl5YlpCSHhMWSIsInByZXZpZXdVcmwiOiJzdGFjay1kYXNoYm9hcmQtZ2l0LXVwZGF0ZS1wYWNrYWdlcy1teS10ZWFtLTIwYTc5NTFmLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoic3RhY2stZGFzaGJvYXJkLWdpdC11cGRhdGUtcGFja2FnZXMtbXktdGVhbS0yMGE3OTUxZi52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **stack-backend** | ✅ Ready ([Inspect](https://vercel.com/my-team-20a7951f/stack-backend/HShnkGcLjcfMaHN4JfhNR2qt3QTY)) | [Visit Preview](https://stack-backend-git-update-packages-my-team-20a7951f.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/stack-backend-git-update-packages-my-team-20a7951f.vercel.app?via=pr-comment-feedback-link) | May 16, 2025 8:28pm |
| **stack-dashboard** | ✅ Ready ([Inspect](https://vercel.com/my-team-20a7951f/stack-dashboard/JBBm2To8V8gstBeDYjAyybZBHxLY)) | [Visit Preview](https://stack-dashboard-git-update-packages-my-team-20a7951f.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/stack-dashboard-git-update-packages-my-team-20a7951f.vercel.app?via=pr-comment-feedback-link) | May 16, 2025 8:28pm |
| **stack-demo** | ✅ Ready ([Inspect](https://vercel.com/my-team-20a7951f/stack-demo/GHhadrv7jo74MuzNYJSeDSXHzDEh)) | [Visit Preview](https://stack-demo-git-update-packages-my-team-20a7951f.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/stack-demo-git-update-packages-my-team-20a7951f.vercel.app?via=pr-comment-feedback-link) | May 16, 2025 8:28pm |




author_association: NONE
user: login: recurseml[bot]
created_at: 2025-05-09T03:25:36Z
body: ✨ No issues found! Your code is sparkling clean! ✨

<details>
<summary>🗒️ View all ignored comments in this repo</summary>

* The constraint 'TokenStoreType extends string' is too restrictive. It should likely be 'TokenStoreType extends string | object' to match the condition check in line 113 where TokenStoreType is checked against {}
* Return type mismatch - the interface declares useUsers() returning ServerUser[] but the Team interface that this extends declares useUsers() returning TeamUser[]
* There is a syntax error in the super constructor call due to the ellipsis operator used incorrectly. Objects aren't being merged correctly. This syntax usage can lead to runtime errors when trying to pass the merged object to 'super()'. Verify that the intended alterations to the object occur before or outside of the super() call if needed.
* Throwing an error when no active span is found is too aggressive. The log function should gracefully fallback to console.log or another logging mechanism when there's no active span, since not all execution contexts will have an active span. This makes the code less resilient and could break functionality in non-traced environments.

📚 [Relevant Docs](https://opentelemetry.io/docs/instrumentation/js/instrumentation)
* Function sets backendContext with a new configuration but doesn't pass 'defaultProjectKeys'. Since defaultProjectKeys is required in the type definition and cannot be updated (throws error if tried to set), this will cause a type error.
* The schema is using array syntax for pick() which is incorrect for Yup schemas. The pick() method in Yup expects individual arguments, not an array. Should be changed to: emailConfigSchema.pick('type', 'host', 'port', 'username', 'sender_name', 'sender_email')

📚 [Relevant Docs](https://github.com/jquense/yup#schemapcks-string-schema)
* Creating a refresh token with current timestamp as expiration means it expires immediately. Should set a future date for token expiration.
* The 'tools' object is initialized as an empty object, even though 'tools' is presumably expected to contain tool definitions. This could cause the server capabilities to lack necessary tool configurations, thus potentially impacting functionalities that depend on certain tool setups.

📚 [Relevant Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
* 'STACK_SECRET_SERVER_KEY' is potentially being included in every request header without checking its existence again here. Although it's checked during initialization, this could lead to security issues as it's exposed in all communications where the header is logged or captured.

📚 [Relevant Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
* When adding 'use client' directive at the beginning, it doesn't check if file.text already contains the 'use client' directive. This could lead to duplicate 'use client' directives if the file already has one.

📚 [Relevant Docs](https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive)
</details>
author_association: NONE