stvident commited on
Commit
96a945a
·
verified ·
1 Parent(s): c4cae42

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +77 -0
  2. .gitignore +24 -0
  3. Dockerfile +26 -0
  4. README.md +14 -7
  5. backend/__init__.py +0 -0
  6. backend/db.py +55 -0
  7. backend/main.py +43 -0
  8. backend/requirements.txt +4 -0
  9. backend/routes/__init__.py +0 -0
  10. backend/routes/results.py +122 -0
  11. backend/routes/session.py +167 -0
  12. docs/analyses.md +190 -0
  13. docs/data-storage.md +115 -0
  14. docs/pipeline.md +111 -0
  15. fly.toml +20 -0
  16. frontend/index.html +20 -0
  17. frontend/package-lock.json +2467 -0
  18. frontend/package.json +24 -0
  19. frontend/src/App.jsx +17 -0
  20. frontend/src/components/ProgressBar.jsx +23 -0
  21. frontend/src/components/SwipeCard.jsx +175 -0
  22. frontend/src/main.jsx +17 -0
  23. frontend/src/pages/Done.jsx +157 -0
  24. frontend/src/pages/Trial.jsx +242 -0
  25. frontend/src/pages/Tutorial.jsx +263 -0
  26. frontend/src/pages/Welcome.jsx +258 -0
  27. frontend/src/theme.js +55 -0
  28. frontend/vite.config.js +13 -0
  29. generate_colorblind_plates.py +116 -0
  30. image_samples/all_images_list.txt +120 -0
  31. image_samples/bigcolor/ortho/coco/000000000001_c675.jpg +0 -0
  32. image_samples/bigcolor/ortho/coco/000000000016_c981.jpg +0 -0
  33. image_samples/bigcolor/ortho/coco/000000000019_c345.jpg +0 -0
  34. image_samples/bigcolor/ortho/coco/000000000057_c752.jpg +0 -0
  35. image_samples/bigcolor/ortho/coco/000000000063_c522.jpg +0 -0
  36. image_samples/bigcolor/ortho/coco/image_sources.csv +6 -0
  37. image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000001_c065.jpg +0 -0
  38. image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000002_c795.jpg +0 -0
  39. image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000003_c231.jpg +0 -0
  40. image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000004_c809.jpg +0 -0
  41. image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000005_c516.jpg +0 -0
  42. image_samples/bigcolor/ortho/imagenet/image_sources.csv +6 -0
  43. image_samples/bigcolor/ortho/instance/ADE_train_00000732_c579.jpg +0 -0
  44. image_samples/bigcolor/ortho/instance/ADE_train_00003253_c750.jpg +0 -0
  45. image_samples/bigcolor/ortho/instance/ADE_train_00003558_c564.jpg +0 -0
  46. image_samples/bigcolor/ortho/instance/ADE_train_00004140_c564.jpg +0 -0
  47. image_samples/bigcolor/ortho/instance/ADE_train_00005198_c762.jpg +0 -0
  48. image_samples/bigcolor/ortho/instance/image_sources.csv +6 -0
  49. image_samples/bigcolor/standard/coco/000000000001_c468.jpg +0 -0
  50. image_samples/bigcolor/standard/coco/000000000016_c981.jpg +0 -0
.gitattributes CHANGED
@@ -33,3 +33,80 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ image_samples/ddcolor/ortho/coco/000000000001.jpg filter=lfs diff=lfs merge=lfs -text
37
+ image_samples/ddcolor/ortho/coco/000000000016.jpg filter=lfs diff=lfs merge=lfs -text
38
+ image_samples/ddcolor/ortho/coco/000000000019.jpg filter=lfs diff=lfs merge=lfs -text
39
+ image_samples/ddcolor/ortho/coco/000000000063.jpg filter=lfs diff=lfs merge=lfs -text
40
+ image_samples/ddcolor/ortho/instance/ADE_train_00000732.jpg filter=lfs diff=lfs merge=lfs -text
41
+ image_samples/ddcolor/standard/coco/000000000001.jpg filter=lfs diff=lfs merge=lfs -text
42
+ image_samples/ddcolor/standard/coco/000000000016.jpg filter=lfs diff=lfs merge=lfs -text
43
+ image_samples/ddcolor/standard/coco/000000000019.jpg filter=lfs diff=lfs merge=lfs -text
44
+ image_samples/ddcolor/standard/coco/000000000057.jpg filter=lfs diff=lfs merge=lfs -text
45
+ image_samples/ddcolor/standard/coco/000000000063.jpg filter=lfs diff=lfs merge=lfs -text
46
+ image_samples/ddcolor/standard/instance/ADE_train_00000732.jpg filter=lfs diff=lfs merge=lfs -text
47
+ image_samples/disco/ortho/coco/000000000001.png filter=lfs diff=lfs merge=lfs -text
48
+ image_samples/disco/ortho/coco/000000000016.png filter=lfs diff=lfs merge=lfs -text
49
+ image_samples/disco/ortho/coco/000000000019.png filter=lfs diff=lfs merge=lfs -text
50
+ image_samples/disco/ortho/coco/000000000057.png filter=lfs diff=lfs merge=lfs -text
51
+ image_samples/disco/ortho/coco/000000000063.png filter=lfs diff=lfs merge=lfs -text
52
+ image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000001.png filter=lfs diff=lfs merge=lfs -text
53
+ image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000002.png filter=lfs diff=lfs merge=lfs -text
54
+ image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000003.png filter=lfs diff=lfs merge=lfs -text
55
+ image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000005.png filter=lfs diff=lfs merge=lfs -text
56
+ image_samples/disco/ortho/instance/ADE_train_00000732.png filter=lfs diff=lfs merge=lfs -text
57
+ image_samples/disco/ortho/instance/ADE_train_00004140.png filter=lfs diff=lfs merge=lfs -text
58
+ image_samples/disco/ortho/instance/ADE_train_00005198.png filter=lfs diff=lfs merge=lfs -text
59
+ image_samples/disco/standard/coco/000000000001.png filter=lfs diff=lfs merge=lfs -text
60
+ image_samples/disco/standard/coco/000000000016.png filter=lfs diff=lfs merge=lfs -text
61
+ image_samples/disco/standard/coco/000000000019.png filter=lfs diff=lfs merge=lfs -text
62
+ image_samples/disco/standard/coco/000000000057.png filter=lfs diff=lfs merge=lfs -text
63
+ image_samples/disco/standard/coco/000000000063.png filter=lfs diff=lfs merge=lfs -text
64
+ image_samples/disco/standard/imagenet/ILSVRC2012_val_00000001.png filter=lfs diff=lfs merge=lfs -text
65
+ image_samples/disco/standard/imagenet/ILSVRC2012_val_00000002.png filter=lfs diff=lfs merge=lfs -text
66
+ image_samples/disco/standard/imagenet/ILSVRC2012_val_00000003.png filter=lfs diff=lfs merge=lfs -text
67
+ image_samples/disco/standard/imagenet/ILSVRC2012_val_00000005.png filter=lfs diff=lfs merge=lfs -text
68
+ image_samples/disco/standard/instance/ADE_train_00000732.png filter=lfs diff=lfs merge=lfs -text
69
+ image_samples/disco/standard/instance/ADE_train_00004140.png filter=lfs diff=lfs merge=lfs -text
70
+ image_samples/disco/standard/instance/ADE_train_00005198.png filter=lfs diff=lfs merge=lfs -text
71
+ image_samples/gt/standard/coco/000000000001.jpg filter=lfs diff=lfs merge=lfs -text
72
+ image_samples/gt/standard/coco/000000000016.jpg filter=lfs diff=lfs merge=lfs -text
73
+ image_samples/gt/standard/coco/000000000019.jpg filter=lfs diff=lfs merge=lfs -text
74
+ image_samples/gt/standard/coco/000000000057.jpg filter=lfs diff=lfs merge=lfs -text
75
+ image_samples/gt/standard/coco/000000000063.jpg filter=lfs diff=lfs merge=lfs -text
76
+ image_samples/gt/standard/imagenet/ILSVRC2012_val_00000001.JPEG filter=lfs diff=lfs merge=lfs -text
77
+ image_samples/gt/standard/imagenet/ILSVRC2012_val_00000002.JPEG filter=lfs diff=lfs merge=lfs -text
78
+ image_samples/gt/standard/imagenet/ILSVRC2012_val_00000003.JPEG filter=lfs diff=lfs merge=lfs -text
79
+ image_samples/gt/standard/imagenet/ILSVRC2012_val_00000005.JPEG filter=lfs diff=lfs merge=lfs -text
80
+ image_samples/unicolor/ortho/coco/000000000001.jpg.png filter=lfs diff=lfs merge=lfs -text
81
+ image_samples/unicolor/ortho/coco/000000000016.jpg.png filter=lfs diff=lfs merge=lfs -text
82
+ image_samples/unicolor/ortho/coco/000000000019.jpg.png filter=lfs diff=lfs merge=lfs -text
83
+ image_samples/unicolor/ortho/coco/000000000057.jpg.png filter=lfs diff=lfs merge=lfs -text
84
+ image_samples/unicolor/ortho/coco/000000000063.jpg.png filter=lfs diff=lfs merge=lfs -text
85
+ image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000001.JPEG.png filter=lfs diff=lfs merge=lfs -text
86
+ image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000002.JPEG.png filter=lfs diff=lfs merge=lfs -text
87
+ image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000003.JPEG.png filter=lfs diff=lfs merge=lfs -text
88
+ image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000004.JPEG.png filter=lfs diff=lfs merge=lfs -text
89
+ image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000005.JPEG.png filter=lfs diff=lfs merge=lfs -text
90
+ image_samples/unicolor/ortho/instance/ADE_train_00000732.jpg.png filter=lfs diff=lfs merge=lfs -text
91
+ image_samples/unicolor/ortho/instance/ADE_train_00003558.jpg.png filter=lfs diff=lfs merge=lfs -text
92
+ image_samples/unicolor/ortho/instance/ADE_train_00004140.jpg.png filter=lfs diff=lfs merge=lfs -text
93
+ image_samples/unicolor/ortho/instance/ADE_train_00005198.jpg.png filter=lfs diff=lfs merge=lfs -text
94
+ image_samples/unicolor/standard/coco/000000000001.jpg.png filter=lfs diff=lfs merge=lfs -text
95
+ image_samples/unicolor/standard/coco/000000000016.jpg.png filter=lfs diff=lfs merge=lfs -text
96
+ image_samples/unicolor/standard/coco/000000000019.jpg.png filter=lfs diff=lfs merge=lfs -text
97
+ image_samples/unicolor/standard/coco/000000000057.jpg.png filter=lfs diff=lfs merge=lfs -text
98
+ image_samples/unicolor/standard/coco/000000000063.jpg.png filter=lfs diff=lfs merge=lfs -text
99
+ image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000001.JPEG.png filter=lfs diff=lfs merge=lfs -text
100
+ image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000002.JPEG.png filter=lfs diff=lfs merge=lfs -text
101
+ image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000003.JPEG.png filter=lfs diff=lfs merge=lfs -text
102
+ image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000004.JPEG.png filter=lfs diff=lfs merge=lfs -text
103
+ image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000005.JPEG.png filter=lfs diff=lfs merge=lfs -text
104
+ image_samples/unicolor/standard/instance/ADE_train_00000732.jpg.png filter=lfs diff=lfs merge=lfs -text
105
+ image_samples/unicolor/standard/instance/ADE_train_00003558.jpg.png filter=lfs diff=lfs merge=lfs -text
106
+ image_samples/unicolor/standard/instance/ADE_train_00004140.jpg.png filter=lfs diff=lfs merge=lfs -text
107
+ image_samples/unicolor/standard/instance/ADE_train_00005198.jpg.png filter=lfs diff=lfs merge=lfs -text
108
+ tutorial/colorartifacts2.png filter=lfs diff=lfs merge=lfs -text
109
+ tutorial/colorblind/plate_blueyellow.png filter=lfs diff=lfs merge=lfs -text
110
+ tutorial/colorblind/plate_demo.png filter=lfs diff=lfs merge=lfs -text
111
+ tutorial/colorblind/plate_redgreen.png filter=lfs diff=lfs merge=lfs -text
112
+ tutorial/imageArtifacts.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ venv/
7
+
8
+ # Node
9
+ frontend/node_modules/
10
+ frontend/.vite/
11
+
12
+ # Build output — rebuilt by Docker, don't commit
13
+ frontend/dist/
14
+
15
+ # Local database
16
+ responses.db
17
+ *.db
18
+
19
+ # Fly.io artifacts (not needed for HF)
20
+ .fly/
21
+
22
+ # Editor
23
+ .DS_Store
24
+ *.swp
Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim AS frontend-build
2
+ WORKDIR /app/frontend
3
+ COPY frontend/package*.json ./
4
+ RUN npm ci
5
+ COPY frontend/ .
6
+ RUN npm run build
7
+
8
+ FROM python:3.11-slim
9
+ WORKDIR /app
10
+
11
+ COPY backend/requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ COPY backend/ ./backend/
15
+ COPY --from=frontend-build /app/frontend/dist ./frontend/dist
16
+ COPY image_samples/ ./image_samples/
17
+ COPY tutorial/ ./tutorial/
18
+ COPY manifest.json .
19
+
20
+ # HF Spaces runs as user 1000; ensure /data is writable for SQLite
21
+ RUN mkdir -p /data && chmod 777 /data
22
+
23
+ EXPOSE 7860
24
+
25
+ # PORT is injected by HF Spaces (7860); falls back to 8080 elsewhere
26
+ CMD ["sh", "-c", "uvicorn backend.main:app --host 0.0.0.0 --port ${PORT:-8080}"]
README.md CHANGED
@@ -1,12 +1,19 @@
1
  ---
2
- title: Colorization
3
- emoji: 🦀
4
- colorFrom: purple
5
- colorTo: purple
6
  sdk: docker
7
  pinned: false
8
- license: cc-by-sa-4.0
9
- short_description: User study for colorization techniques
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Color Turing Test
3
+ emoji: 🎨
4
+ colorFrom: indigo
5
+ colorTo: green
6
  sdk: docker
7
  pinned: false
 
 
8
  ---
9
 
10
+ # Color Turing Test Colorization Perception Study
11
+
12
+ A perceptual study examining how well AI colorization fools human observers.
13
+
14
+ Participants see 50 images and judge whether colorization artifacts are present or absent. Takes ~5–10 minutes. Anonymous, no account required.
15
+
16
+ ## For researchers
17
+
18
+ Results CSV: `/api/results/csv?key=colorturingtest2025`
19
+ Summary JSON: `/api/results/summary?key=colorturingtest2025`
backend/__init__.py ADDED
File without changes
backend/db.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import aiosqlite
3
+
4
+ # Use persistent storage on Fly.io / HF Spaces, local file otherwise
5
+ if os.environ.get("FLY_APP_NAME") or os.environ.get("SPACE_ID"):
6
+ DB_PATH = "/data/responses.db"
7
+ else:
8
+ DB_PATH = os.path.join(os.path.dirname(__file__), "..", "responses.db")
9
+
10
+
11
+ async def get_db() -> aiosqlite.Connection:
12
+ db = await aiosqlite.connect(DB_PATH)
13
+ db.row_factory = aiosqlite.Row
14
+ await db.execute("PRAGMA journal_mode=WAL")
15
+ return db
16
+
17
+
18
+ async def init_db():
19
+ os.makedirs(os.path.dirname(DB_PATH) if os.path.dirname(DB_PATH) else ".", exist_ok=True)
20
+ db = await get_db()
21
+ try:
22
+ await db.execute("""
23
+ CREATE TABLE IF NOT EXISTS sessions (
24
+ session_id TEXT PRIMARY KEY,
25
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
26
+ expertise TEXT,
27
+ colorblind INTEGER DEFAULT 0,
28
+ device TEXT,
29
+ cb_redgreen TEXT DEFAULT '',
30
+ cb_blueyellow TEXT DEFAULT '',
31
+ trials TEXT,
32
+ completed INTEGER DEFAULT 0
33
+ )
34
+ """)
35
+ await db.execute("""
36
+ CREATE TABLE IF NOT EXISTS responses (
37
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+ session_id TEXT,
39
+ trial_index INTEGER,
40
+ image_id TEXT,
41
+ image_path TEXT,
42
+ method TEXT,
43
+ variant TEXT,
44
+ dataset TEXT,
45
+ base_id TEXT,
46
+ label TEXT,
47
+ response TEXT,
48
+ correct INTEGER,
49
+ response_time_ms INTEGER,
50
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
51
+ )
52
+ """)
53
+ await db.commit()
54
+ finally:
55
+ await db.close()
backend/main.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from contextlib import asynccontextmanager
2
+ from pathlib import Path
3
+
4
+ from fastapi import FastAPI
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from fastapi.staticfiles import StaticFiles
7
+
8
+ from .db import init_db
9
+ from .routes.session import router as session_router
10
+ from .routes.results import router as results_router
11
+
12
+ BASE_DIR = Path(__file__).resolve().parent.parent
13
+
14
+
15
+ @asynccontextmanager
16
+ async def lifespan(app: FastAPI):
17
+ await init_db()
18
+ yield
19
+
20
+
21
+ app = FastAPI(title="Colorization Perception Study", lifespan=lifespan)
22
+
23
+ # CORS
24
+ app.add_middleware(
25
+ CORSMiddleware,
26
+ allow_origins=["*"],
27
+ allow_credentials=True,
28
+ allow_methods=["*"],
29
+ allow_headers=["*"],
30
+ )
31
+
32
+ # API routes
33
+ app.include_router(session_router)
34
+ app.include_router(results_router)
35
+
36
+ # Static file mounts — order matters: more specific first
37
+ app.mount("/images", StaticFiles(directory=str(BASE_DIR / "image_samples")), name="images")
38
+ app.mount("/tutorial", StaticFiles(directory=str(BASE_DIR / "tutorial")), name="tutorial")
39
+
40
+ # Frontend SPA — must be last
41
+ frontend_dist = BASE_DIR / "frontend" / "dist"
42
+ if frontend_dist.exists():
43
+ app.mount("/", StaticFiles(directory=str(frontend_dist), html=True), name="frontend")
backend/requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi==0.111.0
2
+ uvicorn[standard]==0.29.0
3
+ aiosqlite==0.20.0
4
+ python-multipart==0.0.9
backend/routes/__init__.py ADDED
File without changes
backend/routes/results.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ import io
5
+ from typing import Optional
6
+
7
+ from fastapi import APIRouter, HTTPException, Query
8
+ from fastapi.responses import StreamingResponse
9
+
10
+ from ..db import get_db
11
+
12
+ router = APIRouter(prefix="/api/results", tags=["results"])
13
+
14
+ API_KEY = "colorturingtest2025"
15
+
16
+
17
+ def check_key(key: Optional[str]):
18
+ if key != API_KEY:
19
+ raise HTTPException(status_code=403, detail="Invalid API key")
20
+
21
+
22
+ @router.get("/csv")
23
+ async def download_csv(key: str = Query(None)):
24
+ check_key(key)
25
+
26
+ db = await get_db()
27
+ try:
28
+ cursor = await db.execute("""
29
+ SELECT
30
+ r.id,
31
+ r.session_id,
32
+ s.expertise,
33
+ s.colorblind,
34
+ s.device,
35
+ s.cb_redgreen,
36
+ s.cb_blueyellow,
37
+ r.trial_index,
38
+ r.image_id,
39
+ r.image_path,
40
+ r.method,
41
+ r.variant,
42
+ r.dataset,
43
+ r.base_id,
44
+ r.label,
45
+ r.response,
46
+ r.correct,
47
+ r.response_time_ms,
48
+ r.created_at
49
+ FROM responses r
50
+ JOIN sessions s ON r.session_id = s.session_id
51
+ ORDER BY r.created_at
52
+ """)
53
+ rows = await cursor.fetchall()
54
+ finally:
55
+ await db.close()
56
+
57
+ output = io.StringIO()
58
+ writer = csv.writer(output)
59
+ writer.writerow([
60
+ "id", "session_id", "expertise", "colorblind", "device",
61
+ "cb_redgreen", "cb_blueyellow",
62
+ "trial_index", "image_id", "image_path", "method", "variant",
63
+ "dataset", "base_id", "label", "response", "correct",
64
+ "response_time_ms", "created_at"
65
+ ])
66
+ for row in rows:
67
+ writer.writerow(list(row))
68
+
69
+ output.seek(0)
70
+ return StreamingResponse(
71
+ iter([output.getvalue()]),
72
+ media_type="text/csv",
73
+ headers={"Content-Disposition": "attachment; filename=colorization_results.csv"},
74
+ )
75
+
76
+
77
+ @router.get("/summary")
78
+ async def summary(key: str = Query(None)):
79
+ check_key(key)
80
+
81
+ db = await get_db()
82
+ try:
83
+ # Per-method detection rates
84
+ cursor = await db.execute("""
85
+ SELECT
86
+ method,
87
+ COUNT(*) as total_shown,
88
+ SUM(CASE WHEN label = 'fake' AND response = 'fake' THEN 1 ELSE 0 END) as correctly_identified_fake,
89
+ ROUND(
90
+ CAST(SUM(CASE WHEN label = 'fake' AND response = 'fake' THEN 1 ELSE 0 END) AS FLOAT)
91
+ / NULLIF(SUM(CASE WHEN label = 'fake' THEN 1 ELSE 0 END), 0)
92
+ * 100, 2
93
+ ) as detection_rate
94
+ FROM responses
95
+ GROUP BY method
96
+ ORDER BY method
97
+ """)
98
+ method_stats = [dict(row) for row in await cursor.fetchall()]
99
+
100
+ # Overall stats
101
+ cursor = await db.execute("SELECT COUNT(*) as total FROM responses")
102
+ total_responses = (await cursor.fetchone())["total"]
103
+
104
+ cursor = await db.execute(
105
+ "SELECT COUNT(*) as total FROM sessions WHERE completed = 1"
106
+ )
107
+ sessions_completed = (await cursor.fetchone())["total"]
108
+
109
+ # Overall accuracy
110
+ cursor = await db.execute(
111
+ "SELECT ROUND(AVG(correct) * 100, 2) as accuracy FROM responses"
112
+ )
113
+ overall_accuracy = (await cursor.fetchone())["accuracy"]
114
+ finally:
115
+ await db.close()
116
+
117
+ return {
118
+ "total_responses": total_responses,
119
+ "sessions_completed": sessions_completed,
120
+ "overall_accuracy": overall_accuracy,
121
+ "per_method": method_stats,
122
+ }
backend/routes/session.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import random
5
+ import uuid
6
+ from collections import defaultdict
7
+ from pathlib import Path
8
+ from typing import List, Dict
9
+
10
+ from fastapi import APIRouter, HTTPException
11
+ from pydantic import BaseModel
12
+
13
+ from ..db import get_db
14
+
15
+ router = APIRouter(prefix="/api/session", tags=["session"])
16
+
17
+ # Load manifest once at module level
18
+ MANIFEST_PATH = Path(__file__).resolve().parent.parent.parent / "manifest.json"
19
+ with open(MANIFEST_PATH) as f:
20
+ MANIFEST = json.load(f)
21
+
22
+
23
+ class StartRequest(BaseModel):
24
+ expertise: str
25
+ colorblind: int # 0 or 1
26
+ device: str
27
+ cb_redgreen: str = "" # "normal" | "deficient" | "unsure"
28
+ cb_blueyellow: str = "" # "normal" | "deficient" | "unsure"
29
+
30
+
31
+ class RespondRequest(BaseModel):
32
+ session_id: str
33
+ trial_index: int
34
+ image_id: str
35
+ label: str
36
+ response: str
37
+ response_time_ms: int
38
+
39
+
40
+ class CompleteRequest(BaseModel):
41
+ session_id: str
42
+
43
+
44
+ def sample_trials() -> list[dict]:
45
+ """
46
+ Sample 50 images: 10 gt images (randomly sampled from 15) + 40 colorized (8 per method),
47
+ balanced across variants (ortho/standard) and datasets.
48
+
49
+ GT is capped at 10 to avoid consistent overexposure of the same real images across sessions.
50
+ 8 per method (instead of 7) keeps the total at 50 and maintains a ~20/80 real/fake split —
51
+ document the base rate in analysis.
52
+ """
53
+ all_gt = [e for e in MANIFEST if e["method"] == "gt"]
54
+ gt_images = random.sample(all_gt, min(10, len(all_gt)))
55
+
56
+ colorized_methods = ["bigcolor", "ddcolor", "disco", "unicolor", "mixed"]
57
+ sampled_colorized = []
58
+
59
+ for method in colorized_methods:
60
+ method_images = [e for e in MANIFEST if e["method"] == method]
61
+
62
+ # Group by (variant, dataset) for balanced sampling
63
+ groups = defaultdict(list)
64
+ for img in method_images:
65
+ groups[(img["variant"], img["dataset"])].append(img)
66
+
67
+ # We need 8 per method. There are 6 groups (2 variants x 3 datasets).
68
+ # Each group has ~5 images. Pick 1 from each group (6), then 2 more random.
69
+ selected = []
70
+ remaining = []
71
+
72
+ group_keys = sorted(groups.keys())
73
+ for key in group_keys:
74
+ pool = groups[key][:]
75
+ random.shuffle(pool)
76
+ if pool:
77
+ selected.append(pool[0])
78
+ remaining.extend(pool[1:])
79
+
80
+ # Fill up to 8
81
+ random.shuffle(remaining)
82
+ needed = 8 - len(selected)
83
+ selected.extend(remaining[:needed])
84
+
85
+ sampled_colorized.extend(selected)
86
+
87
+ trials = gt_images + sampled_colorized
88
+ random.shuffle(trials)
89
+ return trials
90
+
91
+
92
+ @router.post("/start")
93
+ async def start_session(req: StartRequest):
94
+ session_id = str(uuid.uuid4())
95
+ trials = sample_trials()
96
+
97
+ db = await get_db()
98
+ try:
99
+ await db.execute(
100
+ "INSERT INTO sessions (session_id, expertise, colorblind, device, cb_redgreen, cb_blueyellow, trials) VALUES (?, ?, ?, ?, ?, ?, ?)",
101
+ (session_id, req.expertise, req.colorblind, req.device, req.cb_redgreen, req.cb_blueyellow, json.dumps(trials)),
102
+ )
103
+ await db.commit()
104
+ finally:
105
+ await db.close()
106
+
107
+ return {"session_id": session_id, "trials": trials}
108
+
109
+
110
+ @router.post("/respond")
111
+ async def respond(req: RespondRequest):
112
+ db = await get_db()
113
+ try:
114
+ # Look up the image details from the session trials
115
+ row = await db.execute(
116
+ "SELECT trials FROM sessions WHERE session_id = ?", (req.session_id,)
117
+ )
118
+ session = await row.fetchone()
119
+ if not session:
120
+ raise HTTPException(status_code=404, detail="Session not found")
121
+
122
+ trials = json.loads(session["trials"])
123
+ if req.trial_index < 0 or req.trial_index >= len(trials):
124
+ raise HTTPException(status_code=400, detail="Invalid trial index")
125
+
126
+ trial = trials[req.trial_index]
127
+ correct = 1 if req.response == req.label else 0
128
+
129
+ await db.execute(
130
+ """INSERT INTO responses
131
+ (session_id, trial_index, image_id, image_path, method, variant, dataset, base_id, label, response, correct, response_time_ms)
132
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
133
+ (
134
+ req.session_id,
135
+ req.trial_index,
136
+ trial["id"],
137
+ trial["path"],
138
+ trial["method"],
139
+ trial.get("variant", ""),
140
+ trial.get("dataset", ""),
141
+ trial.get("base_id", ""),
142
+ req.label,
143
+ req.response,
144
+ correct,
145
+ req.response_time_ms,
146
+ ),
147
+ )
148
+ await db.commit()
149
+ finally:
150
+ await db.close()
151
+
152
+ return {"correct": bool(correct)}
153
+
154
+
155
+ @router.post("/complete")
156
+ async def complete_session(req: CompleteRequest):
157
+ db = await get_db()
158
+ try:
159
+ await db.execute(
160
+ "UPDATE sessions SET completed = 1 WHERE session_id = ?",
161
+ (req.session_id,),
162
+ )
163
+ await db.commit()
164
+ finally:
165
+ await db.close()
166
+
167
+ return {"status": "completed"}
docs/analyses.md ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Possible Analyses
2
+
3
+ The dataset is a binary detection task with known ground truth. Each response is: did the participant detect colorization artifacts (PRESENT) or not (ABSENT)?
4
+
5
+ ---
6
+
7
+ ## 1. Per-Method Detection Rate (primary)
8
+
9
+ **Question:** Which colorization method is hardest to detect?
10
+
11
+ ```python
12
+ df.groupby('method').apply(lambda g: (
13
+ g[g['label']=='fake']['response'].eq('fake').mean()
14
+ ))
15
+ ```
16
+
17
+ Produces hit rate per method. Lower = more convincing. Compare methods pairwise with proportion tests or a logistic mixed-effects model.
18
+
19
+ ---
20
+
21
+ ## 2. Signal Detection Theory (d-prime)
22
+
23
+ **Question:** Separating sensitivity from response bias.
24
+
25
+ For each participant (or aggregated per method):
26
+
27
+ ```python
28
+ from scipy.stats import norm
29
+
30
+ def dprime(hits, misses, fas, crs):
31
+ hr = hits / (hits + misses)
32
+ far = fas / (fas + crs)
33
+ # Clip to avoid ±inf
34
+ hr = max(0.01, min(0.99, hr))
35
+ far = max(0.01, min(0.99, far))
36
+ return norm.ppf(hr) - norm.ppf(far)
37
+
38
+ # hits = label=='fake' AND response=='fake'
39
+ # misses = label=='fake' AND response=='real'
40
+ # false alarms = label=='real' AND response=='fake'
41
+ # correct rejections = label=='real' AND response=='real'
42
+ ```
43
+
44
+ d' = 0 → chance; d' = 1 → moderate sensitivity; d' ≥ 2 → strong.
45
+ Criterion c = −0.5 × (z_HR + z_FAR) tells you response bias (positive = conservative, negative = liberal).
46
+
47
+ **Important:** The 20/80 real/fake base rate means you need at least 10 gt trials and 40 fake trials per participant — these are already guaranteed by the sampling scheme.
48
+
49
+ ---
50
+
51
+ ## 3. Response Time Analysis
52
+
53
+ **Question:** Do faster responses indicate more automatic (vs deliberative) detection?
54
+
55
+ ```python
56
+ import seaborn as sns
57
+ sns.boxplot(data=df, x='method', y='response_time_ms', hue='correct')
58
+ ```
59
+
60
+ Hypotheses:
61
+ - Correct rejections of hard-to-detect fakes may be slower (more deliberation)
62
+ - Easy artifacts (some methods) may produce fast, confident PRESENT responses
63
+ - Compare median RT for correct vs incorrect per method with Mann-Whitney U
64
+
65
+ ---
66
+
67
+ ## 4. Expertise Effect
68
+
69
+ **Question:** Do experts outperform novices?
70
+
71
+ ```python
72
+ df.groupby(['expertise', 'method'])['correct'].mean().unstack()
73
+ ```
74
+
75
+ Run a 2-way ANOVA or mixed-effects logistic regression:
76
+ ```
77
+ correct ~ expertise * method + (1|session_id)
78
+ ```
79
+ Use `pymer4` (R-style) or `statsmodels` MixedLM.
80
+
81
+ ---
82
+
83
+ ## 5. Colorblindness Effect
84
+
85
+ **Question:** Does colorblindness affect artifact detection?
86
+
87
+ ```python
88
+ df.groupby(['cb_redgreen', 'method'])['correct'].mean()
89
+ ```
90
+
91
+ Red-green colorblindness may reduce sensitivity to chromatic artifacts from some methods. Compare d' between `normal` and `deficient` groups with independent-samples t-test or Bayesian comparison (if n is small).
92
+
93
+ ---
94
+
95
+ ## 6. Variant Comparison (ortho vs standard)
96
+
97
+ **Question:** Are orthogonal colorizations harder to detect?
98
+
99
+ ```python
100
+ df[df['label']=='fake'].groupby(['method','variant'])['correct'].mean()
101
+ ```
102
+
103
+ Within each method, compare detection rates for `ortho` vs `standard`. Paired within method to control for method-level difficulty.
104
+
105
+ ---
106
+
107
+ ## 7. Dataset Difficulty
108
+
109
+ **Question:** Do COCO / ImageNet / Instance images differ in detection difficulty?
110
+
111
+ ```python
112
+ df[df['label']=='fake'].groupby(['dataset','method'])['correct'].mean().unstack()
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 8. Learning / Fatigue Effects
118
+
119
+ **Question:** Does performance change over the course of the session?
120
+
121
+ ```python
122
+ df['trial_bin'] = pd.cut(df['trial_index'], bins=5, labels=['1-10','11-20','21-30','31-40','41-50'])
123
+ df.groupby('trial_bin')['correct'].mean()
124
+ ```
125
+
126
+ Plot accuracy across bins. Rising = learning (calibration to task). Falling = fatigue.
127
+
128
+ ---
129
+
130
+ ## 9. Per-Image Difficulty
131
+
132
+ **Question:** Which specific images are consistently detected / missed?
133
+
134
+ ```python
135
+ img_stats = df.groupby('image_id').agg(
136
+ n=('correct','count'),
137
+ accuracy=('correct','mean'),
138
+ method=('method','first'),
139
+ dataset=('dataset','first'),
140
+ ).sort_values('accuracy')
141
+ ```
142
+
143
+ Top easiest (always detected) and hardest (never detected) images. Useful for understanding method-specific failure modes.
144
+
145
+ ---
146
+
147
+ ## 10. Inter-Rater Agreement
148
+
149
+ **Question:** Do participants agree with each other on specific images?
150
+
151
+ For images seen by multiple participants, compute Fleiss' kappa or just pairwise agreement rate. High agreement on specific images = consistent perceptual signal, not noise.
152
+
153
+ ---
154
+
155
+ ## Suggested Analysis Pipeline (Python)
156
+
157
+ ```python
158
+ import pandas as pd
159
+ from scipy.stats import norm, mannwhitneyu, chi2_contingency
160
+
161
+ df = pd.read_csv('colorization_results.csv')
162
+
163
+ # Filter completed sessions only
164
+ completed = df[df['session_id'].isin(
165
+ df.groupby('session_id')['trial_index'].max()[lambda x: x >= 49].index
166
+ )]
167
+
168
+ # Base rates
169
+ print(completed['label'].value_counts(normalize=True))
170
+
171
+ # Per-method detection rate (fake images only)
172
+ fakes = completed[completed['label'] == 'fake']
173
+ print(fakes.groupby('method')['correct'].mean().sort_values())
174
+
175
+ # Overall accuracy
176
+ print("Overall accuracy:", completed['correct'].mean())
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Recommended Libraries
182
+
183
+ | Task | Library |
184
+ |---|---|
185
+ | Data wrangling | `pandas` |
186
+ | Statistical tests | `scipy.stats`, `pingouin` |
187
+ | Mixed-effects models | `statsmodels`, `pymer4` |
188
+ | Signal detection | `sdtpy` or manual (formula above) |
189
+ | Visualization | `matplotlib`, `seaborn` |
190
+ | Reporting | `jupyter` notebooks |
docs/data-storage.md ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Storage Format
2
+
3
+ ## Database
4
+
5
+ SQLite file at:
6
+ - **Local dev:** `./responses.db` (next to `backend/`)
7
+ - **Fly.io:** `/app/data/responses.db` (on persistent volume)
8
+
9
+ Two tables: `sessions` and `responses`.
10
+
11
+ ---
12
+
13
+ ## Table: `sessions`
14
+
15
+ One row per participant session.
16
+
17
+ | Column | Type | Description |
18
+ |---|---|---|
19
+ | `session_id` | TEXT PK | UUID generated server-side at session start |
20
+ | `expertise` | TEXT | Self-reported: `novice`, `hobbyist`, `professional`, `researcher` |
21
+ | `colorblind` | INTEGER | 1 if participant self-reported colorblindness, else 0 |
22
+ | `device` | TEXT | User-agent string (desktop/mobile detection) |
23
+ | `cb_redgreen` | TEXT | Ishihara plate result: `normal`, `deficient`, or `unsure` |
24
+ | `cb_blueyellow` | TEXT | Ishihara plate result: `normal`, `deficient`, or `unsure` |
25
+ | `trials` | TEXT | JSON array of the 50 sampled trial objects (full manifest entries) |
26
+ | `completed` | INTEGER | 1 if participant reached Done screen, else 0 |
27
+ | `created_at` | DATETIME | UTC timestamp of session creation |
28
+
29
+ ---
30
+
31
+ ## Table: `responses`
32
+
33
+ One row per trial judgment.
34
+
35
+ | Column | Type | Description |
36
+ |---|---|---|
37
+ | `id` | INTEGER PK | Auto-increment |
38
+ | `session_id` | TEXT FK | References `sessions.session_id` |
39
+ | `trial_index` | INTEGER | 0-indexed position in this participant's trial sequence |
40
+ | `image_id` | TEXT | Unique image identifier from `manifest.json` |
41
+ | `image_path` | TEXT | Relative path served at `/images/<path>` |
42
+ | `method` | TEXT | Colorization method: `gt`, `bigcolor`, `ddcolor`, `disco`, `unicolor`, `mixed` |
43
+ | `variant` | TEXT | `ortho` or `standard` (empty for `gt`) |
44
+ | `dataset` | TEXT | `coco`, `imagenet`, or `instance` |
45
+ | `base_id` | TEXT | Base scene identifier (shared across methods for same scene) |
46
+ | `label` | TEXT | Ground truth: `real` (gt image) or `fake` (colorized) |
47
+ | `response` | TEXT | Participant response: `real` (ABSENT pressed) or `fake` (PRESENT pressed) |
48
+ | `correct` | INTEGER | 1 if `response == label`, else 0 |
49
+ | `response_time_ms` | INTEGER | Milliseconds from image display to button press |
50
+ | `created_at` | DATETIME | UTC timestamp |
51
+
52
+ ---
53
+
54
+ ## CSV Export
55
+
56
+ Download the full dataset:
57
+
58
+ ```
59
+ GET https://<your-fly-app>.fly.dev/api/results/csv?key=colorturingtest2025
60
+ ```
61
+
62
+ The CSV has one row per response with all columns from the join of `responses` + `sessions`.
63
+
64
+ ### Column order in CSV
65
+
66
+ ```
67
+ id, session_id, expertise, colorblind, device,
68
+ cb_redgreen, cb_blueyellow,
69
+ trial_index, image_id, image_path, method, variant,
70
+ dataset, base_id, label, response, correct,
71
+ response_time_ms, created_at
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Importing to Google Sheets
77
+
78
+ 1. Download the CSV from the endpoint above (or open the URL directly in browser — it will download)
79
+ 2. In Google Sheets: **File → Import → Upload** → select the CSV → "Replace spreadsheet"
80
+ 3. For live/refreshable data, use **Apps Script**:
81
+
82
+ ```javascript
83
+ // In Google Sheets → Extensions → Apps Script
84
+ function importResults() {
85
+ const url = 'https://<your-fly-app>.fly.dev/api/results/csv?key=colorturingtest2025';
86
+ const response = UrlFetchApp.fetch(url);
87
+ const csv = response.getContentText();
88
+ const data = Utilities.parseCsv(csv);
89
+
90
+ const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Raw')
91
+ || SpreadsheetApp.getActiveSpreadsheet().insertSheet('Raw');
92
+ sheet.clearContents();
93
+ sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
94
+ }
95
+ ```
96
+
97
+ Run `importResults()` manually or set a time-based trigger (e.g., every hour) to keep the sheet updated.
98
+
99
+ ---
100
+
101
+ ## Base Rate
102
+
103
+ Every session has a fixed real/fake split:
104
+ - **20% real** (10 gt images)
105
+ - **80% fake** (40 colorized images, 8 per method)
106
+
107
+ Factor this into any sensitivity analysis (d-prime, criterion).
108
+
109
+ ---
110
+
111
+ ## Notes
112
+
113
+ - Sessions where `completed = 0` are partial — participant dropped out mid-task. Include/exclude based on analysis goals.
114
+ - `response_time_ms` starts when image finishes loading (`onLoad` event), not when the trial begins.
115
+ - `trials` JSON in the sessions table preserves the exact sequence seen by each participant — useful for re-checking specific images post-hoc.
docs/pipeline.md ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Study Pipeline
2
+
3
+ ## Overview
4
+
5
+ A one-page web app that runs a perceptual discrimination study. Participants see images one at a time and judge whether colorization artifacts are present or absent. No login, no score reveal — pure signal detection data collection.
6
+
7
+ ---
8
+
9
+ ## Participant Flow
10
+
11
+ ```
12
+ Welcome / Colorblindness Screen
13
+ └─ 3 Ishihara plates (demo → red-green → blue-yellow)
14
+ └─ Expertise dropdown (novice / hobbyist / professional / researcher)
15
+
16
+
17
+ Tutorial (3 steps)
18
+ Step 1 — artifact reference: what artifacts look like (static explainer image)
19
+ Step 2 — practice real: participant sees a ground-truth image, makes a judgment, gets feedback
20
+ Step 3 — practice fake: participant sees a colorized image, makes a judgment, gets feedback
21
+
22
+
23
+ 50 Trial Loop
24
+ ├─ Image displayed (fixed-size container, objectFit: contain)
25
+ ├─ Participant responds: ABSENT (←) or PRESENT (→)
26
+ │ • Swipe right / → key / green button = PRESENT (artifacts detected)
27
+ │ • Swipe left / ← key / red button = ABSENT (no artifacts)
28
+ ├─ Response + timing logged to backend (fire-and-forget)
29
+ └─ Repeat until trial 50
30
+
31
+
32
+ Done Screen
33
+ └─ Thank-you message + share link
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Image Sampling (per session)
39
+
40
+ - **10 ground-truth (real)** images sampled randomly from 15 available
41
+ - **8 colorized (fake)** images per method × 5 methods = 40
42
+ - Total: **50 trials**, shuffled
43
+ - Balance enforced: within each method, 1 image is drawn from each of the 6 (variant × dataset) groups (6 groups × 1 = 6; 2 extra drawn randomly from remaining pool)
44
+ - GT varies session-to-session; same 15 base scenes, different 10 selected each time
45
+
46
+ **Methods:** `bigcolor`, `ddcolor`, `disco`, `unicolor`, `mixed`
47
+ **Variants:** `ortho`, `standard`
48
+ **Datasets:** `coco`, `imagenet`, `instance`
49
+
50
+ ---
51
+
52
+ ## Response Encoding
53
+
54
+ | User action | Button | Color | `response` value stored | Meaning |
55
+ |---|---|---|---|---|
56
+ | Swipe right / → | PRESENT | Green | `fake` | Participant detected artifacts |
57
+ | Swipe left / ← | ABSENT | Red | `real` | Participant saw no artifacts |
58
+
59
+ `label` field encodes ground truth: `fake` (colorized) or `real` (ground-truth photo).
60
+ `correct = 1` when `response == label`.
61
+
62
+ ---
63
+
64
+ ## Tech Stack
65
+
66
+ | Layer | Tech |
67
+ |---|---|
68
+ | Frontend | React 18 + Vite + MUI v5 dark theme |
69
+ | Backend | FastAPI + aiosqlite |
70
+ | Database | SQLite (local: `./responses.db`, Fly.io: `/app/data/responses.db`) |
71
+ | Deployment | Fly.io — single Docker container, persistent volume |
72
+ | Session state | `localStorage` (resume on reload, UUID per participant) |
73
+
74
+ ---
75
+
76
+ ## Key Files
77
+
78
+ ```
79
+ colorization-webapp/
80
+ ├── backend/
81
+ │ ├── main.py — FastAPI app, static mounts
82
+ │ ├── db.py — SQLite init, connection
83
+ │ └── routes/
84
+ │ ├── session.py — /api/session/start, /respond, /complete + sampling logic
85
+ │ └── results.py — /api/results/csv, /summary (key-protected)
86
+ ├── frontend/src/
87
+ │ ├── pages/
88
+ │ │ ├── Welcome.jsx — colorblindness plates + expertise
89
+ │ │ ├── Tutorial.jsx — 3-step practice
90
+ │ │ ├── Trial.jsx — main 50-trial loop
91
+ │ │ └── Done.jsx — thank-you + share
92
+ │ └── components/
93
+ │ ├── SwipeCard.jsx — touch/drag swipe handler
94
+ │ └── ProgressBar.jsx
95
+ ├── image_samples/ — 165 images (served at /images/...)
96
+ ├── tutorial/ — tutorial assets + Ishihara plates
97
+ ├── manifest.json — image metadata index
98
+ ├── Dockerfile
99
+ └── fly.toml
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Admin Endpoints
105
+
106
+ Both require `?key=colorturingtest2025`.
107
+
108
+ | Endpoint | Description |
109
+ |---|---|
110
+ | `GET /api/results/csv?key=colorturingtest2025` | Download full response CSV |
111
+ | `GET /api/results/summary?key=colorturingtest2025` | JSON summary: per-method detection rates, overall accuracy |
fly.toml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ app = "colorization-study"
2
+ primary_region = "sin"
3
+
4
+ [build]
5
+
6
+ [http_service]
7
+ internal_port = 8080
8
+ force_https = true
9
+ auto_stop_machines = "stop"
10
+ auto_start_machines = true
11
+ min_machines_running = 0
12
+
13
+ [[vm]]
14
+ memory = "512mb"
15
+ cpu_kind = "shared"
16
+ cpus = 1
17
+
18
+ [mounts]
19
+ source = "colorization_data"
20
+ destination = "/app/data"
frontend/index.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Colorization Perception Study</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
10
+ <style>
11
+ * { margin: 0; padding: 0; box-sizing: border-box; }
12
+ html, body, #root { height: 100%; width: 100%; }
13
+ body { background: #121212; }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div id="root"></div>
18
+ <script type="module" src="/src/main.jsx"></script>
19
+ </body>
20
+ </html>
frontend/package-lock.json ADDED
@@ -0,0 +1,2467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "colorization-study",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "colorization-study",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "@emotion/react": "^11.11.0",
12
+ "@emotion/styled": "^11.11.0",
13
+ "@mui/icons-material": "^5.15.0",
14
+ "@mui/material": "^5.15.0",
15
+ "react": "^18.2.0",
16
+ "react-dom": "^18.2.0",
17
+ "react-router-dom": "^6.21.0"
18
+ },
19
+ "devDependencies": {
20
+ "@vitejs/plugin-react": "^4.2.0",
21
+ "vite": "^5.0.0"
22
+ }
23
+ },
24
+ "node_modules/@babel/code-frame": {
25
+ "version": "7.29.0",
26
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
27
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "@babel/helper-validator-identifier": "^7.28.5",
31
+ "js-tokens": "^4.0.0",
32
+ "picocolors": "^1.1.1"
33
+ },
34
+ "engines": {
35
+ "node": ">=6.9.0"
36
+ }
37
+ },
38
+ "node_modules/@babel/compat-data": {
39
+ "version": "7.29.0",
40
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
41
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
42
+ "dev": true,
43
+ "license": "MIT",
44
+ "engines": {
45
+ "node": ">=6.9.0"
46
+ }
47
+ },
48
+ "node_modules/@babel/core": {
49
+ "version": "7.29.0",
50
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
51
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
52
+ "dev": true,
53
+ "license": "MIT",
54
+ "dependencies": {
55
+ "@babel/code-frame": "^7.29.0",
56
+ "@babel/generator": "^7.29.0",
57
+ "@babel/helper-compilation-targets": "^7.28.6",
58
+ "@babel/helper-module-transforms": "^7.28.6",
59
+ "@babel/helpers": "^7.28.6",
60
+ "@babel/parser": "^7.29.0",
61
+ "@babel/template": "^7.28.6",
62
+ "@babel/traverse": "^7.29.0",
63
+ "@babel/types": "^7.29.0",
64
+ "@jridgewell/remapping": "^2.3.5",
65
+ "convert-source-map": "^2.0.0",
66
+ "debug": "^4.1.0",
67
+ "gensync": "^1.0.0-beta.2",
68
+ "json5": "^2.2.3",
69
+ "semver": "^6.3.1"
70
+ },
71
+ "engines": {
72
+ "node": ">=6.9.0"
73
+ },
74
+ "funding": {
75
+ "type": "opencollective",
76
+ "url": "https://opencollective.com/babel"
77
+ }
78
+ },
79
+ "node_modules/@babel/core/node_modules/convert-source-map": {
80
+ "version": "2.0.0",
81
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
82
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
83
+ "dev": true,
84
+ "license": "MIT"
85
+ },
86
+ "node_modules/@babel/generator": {
87
+ "version": "7.29.1",
88
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
89
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
90
+ "license": "MIT",
91
+ "dependencies": {
92
+ "@babel/parser": "^7.29.0",
93
+ "@babel/types": "^7.29.0",
94
+ "@jridgewell/gen-mapping": "^0.3.12",
95
+ "@jridgewell/trace-mapping": "^0.3.28",
96
+ "jsesc": "^3.0.2"
97
+ },
98
+ "engines": {
99
+ "node": ">=6.9.0"
100
+ }
101
+ },
102
+ "node_modules/@babel/helper-compilation-targets": {
103
+ "version": "7.28.6",
104
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
105
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
106
+ "dev": true,
107
+ "license": "MIT",
108
+ "dependencies": {
109
+ "@babel/compat-data": "^7.28.6",
110
+ "@babel/helper-validator-option": "^7.27.1",
111
+ "browserslist": "^4.24.0",
112
+ "lru-cache": "^5.1.1",
113
+ "semver": "^6.3.1"
114
+ },
115
+ "engines": {
116
+ "node": ">=6.9.0"
117
+ }
118
+ },
119
+ "node_modules/@babel/helper-globals": {
120
+ "version": "7.28.0",
121
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
122
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
123
+ "license": "MIT",
124
+ "engines": {
125
+ "node": ">=6.9.0"
126
+ }
127
+ },
128
+ "node_modules/@babel/helper-module-imports": {
129
+ "version": "7.28.6",
130
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
131
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
132
+ "license": "MIT",
133
+ "dependencies": {
134
+ "@babel/traverse": "^7.28.6",
135
+ "@babel/types": "^7.28.6"
136
+ },
137
+ "engines": {
138
+ "node": ">=6.9.0"
139
+ }
140
+ },
141
+ "node_modules/@babel/helper-module-transforms": {
142
+ "version": "7.28.6",
143
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
144
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
145
+ "dev": true,
146
+ "license": "MIT",
147
+ "dependencies": {
148
+ "@babel/helper-module-imports": "^7.28.6",
149
+ "@babel/helper-validator-identifier": "^7.28.5",
150
+ "@babel/traverse": "^7.28.6"
151
+ },
152
+ "engines": {
153
+ "node": ">=6.9.0"
154
+ },
155
+ "peerDependencies": {
156
+ "@babel/core": "^7.0.0"
157
+ }
158
+ },
159
+ "node_modules/@babel/helper-plugin-utils": {
160
+ "version": "7.28.6",
161
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
162
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
163
+ "dev": true,
164
+ "license": "MIT",
165
+ "engines": {
166
+ "node": ">=6.9.0"
167
+ }
168
+ },
169
+ "node_modules/@babel/helper-string-parser": {
170
+ "version": "7.27.1",
171
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
172
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
173
+ "license": "MIT",
174
+ "engines": {
175
+ "node": ">=6.9.0"
176
+ }
177
+ },
178
+ "node_modules/@babel/helper-validator-identifier": {
179
+ "version": "7.28.5",
180
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
181
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
182
+ "license": "MIT",
183
+ "engines": {
184
+ "node": ">=6.9.0"
185
+ }
186
+ },
187
+ "node_modules/@babel/helper-validator-option": {
188
+ "version": "7.27.1",
189
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
190
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
191
+ "dev": true,
192
+ "license": "MIT",
193
+ "engines": {
194
+ "node": ">=6.9.0"
195
+ }
196
+ },
197
+ "node_modules/@babel/helpers": {
198
+ "version": "7.28.6",
199
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
200
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
201
+ "dev": true,
202
+ "license": "MIT",
203
+ "dependencies": {
204
+ "@babel/template": "^7.28.6",
205
+ "@babel/types": "^7.28.6"
206
+ },
207
+ "engines": {
208
+ "node": ">=6.9.0"
209
+ }
210
+ },
211
+ "node_modules/@babel/parser": {
212
+ "version": "7.29.0",
213
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
214
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
215
+ "license": "MIT",
216
+ "dependencies": {
217
+ "@babel/types": "^7.29.0"
218
+ },
219
+ "bin": {
220
+ "parser": "bin/babel-parser.js"
221
+ },
222
+ "engines": {
223
+ "node": ">=6.0.0"
224
+ }
225
+ },
226
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
227
+ "version": "7.27.1",
228
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
229
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
230
+ "dev": true,
231
+ "license": "MIT",
232
+ "dependencies": {
233
+ "@babel/helper-plugin-utils": "^7.27.1"
234
+ },
235
+ "engines": {
236
+ "node": ">=6.9.0"
237
+ },
238
+ "peerDependencies": {
239
+ "@babel/core": "^7.0.0-0"
240
+ }
241
+ },
242
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
243
+ "version": "7.27.1",
244
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
245
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
246
+ "dev": true,
247
+ "license": "MIT",
248
+ "dependencies": {
249
+ "@babel/helper-plugin-utils": "^7.27.1"
250
+ },
251
+ "engines": {
252
+ "node": ">=6.9.0"
253
+ },
254
+ "peerDependencies": {
255
+ "@babel/core": "^7.0.0-0"
256
+ }
257
+ },
258
+ "node_modules/@babel/runtime": {
259
+ "version": "7.28.6",
260
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
261
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
262
+ "license": "MIT",
263
+ "engines": {
264
+ "node": ">=6.9.0"
265
+ }
266
+ },
267
+ "node_modules/@babel/template": {
268
+ "version": "7.28.6",
269
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
270
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
271
+ "license": "MIT",
272
+ "dependencies": {
273
+ "@babel/code-frame": "^7.28.6",
274
+ "@babel/parser": "^7.28.6",
275
+ "@babel/types": "^7.28.6"
276
+ },
277
+ "engines": {
278
+ "node": ">=6.9.0"
279
+ }
280
+ },
281
+ "node_modules/@babel/traverse": {
282
+ "version": "7.29.0",
283
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
284
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
285
+ "license": "MIT",
286
+ "dependencies": {
287
+ "@babel/code-frame": "^7.29.0",
288
+ "@babel/generator": "^7.29.0",
289
+ "@babel/helper-globals": "^7.28.0",
290
+ "@babel/parser": "^7.29.0",
291
+ "@babel/template": "^7.28.6",
292
+ "@babel/types": "^7.29.0",
293
+ "debug": "^4.3.1"
294
+ },
295
+ "engines": {
296
+ "node": ">=6.9.0"
297
+ }
298
+ },
299
+ "node_modules/@babel/types": {
300
+ "version": "7.29.0",
301
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
302
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
303
+ "license": "MIT",
304
+ "dependencies": {
305
+ "@babel/helper-string-parser": "^7.27.1",
306
+ "@babel/helper-validator-identifier": "^7.28.5"
307
+ },
308
+ "engines": {
309
+ "node": ">=6.9.0"
310
+ }
311
+ },
312
+ "node_modules/@emotion/babel-plugin": {
313
+ "version": "11.13.5",
314
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
315
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
316
+ "license": "MIT",
317
+ "dependencies": {
318
+ "@babel/helper-module-imports": "^7.16.7",
319
+ "@babel/runtime": "^7.18.3",
320
+ "@emotion/hash": "^0.9.2",
321
+ "@emotion/memoize": "^0.9.0",
322
+ "@emotion/serialize": "^1.3.3",
323
+ "babel-plugin-macros": "^3.1.0",
324
+ "convert-source-map": "^1.5.0",
325
+ "escape-string-regexp": "^4.0.0",
326
+ "find-root": "^1.1.0",
327
+ "source-map": "^0.5.7",
328
+ "stylis": "4.2.0"
329
+ }
330
+ },
331
+ "node_modules/@emotion/cache": {
332
+ "version": "11.14.0",
333
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
334
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
335
+ "license": "MIT",
336
+ "dependencies": {
337
+ "@emotion/memoize": "^0.9.0",
338
+ "@emotion/sheet": "^1.4.0",
339
+ "@emotion/utils": "^1.4.2",
340
+ "@emotion/weak-memoize": "^0.4.0",
341
+ "stylis": "4.2.0"
342
+ }
343
+ },
344
+ "node_modules/@emotion/hash": {
345
+ "version": "0.9.2",
346
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
347
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
348
+ "license": "MIT"
349
+ },
350
+ "node_modules/@emotion/is-prop-valid": {
351
+ "version": "1.4.0",
352
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
353
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
354
+ "license": "MIT",
355
+ "dependencies": {
356
+ "@emotion/memoize": "^0.9.0"
357
+ }
358
+ },
359
+ "node_modules/@emotion/memoize": {
360
+ "version": "0.9.0",
361
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
362
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
363
+ "license": "MIT"
364
+ },
365
+ "node_modules/@emotion/react": {
366
+ "version": "11.14.0",
367
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
368
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
369
+ "license": "MIT",
370
+ "dependencies": {
371
+ "@babel/runtime": "^7.18.3",
372
+ "@emotion/babel-plugin": "^11.13.5",
373
+ "@emotion/cache": "^11.14.0",
374
+ "@emotion/serialize": "^1.3.3",
375
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
376
+ "@emotion/utils": "^1.4.2",
377
+ "@emotion/weak-memoize": "^0.4.0",
378
+ "hoist-non-react-statics": "^3.3.1"
379
+ },
380
+ "peerDependencies": {
381
+ "react": ">=16.8.0"
382
+ },
383
+ "peerDependenciesMeta": {
384
+ "@types/react": {
385
+ "optional": true
386
+ }
387
+ }
388
+ },
389
+ "node_modules/@emotion/serialize": {
390
+ "version": "1.3.3",
391
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
392
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
393
+ "license": "MIT",
394
+ "dependencies": {
395
+ "@emotion/hash": "^0.9.2",
396
+ "@emotion/memoize": "^0.9.0",
397
+ "@emotion/unitless": "^0.10.0",
398
+ "@emotion/utils": "^1.4.2",
399
+ "csstype": "^3.0.2"
400
+ }
401
+ },
402
+ "node_modules/@emotion/sheet": {
403
+ "version": "1.4.0",
404
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
405
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
406
+ "license": "MIT"
407
+ },
408
+ "node_modules/@emotion/styled": {
409
+ "version": "11.14.1",
410
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
411
+ "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
412
+ "license": "MIT",
413
+ "dependencies": {
414
+ "@babel/runtime": "^7.18.3",
415
+ "@emotion/babel-plugin": "^11.13.5",
416
+ "@emotion/is-prop-valid": "^1.3.0",
417
+ "@emotion/serialize": "^1.3.3",
418
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
419
+ "@emotion/utils": "^1.4.2"
420
+ },
421
+ "peerDependencies": {
422
+ "@emotion/react": "^11.0.0-rc.0",
423
+ "react": ">=16.8.0"
424
+ },
425
+ "peerDependenciesMeta": {
426
+ "@types/react": {
427
+ "optional": true
428
+ }
429
+ }
430
+ },
431
+ "node_modules/@emotion/unitless": {
432
+ "version": "0.10.0",
433
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
434
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
435
+ "license": "MIT"
436
+ },
437
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
438
+ "version": "1.2.0",
439
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
440
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
441
+ "license": "MIT",
442
+ "peerDependencies": {
443
+ "react": ">=16.8.0"
444
+ }
445
+ },
446
+ "node_modules/@emotion/utils": {
447
+ "version": "1.4.2",
448
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
449
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
450
+ "license": "MIT"
451
+ },
452
+ "node_modules/@emotion/weak-memoize": {
453
+ "version": "0.4.0",
454
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
455
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
456
+ "license": "MIT"
457
+ },
458
+ "node_modules/@esbuild/aix-ppc64": {
459
+ "version": "0.21.5",
460
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
461
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
462
+ "cpu": [
463
+ "ppc64"
464
+ ],
465
+ "dev": true,
466
+ "license": "MIT",
467
+ "optional": true,
468
+ "os": [
469
+ "aix"
470
+ ],
471
+ "engines": {
472
+ "node": ">=12"
473
+ }
474
+ },
475
+ "node_modules/@esbuild/android-arm": {
476
+ "version": "0.21.5",
477
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
478
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
479
+ "cpu": [
480
+ "arm"
481
+ ],
482
+ "dev": true,
483
+ "license": "MIT",
484
+ "optional": true,
485
+ "os": [
486
+ "android"
487
+ ],
488
+ "engines": {
489
+ "node": ">=12"
490
+ }
491
+ },
492
+ "node_modules/@esbuild/android-arm64": {
493
+ "version": "0.21.5",
494
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
495
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
496
+ "cpu": [
497
+ "arm64"
498
+ ],
499
+ "dev": true,
500
+ "license": "MIT",
501
+ "optional": true,
502
+ "os": [
503
+ "android"
504
+ ],
505
+ "engines": {
506
+ "node": ">=12"
507
+ }
508
+ },
509
+ "node_modules/@esbuild/android-x64": {
510
+ "version": "0.21.5",
511
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
512
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
513
+ "cpu": [
514
+ "x64"
515
+ ],
516
+ "dev": true,
517
+ "license": "MIT",
518
+ "optional": true,
519
+ "os": [
520
+ "android"
521
+ ],
522
+ "engines": {
523
+ "node": ">=12"
524
+ }
525
+ },
526
+ "node_modules/@esbuild/darwin-arm64": {
527
+ "version": "0.21.5",
528
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
529
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
530
+ "cpu": [
531
+ "arm64"
532
+ ],
533
+ "dev": true,
534
+ "license": "MIT",
535
+ "optional": true,
536
+ "os": [
537
+ "darwin"
538
+ ],
539
+ "engines": {
540
+ "node": ">=12"
541
+ }
542
+ },
543
+ "node_modules/@esbuild/darwin-x64": {
544
+ "version": "0.21.5",
545
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
546
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
547
+ "cpu": [
548
+ "x64"
549
+ ],
550
+ "dev": true,
551
+ "license": "MIT",
552
+ "optional": true,
553
+ "os": [
554
+ "darwin"
555
+ ],
556
+ "engines": {
557
+ "node": ">=12"
558
+ }
559
+ },
560
+ "node_modules/@esbuild/freebsd-arm64": {
561
+ "version": "0.21.5",
562
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
563
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
564
+ "cpu": [
565
+ "arm64"
566
+ ],
567
+ "dev": true,
568
+ "license": "MIT",
569
+ "optional": true,
570
+ "os": [
571
+ "freebsd"
572
+ ],
573
+ "engines": {
574
+ "node": ">=12"
575
+ }
576
+ },
577
+ "node_modules/@esbuild/freebsd-x64": {
578
+ "version": "0.21.5",
579
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
580
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
581
+ "cpu": [
582
+ "x64"
583
+ ],
584
+ "dev": true,
585
+ "license": "MIT",
586
+ "optional": true,
587
+ "os": [
588
+ "freebsd"
589
+ ],
590
+ "engines": {
591
+ "node": ">=12"
592
+ }
593
+ },
594
+ "node_modules/@esbuild/linux-arm": {
595
+ "version": "0.21.5",
596
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
597
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
598
+ "cpu": [
599
+ "arm"
600
+ ],
601
+ "dev": true,
602
+ "license": "MIT",
603
+ "optional": true,
604
+ "os": [
605
+ "linux"
606
+ ],
607
+ "engines": {
608
+ "node": ">=12"
609
+ }
610
+ },
611
+ "node_modules/@esbuild/linux-arm64": {
612
+ "version": "0.21.5",
613
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
614
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
615
+ "cpu": [
616
+ "arm64"
617
+ ],
618
+ "dev": true,
619
+ "license": "MIT",
620
+ "optional": true,
621
+ "os": [
622
+ "linux"
623
+ ],
624
+ "engines": {
625
+ "node": ">=12"
626
+ }
627
+ },
628
+ "node_modules/@esbuild/linux-ia32": {
629
+ "version": "0.21.5",
630
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
631
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
632
+ "cpu": [
633
+ "ia32"
634
+ ],
635
+ "dev": true,
636
+ "license": "MIT",
637
+ "optional": true,
638
+ "os": [
639
+ "linux"
640
+ ],
641
+ "engines": {
642
+ "node": ">=12"
643
+ }
644
+ },
645
+ "node_modules/@esbuild/linux-loong64": {
646
+ "version": "0.21.5",
647
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
648
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
649
+ "cpu": [
650
+ "loong64"
651
+ ],
652
+ "dev": true,
653
+ "license": "MIT",
654
+ "optional": true,
655
+ "os": [
656
+ "linux"
657
+ ],
658
+ "engines": {
659
+ "node": ">=12"
660
+ }
661
+ },
662
+ "node_modules/@esbuild/linux-mips64el": {
663
+ "version": "0.21.5",
664
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
665
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
666
+ "cpu": [
667
+ "mips64el"
668
+ ],
669
+ "dev": true,
670
+ "license": "MIT",
671
+ "optional": true,
672
+ "os": [
673
+ "linux"
674
+ ],
675
+ "engines": {
676
+ "node": ">=12"
677
+ }
678
+ },
679
+ "node_modules/@esbuild/linux-ppc64": {
680
+ "version": "0.21.5",
681
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
682
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
683
+ "cpu": [
684
+ "ppc64"
685
+ ],
686
+ "dev": true,
687
+ "license": "MIT",
688
+ "optional": true,
689
+ "os": [
690
+ "linux"
691
+ ],
692
+ "engines": {
693
+ "node": ">=12"
694
+ }
695
+ },
696
+ "node_modules/@esbuild/linux-riscv64": {
697
+ "version": "0.21.5",
698
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
699
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
700
+ "cpu": [
701
+ "riscv64"
702
+ ],
703
+ "dev": true,
704
+ "license": "MIT",
705
+ "optional": true,
706
+ "os": [
707
+ "linux"
708
+ ],
709
+ "engines": {
710
+ "node": ">=12"
711
+ }
712
+ },
713
+ "node_modules/@esbuild/linux-s390x": {
714
+ "version": "0.21.5",
715
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
716
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
717
+ "cpu": [
718
+ "s390x"
719
+ ],
720
+ "dev": true,
721
+ "license": "MIT",
722
+ "optional": true,
723
+ "os": [
724
+ "linux"
725
+ ],
726
+ "engines": {
727
+ "node": ">=12"
728
+ }
729
+ },
730
+ "node_modules/@esbuild/linux-x64": {
731
+ "version": "0.21.5",
732
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
733
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
734
+ "cpu": [
735
+ "x64"
736
+ ],
737
+ "dev": true,
738
+ "license": "MIT",
739
+ "optional": true,
740
+ "os": [
741
+ "linux"
742
+ ],
743
+ "engines": {
744
+ "node": ">=12"
745
+ }
746
+ },
747
+ "node_modules/@esbuild/netbsd-x64": {
748
+ "version": "0.21.5",
749
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
750
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
751
+ "cpu": [
752
+ "x64"
753
+ ],
754
+ "dev": true,
755
+ "license": "MIT",
756
+ "optional": true,
757
+ "os": [
758
+ "netbsd"
759
+ ],
760
+ "engines": {
761
+ "node": ">=12"
762
+ }
763
+ },
764
+ "node_modules/@esbuild/openbsd-x64": {
765
+ "version": "0.21.5",
766
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
767
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
768
+ "cpu": [
769
+ "x64"
770
+ ],
771
+ "dev": true,
772
+ "license": "MIT",
773
+ "optional": true,
774
+ "os": [
775
+ "openbsd"
776
+ ],
777
+ "engines": {
778
+ "node": ">=12"
779
+ }
780
+ },
781
+ "node_modules/@esbuild/sunos-x64": {
782
+ "version": "0.21.5",
783
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
784
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
785
+ "cpu": [
786
+ "x64"
787
+ ],
788
+ "dev": true,
789
+ "license": "MIT",
790
+ "optional": true,
791
+ "os": [
792
+ "sunos"
793
+ ],
794
+ "engines": {
795
+ "node": ">=12"
796
+ }
797
+ },
798
+ "node_modules/@esbuild/win32-arm64": {
799
+ "version": "0.21.5",
800
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
801
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
802
+ "cpu": [
803
+ "arm64"
804
+ ],
805
+ "dev": true,
806
+ "license": "MIT",
807
+ "optional": true,
808
+ "os": [
809
+ "win32"
810
+ ],
811
+ "engines": {
812
+ "node": ">=12"
813
+ }
814
+ },
815
+ "node_modules/@esbuild/win32-ia32": {
816
+ "version": "0.21.5",
817
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
818
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
819
+ "cpu": [
820
+ "ia32"
821
+ ],
822
+ "dev": true,
823
+ "license": "MIT",
824
+ "optional": true,
825
+ "os": [
826
+ "win32"
827
+ ],
828
+ "engines": {
829
+ "node": ">=12"
830
+ }
831
+ },
832
+ "node_modules/@esbuild/win32-x64": {
833
+ "version": "0.21.5",
834
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
835
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
836
+ "cpu": [
837
+ "x64"
838
+ ],
839
+ "dev": true,
840
+ "license": "MIT",
841
+ "optional": true,
842
+ "os": [
843
+ "win32"
844
+ ],
845
+ "engines": {
846
+ "node": ">=12"
847
+ }
848
+ },
849
+ "node_modules/@jridgewell/gen-mapping": {
850
+ "version": "0.3.13",
851
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
852
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
853
+ "license": "MIT",
854
+ "dependencies": {
855
+ "@jridgewell/sourcemap-codec": "^1.5.0",
856
+ "@jridgewell/trace-mapping": "^0.3.24"
857
+ }
858
+ },
859
+ "node_modules/@jridgewell/remapping": {
860
+ "version": "2.3.5",
861
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
862
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
863
+ "dev": true,
864
+ "license": "MIT",
865
+ "dependencies": {
866
+ "@jridgewell/gen-mapping": "^0.3.5",
867
+ "@jridgewell/trace-mapping": "^0.3.24"
868
+ }
869
+ },
870
+ "node_modules/@jridgewell/resolve-uri": {
871
+ "version": "3.1.2",
872
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
873
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
874
+ "license": "MIT",
875
+ "engines": {
876
+ "node": ">=6.0.0"
877
+ }
878
+ },
879
+ "node_modules/@jridgewell/sourcemap-codec": {
880
+ "version": "1.5.5",
881
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
882
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
883
+ "license": "MIT"
884
+ },
885
+ "node_modules/@jridgewell/trace-mapping": {
886
+ "version": "0.3.31",
887
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
888
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
889
+ "license": "MIT",
890
+ "dependencies": {
891
+ "@jridgewell/resolve-uri": "^3.1.0",
892
+ "@jridgewell/sourcemap-codec": "^1.4.14"
893
+ }
894
+ },
895
+ "node_modules/@mui/core-downloads-tracker": {
896
+ "version": "5.18.0",
897
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.18.0.tgz",
898
+ "integrity": "sha512-jbhwoQ1AY200PSSOrNXmrFCaSDSJWP7qk6urkTmIirvRXDROkqe+QwcLlUiw/PrREwsIF/vm3/dAXvjlMHF0RA==",
899
+ "license": "MIT",
900
+ "funding": {
901
+ "type": "opencollective",
902
+ "url": "https://opencollective.com/mui-org"
903
+ }
904
+ },
905
+ "node_modules/@mui/icons-material": {
906
+ "version": "5.18.0",
907
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.18.0.tgz",
908
+ "integrity": "sha512-1s0vEZj5XFXDMmz3Arl/R7IncFqJ+WQ95LDp1roHWGDE2oCO3IS4/hmiOv1/8SD9r6B7tv9GLiqVZYHo+6PkTg==",
909
+ "license": "MIT",
910
+ "dependencies": {
911
+ "@babel/runtime": "^7.23.9"
912
+ },
913
+ "engines": {
914
+ "node": ">=12.0.0"
915
+ },
916
+ "funding": {
917
+ "type": "opencollective",
918
+ "url": "https://opencollective.com/mui-org"
919
+ },
920
+ "peerDependencies": {
921
+ "@mui/material": "^5.0.0",
922
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
923
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
924
+ },
925
+ "peerDependenciesMeta": {
926
+ "@types/react": {
927
+ "optional": true
928
+ }
929
+ }
930
+ },
931
+ "node_modules/@mui/material": {
932
+ "version": "5.18.0",
933
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.18.0.tgz",
934
+ "integrity": "sha512-bbH/HaJZpFtXGvWg3TsBWG4eyt3gah3E7nCNU8GLyRjVoWcA91Vm/T+sjHfUcwgJSw9iLtucfHBoq+qW/T30aA==",
935
+ "license": "MIT",
936
+ "dependencies": {
937
+ "@babel/runtime": "^7.23.9",
938
+ "@mui/core-downloads-tracker": "^5.18.0",
939
+ "@mui/system": "^5.18.0",
940
+ "@mui/types": "~7.2.15",
941
+ "@mui/utils": "^5.17.1",
942
+ "@popperjs/core": "^2.11.8",
943
+ "@types/react-transition-group": "^4.4.10",
944
+ "clsx": "^2.1.0",
945
+ "csstype": "^3.1.3",
946
+ "prop-types": "^15.8.1",
947
+ "react-is": "^19.0.0",
948
+ "react-transition-group": "^4.4.5"
949
+ },
950
+ "engines": {
951
+ "node": ">=12.0.0"
952
+ },
953
+ "funding": {
954
+ "type": "opencollective",
955
+ "url": "https://opencollective.com/mui-org"
956
+ },
957
+ "peerDependencies": {
958
+ "@emotion/react": "^11.5.0",
959
+ "@emotion/styled": "^11.3.0",
960
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
961
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
962
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
963
+ },
964
+ "peerDependenciesMeta": {
965
+ "@emotion/react": {
966
+ "optional": true
967
+ },
968
+ "@emotion/styled": {
969
+ "optional": true
970
+ },
971
+ "@types/react": {
972
+ "optional": true
973
+ }
974
+ }
975
+ },
976
+ "node_modules/@mui/private-theming": {
977
+ "version": "5.17.1",
978
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz",
979
+ "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==",
980
+ "license": "MIT",
981
+ "dependencies": {
982
+ "@babel/runtime": "^7.23.9",
983
+ "@mui/utils": "^5.17.1",
984
+ "prop-types": "^15.8.1"
985
+ },
986
+ "engines": {
987
+ "node": ">=12.0.0"
988
+ },
989
+ "funding": {
990
+ "type": "opencollective",
991
+ "url": "https://opencollective.com/mui-org"
992
+ },
993
+ "peerDependencies": {
994
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
995
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
996
+ },
997
+ "peerDependenciesMeta": {
998
+ "@types/react": {
999
+ "optional": true
1000
+ }
1001
+ }
1002
+ },
1003
+ "node_modules/@mui/styled-engine": {
1004
+ "version": "5.18.0",
1005
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.18.0.tgz",
1006
+ "integrity": "sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==",
1007
+ "license": "MIT",
1008
+ "dependencies": {
1009
+ "@babel/runtime": "^7.23.9",
1010
+ "@emotion/cache": "^11.13.5",
1011
+ "@emotion/serialize": "^1.3.3",
1012
+ "csstype": "^3.1.3",
1013
+ "prop-types": "^15.8.1"
1014
+ },
1015
+ "engines": {
1016
+ "node": ">=12.0.0"
1017
+ },
1018
+ "funding": {
1019
+ "type": "opencollective",
1020
+ "url": "https://opencollective.com/mui-org"
1021
+ },
1022
+ "peerDependencies": {
1023
+ "@emotion/react": "^11.4.1",
1024
+ "@emotion/styled": "^11.3.0",
1025
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
1026
+ },
1027
+ "peerDependenciesMeta": {
1028
+ "@emotion/react": {
1029
+ "optional": true
1030
+ },
1031
+ "@emotion/styled": {
1032
+ "optional": true
1033
+ }
1034
+ }
1035
+ },
1036
+ "node_modules/@mui/system": {
1037
+ "version": "5.18.0",
1038
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.18.0.tgz",
1039
+ "integrity": "sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==",
1040
+ "license": "MIT",
1041
+ "dependencies": {
1042
+ "@babel/runtime": "^7.23.9",
1043
+ "@mui/private-theming": "^5.17.1",
1044
+ "@mui/styled-engine": "^5.18.0",
1045
+ "@mui/types": "~7.2.15",
1046
+ "@mui/utils": "^5.17.1",
1047
+ "clsx": "^2.1.0",
1048
+ "csstype": "^3.1.3",
1049
+ "prop-types": "^15.8.1"
1050
+ },
1051
+ "engines": {
1052
+ "node": ">=12.0.0"
1053
+ },
1054
+ "funding": {
1055
+ "type": "opencollective",
1056
+ "url": "https://opencollective.com/mui-org"
1057
+ },
1058
+ "peerDependencies": {
1059
+ "@emotion/react": "^11.5.0",
1060
+ "@emotion/styled": "^11.3.0",
1061
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
1062
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
1063
+ },
1064
+ "peerDependenciesMeta": {
1065
+ "@emotion/react": {
1066
+ "optional": true
1067
+ },
1068
+ "@emotion/styled": {
1069
+ "optional": true
1070
+ },
1071
+ "@types/react": {
1072
+ "optional": true
1073
+ }
1074
+ }
1075
+ },
1076
+ "node_modules/@mui/types": {
1077
+ "version": "7.2.24",
1078
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
1079
+ "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
1080
+ "license": "MIT",
1081
+ "peerDependencies": {
1082
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
1083
+ },
1084
+ "peerDependenciesMeta": {
1085
+ "@types/react": {
1086
+ "optional": true
1087
+ }
1088
+ }
1089
+ },
1090
+ "node_modules/@mui/utils": {
1091
+ "version": "5.17.1",
1092
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz",
1093
+ "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==",
1094
+ "license": "MIT",
1095
+ "dependencies": {
1096
+ "@babel/runtime": "^7.23.9",
1097
+ "@mui/types": "~7.2.15",
1098
+ "@types/prop-types": "^15.7.12",
1099
+ "clsx": "^2.1.1",
1100
+ "prop-types": "^15.8.1",
1101
+ "react-is": "^19.0.0"
1102
+ },
1103
+ "engines": {
1104
+ "node": ">=12.0.0"
1105
+ },
1106
+ "funding": {
1107
+ "type": "opencollective",
1108
+ "url": "https://opencollective.com/mui-org"
1109
+ },
1110
+ "peerDependencies": {
1111
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
1112
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
1113
+ },
1114
+ "peerDependenciesMeta": {
1115
+ "@types/react": {
1116
+ "optional": true
1117
+ }
1118
+ }
1119
+ },
1120
+ "node_modules/@popperjs/core": {
1121
+ "version": "2.11.8",
1122
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
1123
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
1124
+ "license": "MIT",
1125
+ "funding": {
1126
+ "type": "opencollective",
1127
+ "url": "https://opencollective.com/popperjs"
1128
+ }
1129
+ },
1130
+ "node_modules/@remix-run/router": {
1131
+ "version": "1.23.2",
1132
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
1133
+ "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
1134
+ "license": "MIT",
1135
+ "engines": {
1136
+ "node": ">=14.0.0"
1137
+ }
1138
+ },
1139
+ "node_modules/@rolldown/pluginutils": {
1140
+ "version": "1.0.0-beta.27",
1141
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
1142
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
1143
+ "dev": true,
1144
+ "license": "MIT"
1145
+ },
1146
+ "node_modules/@rollup/rollup-android-arm-eabi": {
1147
+ "version": "4.59.0",
1148
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
1149
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
1150
+ "cpu": [
1151
+ "arm"
1152
+ ],
1153
+ "dev": true,
1154
+ "license": "MIT",
1155
+ "optional": true,
1156
+ "os": [
1157
+ "android"
1158
+ ]
1159
+ },
1160
+ "node_modules/@rollup/rollup-android-arm64": {
1161
+ "version": "4.59.0",
1162
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
1163
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
1164
+ "cpu": [
1165
+ "arm64"
1166
+ ],
1167
+ "dev": true,
1168
+ "license": "MIT",
1169
+ "optional": true,
1170
+ "os": [
1171
+ "android"
1172
+ ]
1173
+ },
1174
+ "node_modules/@rollup/rollup-darwin-arm64": {
1175
+ "version": "4.59.0",
1176
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
1177
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
1178
+ "cpu": [
1179
+ "arm64"
1180
+ ],
1181
+ "dev": true,
1182
+ "license": "MIT",
1183
+ "optional": true,
1184
+ "os": [
1185
+ "darwin"
1186
+ ]
1187
+ },
1188
+ "node_modules/@rollup/rollup-darwin-x64": {
1189
+ "version": "4.59.0",
1190
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
1191
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
1192
+ "cpu": [
1193
+ "x64"
1194
+ ],
1195
+ "dev": true,
1196
+ "license": "MIT",
1197
+ "optional": true,
1198
+ "os": [
1199
+ "darwin"
1200
+ ]
1201
+ },
1202
+ "node_modules/@rollup/rollup-freebsd-arm64": {
1203
+ "version": "4.59.0",
1204
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
1205
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
1206
+ "cpu": [
1207
+ "arm64"
1208
+ ],
1209
+ "dev": true,
1210
+ "license": "MIT",
1211
+ "optional": true,
1212
+ "os": [
1213
+ "freebsd"
1214
+ ]
1215
+ },
1216
+ "node_modules/@rollup/rollup-freebsd-x64": {
1217
+ "version": "4.59.0",
1218
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
1219
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
1220
+ "cpu": [
1221
+ "x64"
1222
+ ],
1223
+ "dev": true,
1224
+ "license": "MIT",
1225
+ "optional": true,
1226
+ "os": [
1227
+ "freebsd"
1228
+ ]
1229
+ },
1230
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
1231
+ "version": "4.59.0",
1232
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
1233
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
1234
+ "cpu": [
1235
+ "arm"
1236
+ ],
1237
+ "dev": true,
1238
+ "license": "MIT",
1239
+ "optional": true,
1240
+ "os": [
1241
+ "linux"
1242
+ ]
1243
+ },
1244
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
1245
+ "version": "4.59.0",
1246
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
1247
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
1248
+ "cpu": [
1249
+ "arm"
1250
+ ],
1251
+ "dev": true,
1252
+ "license": "MIT",
1253
+ "optional": true,
1254
+ "os": [
1255
+ "linux"
1256
+ ]
1257
+ },
1258
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
1259
+ "version": "4.59.0",
1260
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
1261
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
1262
+ "cpu": [
1263
+ "arm64"
1264
+ ],
1265
+ "dev": true,
1266
+ "license": "MIT",
1267
+ "optional": true,
1268
+ "os": [
1269
+ "linux"
1270
+ ]
1271
+ },
1272
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
1273
+ "version": "4.59.0",
1274
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
1275
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
1276
+ "cpu": [
1277
+ "arm64"
1278
+ ],
1279
+ "dev": true,
1280
+ "license": "MIT",
1281
+ "optional": true,
1282
+ "os": [
1283
+ "linux"
1284
+ ]
1285
+ },
1286
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
1287
+ "version": "4.59.0",
1288
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
1289
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
1290
+ "cpu": [
1291
+ "loong64"
1292
+ ],
1293
+ "dev": true,
1294
+ "license": "MIT",
1295
+ "optional": true,
1296
+ "os": [
1297
+ "linux"
1298
+ ]
1299
+ },
1300
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
1301
+ "version": "4.59.0",
1302
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
1303
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
1304
+ "cpu": [
1305
+ "loong64"
1306
+ ],
1307
+ "dev": true,
1308
+ "license": "MIT",
1309
+ "optional": true,
1310
+ "os": [
1311
+ "linux"
1312
+ ]
1313
+ },
1314
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
1315
+ "version": "4.59.0",
1316
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
1317
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
1318
+ "cpu": [
1319
+ "ppc64"
1320
+ ],
1321
+ "dev": true,
1322
+ "license": "MIT",
1323
+ "optional": true,
1324
+ "os": [
1325
+ "linux"
1326
+ ]
1327
+ },
1328
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
1329
+ "version": "4.59.0",
1330
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
1331
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
1332
+ "cpu": [
1333
+ "ppc64"
1334
+ ],
1335
+ "dev": true,
1336
+ "license": "MIT",
1337
+ "optional": true,
1338
+ "os": [
1339
+ "linux"
1340
+ ]
1341
+ },
1342
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
1343
+ "version": "4.59.0",
1344
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
1345
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
1346
+ "cpu": [
1347
+ "riscv64"
1348
+ ],
1349
+ "dev": true,
1350
+ "license": "MIT",
1351
+ "optional": true,
1352
+ "os": [
1353
+ "linux"
1354
+ ]
1355
+ },
1356
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
1357
+ "version": "4.59.0",
1358
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
1359
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
1360
+ "cpu": [
1361
+ "riscv64"
1362
+ ],
1363
+ "dev": true,
1364
+ "license": "MIT",
1365
+ "optional": true,
1366
+ "os": [
1367
+ "linux"
1368
+ ]
1369
+ },
1370
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
1371
+ "version": "4.59.0",
1372
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
1373
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
1374
+ "cpu": [
1375
+ "s390x"
1376
+ ],
1377
+ "dev": true,
1378
+ "license": "MIT",
1379
+ "optional": true,
1380
+ "os": [
1381
+ "linux"
1382
+ ]
1383
+ },
1384
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
1385
+ "version": "4.59.0",
1386
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
1387
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
1388
+ "cpu": [
1389
+ "x64"
1390
+ ],
1391
+ "dev": true,
1392
+ "license": "MIT",
1393
+ "optional": true,
1394
+ "os": [
1395
+ "linux"
1396
+ ]
1397
+ },
1398
+ "node_modules/@rollup/rollup-linux-x64-musl": {
1399
+ "version": "4.59.0",
1400
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
1401
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
1402
+ "cpu": [
1403
+ "x64"
1404
+ ],
1405
+ "dev": true,
1406
+ "license": "MIT",
1407
+ "optional": true,
1408
+ "os": [
1409
+ "linux"
1410
+ ]
1411
+ },
1412
+ "node_modules/@rollup/rollup-openbsd-x64": {
1413
+ "version": "4.59.0",
1414
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
1415
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
1416
+ "cpu": [
1417
+ "x64"
1418
+ ],
1419
+ "dev": true,
1420
+ "license": "MIT",
1421
+ "optional": true,
1422
+ "os": [
1423
+ "openbsd"
1424
+ ]
1425
+ },
1426
+ "node_modules/@rollup/rollup-openharmony-arm64": {
1427
+ "version": "4.59.0",
1428
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
1429
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
1430
+ "cpu": [
1431
+ "arm64"
1432
+ ],
1433
+ "dev": true,
1434
+ "license": "MIT",
1435
+ "optional": true,
1436
+ "os": [
1437
+ "openharmony"
1438
+ ]
1439
+ },
1440
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
1441
+ "version": "4.59.0",
1442
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
1443
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
1444
+ "cpu": [
1445
+ "arm64"
1446
+ ],
1447
+ "dev": true,
1448
+ "license": "MIT",
1449
+ "optional": true,
1450
+ "os": [
1451
+ "win32"
1452
+ ]
1453
+ },
1454
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
1455
+ "version": "4.59.0",
1456
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
1457
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
1458
+ "cpu": [
1459
+ "ia32"
1460
+ ],
1461
+ "dev": true,
1462
+ "license": "MIT",
1463
+ "optional": true,
1464
+ "os": [
1465
+ "win32"
1466
+ ]
1467
+ },
1468
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
1469
+ "version": "4.59.0",
1470
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
1471
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
1472
+ "cpu": [
1473
+ "x64"
1474
+ ],
1475
+ "dev": true,
1476
+ "license": "MIT",
1477
+ "optional": true,
1478
+ "os": [
1479
+ "win32"
1480
+ ]
1481
+ },
1482
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
1483
+ "version": "4.59.0",
1484
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
1485
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
1486
+ "cpu": [
1487
+ "x64"
1488
+ ],
1489
+ "dev": true,
1490
+ "license": "MIT",
1491
+ "optional": true,
1492
+ "os": [
1493
+ "win32"
1494
+ ]
1495
+ },
1496
+ "node_modules/@types/babel__core": {
1497
+ "version": "7.20.5",
1498
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1499
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1500
+ "dev": true,
1501
+ "license": "MIT",
1502
+ "dependencies": {
1503
+ "@babel/parser": "^7.20.7",
1504
+ "@babel/types": "^7.20.7",
1505
+ "@types/babel__generator": "*",
1506
+ "@types/babel__template": "*",
1507
+ "@types/babel__traverse": "*"
1508
+ }
1509
+ },
1510
+ "node_modules/@types/babel__generator": {
1511
+ "version": "7.27.0",
1512
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1513
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1514
+ "dev": true,
1515
+ "license": "MIT",
1516
+ "dependencies": {
1517
+ "@babel/types": "^7.0.0"
1518
+ }
1519
+ },
1520
+ "node_modules/@types/babel__template": {
1521
+ "version": "7.4.4",
1522
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1523
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1524
+ "dev": true,
1525
+ "license": "MIT",
1526
+ "dependencies": {
1527
+ "@babel/parser": "^7.1.0",
1528
+ "@babel/types": "^7.0.0"
1529
+ }
1530
+ },
1531
+ "node_modules/@types/babel__traverse": {
1532
+ "version": "7.28.0",
1533
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1534
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1535
+ "dev": true,
1536
+ "license": "MIT",
1537
+ "dependencies": {
1538
+ "@babel/types": "^7.28.2"
1539
+ }
1540
+ },
1541
+ "node_modules/@types/estree": {
1542
+ "version": "1.0.8",
1543
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1544
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1545
+ "dev": true,
1546
+ "license": "MIT"
1547
+ },
1548
+ "node_modules/@types/parse-json": {
1549
+ "version": "4.0.2",
1550
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
1551
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
1552
+ "license": "MIT"
1553
+ },
1554
+ "node_modules/@types/prop-types": {
1555
+ "version": "15.7.15",
1556
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
1557
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
1558
+ "license": "MIT"
1559
+ },
1560
+ "node_modules/@types/react": {
1561
+ "version": "19.2.14",
1562
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
1563
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
1564
+ "license": "MIT",
1565
+ "peer": true,
1566
+ "dependencies": {
1567
+ "csstype": "^3.2.2"
1568
+ }
1569
+ },
1570
+ "node_modules/@types/react-transition-group": {
1571
+ "version": "4.4.12",
1572
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
1573
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
1574
+ "license": "MIT",
1575
+ "peerDependencies": {
1576
+ "@types/react": "*"
1577
+ }
1578
+ },
1579
+ "node_modules/@vitejs/plugin-react": {
1580
+ "version": "4.7.0",
1581
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1582
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1583
+ "dev": true,
1584
+ "license": "MIT",
1585
+ "dependencies": {
1586
+ "@babel/core": "^7.28.0",
1587
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1588
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1589
+ "@rolldown/pluginutils": "1.0.0-beta.27",
1590
+ "@types/babel__core": "^7.20.5",
1591
+ "react-refresh": "^0.17.0"
1592
+ },
1593
+ "engines": {
1594
+ "node": "^14.18.0 || >=16.0.0"
1595
+ },
1596
+ "peerDependencies": {
1597
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1598
+ }
1599
+ },
1600
+ "node_modules/babel-plugin-macros": {
1601
+ "version": "3.1.0",
1602
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
1603
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
1604
+ "license": "MIT",
1605
+ "dependencies": {
1606
+ "@babel/runtime": "^7.12.5",
1607
+ "cosmiconfig": "^7.0.0",
1608
+ "resolve": "^1.19.0"
1609
+ },
1610
+ "engines": {
1611
+ "node": ">=10",
1612
+ "npm": ">=6"
1613
+ }
1614
+ },
1615
+ "node_modules/baseline-browser-mapping": {
1616
+ "version": "2.10.0",
1617
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
1618
+ "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
1619
+ "dev": true,
1620
+ "license": "Apache-2.0",
1621
+ "bin": {
1622
+ "baseline-browser-mapping": "dist/cli.cjs"
1623
+ },
1624
+ "engines": {
1625
+ "node": ">=6.0.0"
1626
+ }
1627
+ },
1628
+ "node_modules/browserslist": {
1629
+ "version": "4.28.1",
1630
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
1631
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
1632
+ "dev": true,
1633
+ "funding": [
1634
+ {
1635
+ "type": "opencollective",
1636
+ "url": "https://opencollective.com/browserslist"
1637
+ },
1638
+ {
1639
+ "type": "tidelift",
1640
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1641
+ },
1642
+ {
1643
+ "type": "github",
1644
+ "url": "https://github.com/sponsors/ai"
1645
+ }
1646
+ ],
1647
+ "license": "MIT",
1648
+ "dependencies": {
1649
+ "baseline-browser-mapping": "^2.9.0",
1650
+ "caniuse-lite": "^1.0.30001759",
1651
+ "electron-to-chromium": "^1.5.263",
1652
+ "node-releases": "^2.0.27",
1653
+ "update-browserslist-db": "^1.2.0"
1654
+ },
1655
+ "bin": {
1656
+ "browserslist": "cli.js"
1657
+ },
1658
+ "engines": {
1659
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1660
+ }
1661
+ },
1662
+ "node_modules/callsites": {
1663
+ "version": "3.1.0",
1664
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
1665
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
1666
+ "license": "MIT",
1667
+ "engines": {
1668
+ "node": ">=6"
1669
+ }
1670
+ },
1671
+ "node_modules/caniuse-lite": {
1672
+ "version": "1.0.30001775",
1673
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
1674
+ "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
1675
+ "dev": true,
1676
+ "funding": [
1677
+ {
1678
+ "type": "opencollective",
1679
+ "url": "https://opencollective.com/browserslist"
1680
+ },
1681
+ {
1682
+ "type": "tidelift",
1683
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1684
+ },
1685
+ {
1686
+ "type": "github",
1687
+ "url": "https://github.com/sponsors/ai"
1688
+ }
1689
+ ],
1690
+ "license": "CC-BY-4.0"
1691
+ },
1692
+ "node_modules/clsx": {
1693
+ "version": "2.1.1",
1694
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
1695
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
1696
+ "license": "MIT",
1697
+ "engines": {
1698
+ "node": ">=6"
1699
+ }
1700
+ },
1701
+ "node_modules/convert-source-map": {
1702
+ "version": "1.9.0",
1703
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
1704
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
1705
+ "license": "MIT"
1706
+ },
1707
+ "node_modules/cosmiconfig": {
1708
+ "version": "7.1.0",
1709
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
1710
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
1711
+ "license": "MIT",
1712
+ "dependencies": {
1713
+ "@types/parse-json": "^4.0.0",
1714
+ "import-fresh": "^3.2.1",
1715
+ "parse-json": "^5.0.0",
1716
+ "path-type": "^4.0.0",
1717
+ "yaml": "^1.10.0"
1718
+ },
1719
+ "engines": {
1720
+ "node": ">=10"
1721
+ }
1722
+ },
1723
+ "node_modules/csstype": {
1724
+ "version": "3.2.3",
1725
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1726
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1727
+ "license": "MIT"
1728
+ },
1729
+ "node_modules/debug": {
1730
+ "version": "4.4.3",
1731
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1732
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1733
+ "license": "MIT",
1734
+ "dependencies": {
1735
+ "ms": "^2.1.3"
1736
+ },
1737
+ "engines": {
1738
+ "node": ">=6.0"
1739
+ },
1740
+ "peerDependenciesMeta": {
1741
+ "supports-color": {
1742
+ "optional": true
1743
+ }
1744
+ }
1745
+ },
1746
+ "node_modules/dom-helpers": {
1747
+ "version": "5.2.1",
1748
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
1749
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
1750
+ "license": "MIT",
1751
+ "dependencies": {
1752
+ "@babel/runtime": "^7.8.7",
1753
+ "csstype": "^3.0.2"
1754
+ }
1755
+ },
1756
+ "node_modules/electron-to-chromium": {
1757
+ "version": "1.5.302",
1758
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
1759
+ "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
1760
+ "dev": true,
1761
+ "license": "ISC"
1762
+ },
1763
+ "node_modules/error-ex": {
1764
+ "version": "1.3.4",
1765
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
1766
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
1767
+ "license": "MIT",
1768
+ "dependencies": {
1769
+ "is-arrayish": "^0.2.1"
1770
+ }
1771
+ },
1772
+ "node_modules/esbuild": {
1773
+ "version": "0.21.5",
1774
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
1775
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
1776
+ "dev": true,
1777
+ "hasInstallScript": true,
1778
+ "license": "MIT",
1779
+ "bin": {
1780
+ "esbuild": "bin/esbuild"
1781
+ },
1782
+ "engines": {
1783
+ "node": ">=12"
1784
+ },
1785
+ "optionalDependencies": {
1786
+ "@esbuild/aix-ppc64": "0.21.5",
1787
+ "@esbuild/android-arm": "0.21.5",
1788
+ "@esbuild/android-arm64": "0.21.5",
1789
+ "@esbuild/android-x64": "0.21.5",
1790
+ "@esbuild/darwin-arm64": "0.21.5",
1791
+ "@esbuild/darwin-x64": "0.21.5",
1792
+ "@esbuild/freebsd-arm64": "0.21.5",
1793
+ "@esbuild/freebsd-x64": "0.21.5",
1794
+ "@esbuild/linux-arm": "0.21.5",
1795
+ "@esbuild/linux-arm64": "0.21.5",
1796
+ "@esbuild/linux-ia32": "0.21.5",
1797
+ "@esbuild/linux-loong64": "0.21.5",
1798
+ "@esbuild/linux-mips64el": "0.21.5",
1799
+ "@esbuild/linux-ppc64": "0.21.5",
1800
+ "@esbuild/linux-riscv64": "0.21.5",
1801
+ "@esbuild/linux-s390x": "0.21.5",
1802
+ "@esbuild/linux-x64": "0.21.5",
1803
+ "@esbuild/netbsd-x64": "0.21.5",
1804
+ "@esbuild/openbsd-x64": "0.21.5",
1805
+ "@esbuild/sunos-x64": "0.21.5",
1806
+ "@esbuild/win32-arm64": "0.21.5",
1807
+ "@esbuild/win32-ia32": "0.21.5",
1808
+ "@esbuild/win32-x64": "0.21.5"
1809
+ }
1810
+ },
1811
+ "node_modules/escalade": {
1812
+ "version": "3.2.0",
1813
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1814
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1815
+ "dev": true,
1816
+ "license": "MIT",
1817
+ "engines": {
1818
+ "node": ">=6"
1819
+ }
1820
+ },
1821
+ "node_modules/escape-string-regexp": {
1822
+ "version": "4.0.0",
1823
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
1824
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
1825
+ "license": "MIT",
1826
+ "engines": {
1827
+ "node": ">=10"
1828
+ },
1829
+ "funding": {
1830
+ "url": "https://github.com/sponsors/sindresorhus"
1831
+ }
1832
+ },
1833
+ "node_modules/find-root": {
1834
+ "version": "1.1.0",
1835
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
1836
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
1837
+ "license": "MIT"
1838
+ },
1839
+ "node_modules/fsevents": {
1840
+ "version": "2.3.3",
1841
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1842
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1843
+ "dev": true,
1844
+ "hasInstallScript": true,
1845
+ "license": "MIT",
1846
+ "optional": true,
1847
+ "os": [
1848
+ "darwin"
1849
+ ],
1850
+ "engines": {
1851
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1852
+ }
1853
+ },
1854
+ "node_modules/function-bind": {
1855
+ "version": "1.1.2",
1856
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1857
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1858
+ "license": "MIT",
1859
+ "funding": {
1860
+ "url": "https://github.com/sponsors/ljharb"
1861
+ }
1862
+ },
1863
+ "node_modules/gensync": {
1864
+ "version": "1.0.0-beta.2",
1865
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1866
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1867
+ "dev": true,
1868
+ "license": "MIT",
1869
+ "engines": {
1870
+ "node": ">=6.9.0"
1871
+ }
1872
+ },
1873
+ "node_modules/hasown": {
1874
+ "version": "2.0.2",
1875
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
1876
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1877
+ "license": "MIT",
1878
+ "dependencies": {
1879
+ "function-bind": "^1.1.2"
1880
+ },
1881
+ "engines": {
1882
+ "node": ">= 0.4"
1883
+ }
1884
+ },
1885
+ "node_modules/hoist-non-react-statics": {
1886
+ "version": "3.3.2",
1887
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
1888
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
1889
+ "license": "BSD-3-Clause",
1890
+ "dependencies": {
1891
+ "react-is": "^16.7.0"
1892
+ }
1893
+ },
1894
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
1895
+ "version": "16.13.1",
1896
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
1897
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
1898
+ "license": "MIT"
1899
+ },
1900
+ "node_modules/import-fresh": {
1901
+ "version": "3.3.1",
1902
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
1903
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
1904
+ "license": "MIT",
1905
+ "dependencies": {
1906
+ "parent-module": "^1.0.0",
1907
+ "resolve-from": "^4.0.0"
1908
+ },
1909
+ "engines": {
1910
+ "node": ">=6"
1911
+ },
1912
+ "funding": {
1913
+ "url": "https://github.com/sponsors/sindresorhus"
1914
+ }
1915
+ },
1916
+ "node_modules/is-arrayish": {
1917
+ "version": "0.2.1",
1918
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1919
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
1920
+ "license": "MIT"
1921
+ },
1922
+ "node_modules/is-core-module": {
1923
+ "version": "2.16.1",
1924
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
1925
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
1926
+ "license": "MIT",
1927
+ "dependencies": {
1928
+ "hasown": "^2.0.2"
1929
+ },
1930
+ "engines": {
1931
+ "node": ">= 0.4"
1932
+ },
1933
+ "funding": {
1934
+ "url": "https://github.com/sponsors/ljharb"
1935
+ }
1936
+ },
1937
+ "node_modules/js-tokens": {
1938
+ "version": "4.0.0",
1939
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1940
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1941
+ "license": "MIT"
1942
+ },
1943
+ "node_modules/jsesc": {
1944
+ "version": "3.1.0",
1945
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1946
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1947
+ "license": "MIT",
1948
+ "bin": {
1949
+ "jsesc": "bin/jsesc"
1950
+ },
1951
+ "engines": {
1952
+ "node": ">=6"
1953
+ }
1954
+ },
1955
+ "node_modules/json-parse-even-better-errors": {
1956
+ "version": "2.3.1",
1957
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
1958
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
1959
+ "license": "MIT"
1960
+ },
1961
+ "node_modules/json5": {
1962
+ "version": "2.2.3",
1963
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1964
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1965
+ "dev": true,
1966
+ "license": "MIT",
1967
+ "bin": {
1968
+ "json5": "lib/cli.js"
1969
+ },
1970
+ "engines": {
1971
+ "node": ">=6"
1972
+ }
1973
+ },
1974
+ "node_modules/lines-and-columns": {
1975
+ "version": "1.2.4",
1976
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
1977
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
1978
+ "license": "MIT"
1979
+ },
1980
+ "node_modules/loose-envify": {
1981
+ "version": "1.4.0",
1982
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1983
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1984
+ "license": "MIT",
1985
+ "dependencies": {
1986
+ "js-tokens": "^3.0.0 || ^4.0.0"
1987
+ },
1988
+ "bin": {
1989
+ "loose-envify": "cli.js"
1990
+ }
1991
+ },
1992
+ "node_modules/lru-cache": {
1993
+ "version": "5.1.1",
1994
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1995
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1996
+ "dev": true,
1997
+ "license": "ISC",
1998
+ "dependencies": {
1999
+ "yallist": "^3.0.2"
2000
+ }
2001
+ },
2002
+ "node_modules/ms": {
2003
+ "version": "2.1.3",
2004
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2005
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2006
+ "license": "MIT"
2007
+ },
2008
+ "node_modules/nanoid": {
2009
+ "version": "3.3.11",
2010
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
2011
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
2012
+ "dev": true,
2013
+ "funding": [
2014
+ {
2015
+ "type": "github",
2016
+ "url": "https://github.com/sponsors/ai"
2017
+ }
2018
+ ],
2019
+ "license": "MIT",
2020
+ "bin": {
2021
+ "nanoid": "bin/nanoid.cjs"
2022
+ },
2023
+ "engines": {
2024
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2025
+ }
2026
+ },
2027
+ "node_modules/node-releases": {
2028
+ "version": "2.0.27",
2029
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
2030
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
2031
+ "dev": true,
2032
+ "license": "MIT"
2033
+ },
2034
+ "node_modules/object-assign": {
2035
+ "version": "4.1.1",
2036
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
2037
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
2038
+ "license": "MIT",
2039
+ "engines": {
2040
+ "node": ">=0.10.0"
2041
+ }
2042
+ },
2043
+ "node_modules/parent-module": {
2044
+ "version": "1.0.1",
2045
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
2046
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
2047
+ "license": "MIT",
2048
+ "dependencies": {
2049
+ "callsites": "^3.0.0"
2050
+ },
2051
+ "engines": {
2052
+ "node": ">=6"
2053
+ }
2054
+ },
2055
+ "node_modules/parse-json": {
2056
+ "version": "5.2.0",
2057
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
2058
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
2059
+ "license": "MIT",
2060
+ "dependencies": {
2061
+ "@babel/code-frame": "^7.0.0",
2062
+ "error-ex": "^1.3.1",
2063
+ "json-parse-even-better-errors": "^2.3.0",
2064
+ "lines-and-columns": "^1.1.6"
2065
+ },
2066
+ "engines": {
2067
+ "node": ">=8"
2068
+ },
2069
+ "funding": {
2070
+ "url": "https://github.com/sponsors/sindresorhus"
2071
+ }
2072
+ },
2073
+ "node_modules/path-parse": {
2074
+ "version": "1.0.7",
2075
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
2076
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
2077
+ "license": "MIT"
2078
+ },
2079
+ "node_modules/path-type": {
2080
+ "version": "4.0.0",
2081
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
2082
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
2083
+ "license": "MIT",
2084
+ "engines": {
2085
+ "node": ">=8"
2086
+ }
2087
+ },
2088
+ "node_modules/picocolors": {
2089
+ "version": "1.1.1",
2090
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
2091
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
2092
+ "license": "ISC"
2093
+ },
2094
+ "node_modules/postcss": {
2095
+ "version": "8.5.6",
2096
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
2097
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
2098
+ "dev": true,
2099
+ "funding": [
2100
+ {
2101
+ "type": "opencollective",
2102
+ "url": "https://opencollective.com/postcss/"
2103
+ },
2104
+ {
2105
+ "type": "tidelift",
2106
+ "url": "https://tidelift.com/funding/github/npm/postcss"
2107
+ },
2108
+ {
2109
+ "type": "github",
2110
+ "url": "https://github.com/sponsors/ai"
2111
+ }
2112
+ ],
2113
+ "license": "MIT",
2114
+ "dependencies": {
2115
+ "nanoid": "^3.3.11",
2116
+ "picocolors": "^1.1.1",
2117
+ "source-map-js": "^1.2.1"
2118
+ },
2119
+ "engines": {
2120
+ "node": "^10 || ^12 || >=14"
2121
+ }
2122
+ },
2123
+ "node_modules/prop-types": {
2124
+ "version": "15.8.1",
2125
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
2126
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
2127
+ "license": "MIT",
2128
+ "dependencies": {
2129
+ "loose-envify": "^1.4.0",
2130
+ "object-assign": "^4.1.1",
2131
+ "react-is": "^16.13.1"
2132
+ }
2133
+ },
2134
+ "node_modules/prop-types/node_modules/react-is": {
2135
+ "version": "16.13.1",
2136
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
2137
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
2138
+ "license": "MIT"
2139
+ },
2140
+ "node_modules/react": {
2141
+ "version": "18.3.1",
2142
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
2143
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
2144
+ "license": "MIT",
2145
+ "dependencies": {
2146
+ "loose-envify": "^1.1.0"
2147
+ },
2148
+ "engines": {
2149
+ "node": ">=0.10.0"
2150
+ }
2151
+ },
2152
+ "node_modules/react-dom": {
2153
+ "version": "18.3.1",
2154
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
2155
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
2156
+ "license": "MIT",
2157
+ "dependencies": {
2158
+ "loose-envify": "^1.1.0",
2159
+ "scheduler": "^0.23.2"
2160
+ },
2161
+ "peerDependencies": {
2162
+ "react": "^18.3.1"
2163
+ }
2164
+ },
2165
+ "node_modules/react-is": {
2166
+ "version": "19.2.4",
2167
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz",
2168
+ "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==",
2169
+ "license": "MIT"
2170
+ },
2171
+ "node_modules/react-refresh": {
2172
+ "version": "0.17.0",
2173
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
2174
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
2175
+ "dev": true,
2176
+ "license": "MIT",
2177
+ "engines": {
2178
+ "node": ">=0.10.0"
2179
+ }
2180
+ },
2181
+ "node_modules/react-router": {
2182
+ "version": "6.30.3",
2183
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
2184
+ "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
2185
+ "license": "MIT",
2186
+ "dependencies": {
2187
+ "@remix-run/router": "1.23.2"
2188
+ },
2189
+ "engines": {
2190
+ "node": ">=14.0.0"
2191
+ },
2192
+ "peerDependencies": {
2193
+ "react": ">=16.8"
2194
+ }
2195
+ },
2196
+ "node_modules/react-router-dom": {
2197
+ "version": "6.30.3",
2198
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
2199
+ "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
2200
+ "license": "MIT",
2201
+ "dependencies": {
2202
+ "@remix-run/router": "1.23.2",
2203
+ "react-router": "6.30.3"
2204
+ },
2205
+ "engines": {
2206
+ "node": ">=14.0.0"
2207
+ },
2208
+ "peerDependencies": {
2209
+ "react": ">=16.8",
2210
+ "react-dom": ">=16.8"
2211
+ }
2212
+ },
2213
+ "node_modules/react-transition-group": {
2214
+ "version": "4.4.5",
2215
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
2216
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
2217
+ "license": "BSD-3-Clause",
2218
+ "dependencies": {
2219
+ "@babel/runtime": "^7.5.5",
2220
+ "dom-helpers": "^5.0.1",
2221
+ "loose-envify": "^1.4.0",
2222
+ "prop-types": "^15.6.2"
2223
+ },
2224
+ "peerDependencies": {
2225
+ "react": ">=16.6.0",
2226
+ "react-dom": ">=16.6.0"
2227
+ }
2228
+ },
2229
+ "node_modules/resolve": {
2230
+ "version": "1.22.11",
2231
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
2232
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
2233
+ "license": "MIT",
2234
+ "dependencies": {
2235
+ "is-core-module": "^2.16.1",
2236
+ "path-parse": "^1.0.7",
2237
+ "supports-preserve-symlinks-flag": "^1.0.0"
2238
+ },
2239
+ "bin": {
2240
+ "resolve": "bin/resolve"
2241
+ },
2242
+ "engines": {
2243
+ "node": ">= 0.4"
2244
+ },
2245
+ "funding": {
2246
+ "url": "https://github.com/sponsors/ljharb"
2247
+ }
2248
+ },
2249
+ "node_modules/resolve-from": {
2250
+ "version": "4.0.0",
2251
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
2252
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
2253
+ "license": "MIT",
2254
+ "engines": {
2255
+ "node": ">=4"
2256
+ }
2257
+ },
2258
+ "node_modules/rollup": {
2259
+ "version": "4.59.0",
2260
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
2261
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
2262
+ "dev": true,
2263
+ "license": "MIT",
2264
+ "dependencies": {
2265
+ "@types/estree": "1.0.8"
2266
+ },
2267
+ "bin": {
2268
+ "rollup": "dist/bin/rollup"
2269
+ },
2270
+ "engines": {
2271
+ "node": ">=18.0.0",
2272
+ "npm": ">=8.0.0"
2273
+ },
2274
+ "optionalDependencies": {
2275
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
2276
+ "@rollup/rollup-android-arm64": "4.59.0",
2277
+ "@rollup/rollup-darwin-arm64": "4.59.0",
2278
+ "@rollup/rollup-darwin-x64": "4.59.0",
2279
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
2280
+ "@rollup/rollup-freebsd-x64": "4.59.0",
2281
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
2282
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
2283
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
2284
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
2285
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
2286
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
2287
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
2288
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
2289
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
2290
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
2291
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
2292
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
2293
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
2294
+ "@rollup/rollup-openbsd-x64": "4.59.0",
2295
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
2296
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
2297
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
2298
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
2299
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
2300
+ "fsevents": "~2.3.2"
2301
+ }
2302
+ },
2303
+ "node_modules/scheduler": {
2304
+ "version": "0.23.2",
2305
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
2306
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
2307
+ "license": "MIT",
2308
+ "dependencies": {
2309
+ "loose-envify": "^1.1.0"
2310
+ }
2311
+ },
2312
+ "node_modules/semver": {
2313
+ "version": "6.3.1",
2314
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
2315
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
2316
+ "dev": true,
2317
+ "license": "ISC",
2318
+ "bin": {
2319
+ "semver": "bin/semver.js"
2320
+ }
2321
+ },
2322
+ "node_modules/source-map": {
2323
+ "version": "0.5.7",
2324
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
2325
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
2326
+ "license": "BSD-3-Clause",
2327
+ "engines": {
2328
+ "node": ">=0.10.0"
2329
+ }
2330
+ },
2331
+ "node_modules/source-map-js": {
2332
+ "version": "1.2.1",
2333
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
2334
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
2335
+ "dev": true,
2336
+ "license": "BSD-3-Clause",
2337
+ "engines": {
2338
+ "node": ">=0.10.0"
2339
+ }
2340
+ },
2341
+ "node_modules/stylis": {
2342
+ "version": "4.2.0",
2343
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
2344
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
2345
+ "license": "MIT"
2346
+ },
2347
+ "node_modules/supports-preserve-symlinks-flag": {
2348
+ "version": "1.0.0",
2349
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
2350
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
2351
+ "license": "MIT",
2352
+ "engines": {
2353
+ "node": ">= 0.4"
2354
+ },
2355
+ "funding": {
2356
+ "url": "https://github.com/sponsors/ljharb"
2357
+ }
2358
+ },
2359
+ "node_modules/update-browserslist-db": {
2360
+ "version": "1.2.3",
2361
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
2362
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
2363
+ "dev": true,
2364
+ "funding": [
2365
+ {
2366
+ "type": "opencollective",
2367
+ "url": "https://opencollective.com/browserslist"
2368
+ },
2369
+ {
2370
+ "type": "tidelift",
2371
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
2372
+ },
2373
+ {
2374
+ "type": "github",
2375
+ "url": "https://github.com/sponsors/ai"
2376
+ }
2377
+ ],
2378
+ "license": "MIT",
2379
+ "dependencies": {
2380
+ "escalade": "^3.2.0",
2381
+ "picocolors": "^1.1.1"
2382
+ },
2383
+ "bin": {
2384
+ "update-browserslist-db": "cli.js"
2385
+ },
2386
+ "peerDependencies": {
2387
+ "browserslist": ">= 4.21.0"
2388
+ }
2389
+ },
2390
+ "node_modules/vite": {
2391
+ "version": "5.4.21",
2392
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
2393
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
2394
+ "dev": true,
2395
+ "license": "MIT",
2396
+ "dependencies": {
2397
+ "esbuild": "^0.21.3",
2398
+ "postcss": "^8.4.43",
2399
+ "rollup": "^4.20.0"
2400
+ },
2401
+ "bin": {
2402
+ "vite": "bin/vite.js"
2403
+ },
2404
+ "engines": {
2405
+ "node": "^18.0.0 || >=20.0.0"
2406
+ },
2407
+ "funding": {
2408
+ "url": "https://github.com/vitejs/vite?sponsor=1"
2409
+ },
2410
+ "optionalDependencies": {
2411
+ "fsevents": "~2.3.3"
2412
+ },
2413
+ "peerDependencies": {
2414
+ "@types/node": "^18.0.0 || >=20.0.0",
2415
+ "less": "*",
2416
+ "lightningcss": "^1.21.0",
2417
+ "sass": "*",
2418
+ "sass-embedded": "*",
2419
+ "stylus": "*",
2420
+ "sugarss": "*",
2421
+ "terser": "^5.4.0"
2422
+ },
2423
+ "peerDependenciesMeta": {
2424
+ "@types/node": {
2425
+ "optional": true
2426
+ },
2427
+ "less": {
2428
+ "optional": true
2429
+ },
2430
+ "lightningcss": {
2431
+ "optional": true
2432
+ },
2433
+ "sass": {
2434
+ "optional": true
2435
+ },
2436
+ "sass-embedded": {
2437
+ "optional": true
2438
+ },
2439
+ "stylus": {
2440
+ "optional": true
2441
+ },
2442
+ "sugarss": {
2443
+ "optional": true
2444
+ },
2445
+ "terser": {
2446
+ "optional": true
2447
+ }
2448
+ }
2449
+ },
2450
+ "node_modules/yallist": {
2451
+ "version": "3.1.1",
2452
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
2453
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
2454
+ "dev": true,
2455
+ "license": "ISC"
2456
+ },
2457
+ "node_modules/yaml": {
2458
+ "version": "1.10.2",
2459
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
2460
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
2461
+ "license": "ISC",
2462
+ "engines": {
2463
+ "node": ">= 6"
2464
+ }
2465
+ }
2466
+ }
2467
+ }
frontend/package.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "colorization-study",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@emotion/react": "^11.11.0",
13
+ "@emotion/styled": "^11.11.0",
14
+ "@mui/icons-material": "^5.15.0",
15
+ "@mui/material": "^5.15.0",
16
+ "react": "^18.2.0",
17
+ "react-dom": "^18.2.0",
18
+ "react-router-dom": "^6.21.0"
19
+ },
20
+ "devDependencies": {
21
+ "@vitejs/plugin-react": "^4.2.0",
22
+ "vite": "^5.0.0"
23
+ }
24
+ }
frontend/src/App.jsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Routes, Route, Navigate } from 'react-router-dom';
2
+ import Welcome from './pages/Welcome';
3
+ import Tutorial from './pages/Tutorial';
4
+ import Trial from './pages/Trial';
5
+ import Done from './pages/Done';
6
+
7
+ export default function App() {
8
+ return (
9
+ <Routes>
10
+ <Route path="/" element={<Welcome />} />
11
+ <Route path="/tutorial" element={<Tutorial />} />
12
+ <Route path="/trial" element={<Trial />} />
13
+ <Route path="/done" element={<Done />} />
14
+ <Route path="*" element={<Navigate to="/" replace />} />
15
+ </Routes>
16
+ );
17
+ }
frontend/src/components/ProgressBar.jsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { LinearProgress, Box } from '@mui/material';
2
+
3
+ export default function ProgressBar({ current, total }) {
4
+ const value = total > 0 ? (current / total) * 100 : 0;
5
+
6
+ return (
7
+ <Box sx={{ width: '100%' }}>
8
+ <LinearProgress
9
+ variant="determinate"
10
+ value={value}
11
+ sx={{
12
+ height: 4,
13
+ borderRadius: 2,
14
+ backgroundColor: 'rgba(124, 77, 255, 0.15)',
15
+ '& .MuiLinearProgress-bar': {
16
+ borderRadius: 2,
17
+ background: 'linear-gradient(90deg, #7C4DFF 0%, #B47CFF 100%)',
18
+ },
19
+ }}
20
+ />
21
+ </Box>
22
+ );
23
+ }
frontend/src/components/SwipeCard.jsx ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useRef, useCallback, useState } from 'react';
2
+ import { Box, Typography } from '@mui/material';
3
+
4
+ const THRESHOLD = 80; // px to trigger decision
5
+ const MAX_ROTATE = 12; // degrees at full drag
6
+ const MAX_DRAG = 180; // px reference for scale calculations
7
+
8
+ export default function SwipeCard({ children, onSwipeLeft, onSwipeRight }) {
9
+ const touchStart = useRef(null);
10
+ const [dx, setDx] = useState(0);
11
+ const [exiting, setExiting] = useState(null); // 'left' | 'right' | null
12
+
13
+ const handleTouchStart = useCallback((e) => {
14
+ touchStart.current = e.touches[0].clientX;
15
+ setExiting(null);
16
+ }, []);
17
+
18
+ const handleTouchMove = useCallback((e) => {
19
+ if (touchStart.current === null) return;
20
+ const delta = e.touches[0].clientX - touchStart.current;
21
+ setDx(delta);
22
+ }, []);
23
+
24
+ const handleTouchEnd = useCallback(() => {
25
+ if (touchStart.current === null) return;
26
+ touchStart.current = null;
27
+
28
+ if (dx <= -THRESHOLD) {
29
+ setExiting('left');
30
+ setTimeout(() => {
31
+ setDx(0);
32
+ setExiting(null);
33
+ onSwipeLeft?.();
34
+ }, 220);
35
+ } else if (dx >= THRESHOLD) {
36
+ setExiting('right');
37
+ setTimeout(() => {
38
+ setDx(0);
39
+ setExiting(null);
40
+ onSwipeRight?.();
41
+ }, 220);
42
+ } else {
43
+ // snap back
44
+ setDx(0);
45
+ }
46
+ }, [dx, onSwipeLeft, onSwipeRight]);
47
+
48
+ const progress = Math.min(Math.abs(dx) / THRESHOLD, 1);
49
+ const rotate = (dx / MAX_DRAG) * MAX_ROTATE;
50
+ const translateX = exiting === 'left'
51
+ ? -window.innerWidth
52
+ : exiting === 'right'
53
+ ? window.innerWidth
54
+ : dx;
55
+
56
+ const isLeft = dx < -10;
57
+ const isRight = dx > 10;
58
+ const overlayOpacity = Math.min(Math.abs(dx) / THRESHOLD, 0.7);
59
+
60
+ return (
61
+ <Box
62
+ onTouchStart={handleTouchStart}
63
+ onTouchMove={handleTouchMove}
64
+ onTouchEnd={handleTouchEnd}
65
+ sx={{
66
+ flex: 1,
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ position: 'relative',
71
+ overflow: 'hidden',
72
+ touchAction: 'none',
73
+ userSelect: 'none',
74
+ cursor: 'grab',
75
+ '&:active': { cursor: 'grabbing' },
76
+ }}
77
+ >
78
+ {/* Draggable card */}
79
+ <Box
80
+ sx={{
81
+ width: '100%',
82
+ height: '100%',
83
+ display: 'flex',
84
+ alignItems: 'center',
85
+ justifyContent: 'center',
86
+ position: 'relative',
87
+ transform: `translateX(${translateX}px) rotate(${rotate}deg)`,
88
+ transition: exiting
89
+ ? 'transform 0.22s ease-out'
90
+ : dx === 0
91
+ ? 'transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275)'
92
+ : 'none',
93
+ }}
94
+ >
95
+ {children}
96
+
97
+ {/* ABSENT overlay — left swipe — red */}
98
+ <Box
99
+ sx={{
100
+ position: 'absolute',
101
+ top: 0, left: 0, right: 0, bottom: 0,
102
+ borderRadius: 2,
103
+ bgcolor: 'error.main',
104
+ opacity: isLeft ? overlayOpacity * 0.35 : 0,
105
+ transition: 'opacity 0.1s',
106
+ pointerEvents: 'none',
107
+ }}
108
+ />
109
+ <Box
110
+ sx={{
111
+ position: 'absolute',
112
+ top: '50%',
113
+ left: 24,
114
+ transform: 'translateY(-50%) rotate(-15deg)',
115
+ opacity: isLeft ? Math.min(progress * 1.4, 1) : 0,
116
+ transition: 'opacity 0.1s',
117
+ pointerEvents: 'none',
118
+ border: '3px solid',
119
+ borderColor: 'error.main',
120
+ borderRadius: 1,
121
+ px: 1.5,
122
+ py: 0.5,
123
+ }}
124
+ >
125
+ <Typography
126
+ variant="h6"
127
+ fontWeight={900}
128
+ color="error.main"
129
+ sx={{ letterSpacing: 2 }}
130
+ >
131
+ ABSENT
132
+ </Typography>
133
+ </Box>
134
+
135
+ {/* PRESENT overlay — right swipe — green */}
136
+ <Box
137
+ sx={{
138
+ position: 'absolute',
139
+ top: 0, left: 0, right: 0, bottom: 0,
140
+ borderRadius: 2,
141
+ bgcolor: 'success.main',
142
+ opacity: isRight ? overlayOpacity * 0.35 : 0,
143
+ transition: 'opacity 0.1s',
144
+ pointerEvents: 'none',
145
+ }}
146
+ />
147
+ <Box
148
+ sx={{
149
+ position: 'absolute',
150
+ top: '50%',
151
+ right: 24,
152
+ transform: 'translateY(-50%) rotate(15deg)',
153
+ opacity: isRight ? Math.min(progress * 1.4, 1) : 0,
154
+ transition: 'opacity 0.1s',
155
+ pointerEvents: 'none',
156
+ border: '3px solid',
157
+ borderColor: 'success.main',
158
+ borderRadius: 1,
159
+ px: 1.5,
160
+ py: 0.5,
161
+ }}
162
+ >
163
+ <Typography
164
+ variant="h6"
165
+ fontWeight={900}
166
+ color="success.main"
167
+ sx={{ letterSpacing: 2 }}
168
+ >
169
+ PRESENT
170
+ </Typography>
171
+ </Box>
172
+ </Box>
173
+ </Box>
174
+ );
175
+ }
frontend/src/main.jsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { ThemeProvider, CssBaseline } from '@mui/material';
4
+ import { BrowserRouter } from 'react-router-dom';
5
+ import theme from './theme';
6
+ import App from './App';
7
+
8
+ ReactDOM.createRoot(document.getElementById('root')).render(
9
+ <React.StrictMode>
10
+ <ThemeProvider theme={theme}>
11
+ <CssBaseline />
12
+ <BrowserRouter>
13
+ <App />
14
+ </BrowserRouter>
15
+ </ThemeProvider>
16
+ </React.StrictMode>
17
+ );
frontend/src/pages/Done.jsx ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useState } from 'react';
2
+ import { Box, Button, Card, CardContent, Snackbar, Typography } from '@mui/material';
3
+ import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
4
+ import ContentCopyIcon from '@mui/icons-material/ContentCopy';
5
+ import ShareIcon from '@mui/icons-material/Share';
6
+
7
+ // ← Replace this with the deployed Fly.io URL before sharing
8
+ const STUDY_URL = 'https://stvident-colorization.hf.space';
9
+
10
+ export default function Done() {
11
+ const [copied, setCopied] = useState(false);
12
+
13
+ useEffect(() => {
14
+ localStorage.removeItem('session_id');
15
+ localStorage.removeItem('trials');
16
+ localStorage.removeItem('current_index');
17
+ }, []);
18
+
19
+ const handleCopy = async () => {
20
+ try {
21
+ await navigator.clipboard.writeText(STUDY_URL);
22
+ setCopied(true);
23
+ } catch {
24
+ // Fallback for older browsers
25
+ const el = document.createElement('textarea');
26
+ el.value = STUDY_URL;
27
+ document.body.appendChild(el);
28
+ el.select();
29
+ document.execCommand('copy');
30
+ document.body.removeChild(el);
31
+ setCopied(true);
32
+ }
33
+ };
34
+
35
+ const handleShare = async () => {
36
+ if (navigator.share) {
37
+ await navigator.share({
38
+ title: 'Color Turing Test',
39
+ text: 'Can you spot AI colorization? Take the test!',
40
+ url: STUDY_URL,
41
+ }).catch(() => {});
42
+ } else {
43
+ handleCopy();
44
+ }
45
+ };
46
+
47
+ return (
48
+ <Box
49
+ sx={{
50
+ minHeight: '100vh',
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ p: 2,
55
+ }}
56
+ >
57
+ <Card sx={{ maxWidth: 480, width: '100%' }}>
58
+ <CardContent
59
+ sx={{
60
+ p: 4,
61
+ display: 'flex',
62
+ flexDirection: 'column',
63
+ alignItems: 'center',
64
+ gap: 2,
65
+ }}
66
+ >
67
+ <CheckCircleOutlineIcon sx={{ fontSize: 80, color: 'success.main' }} />
68
+
69
+ <Typography variant="h4" textAlign="center" fontWeight={700}>
70
+ All done! Thank you.
71
+ </Typography>
72
+
73
+ <Typography variant="body1" color="text.secondary" textAlign="center">
74
+ Your responses have been recorded. You completed 50 trials.
75
+ </Typography>
76
+
77
+ <Typography variant="body2" color="text.secondary" textAlign="center" sx={{ mt: 1, opacity: 0.7 }}>
78
+ This study examines how well AI colorization techniques fool human perception.
79
+ </Typography>
80
+
81
+ {/* Share section */}
82
+ <Box
83
+ sx={{
84
+ mt: 2,
85
+ width: '100%',
86
+ borderTop: 1,
87
+ borderColor: 'divider',
88
+ pt: 3,
89
+ display: 'flex',
90
+ flexDirection: 'column',
91
+ alignItems: 'center',
92
+ gap: 1.5,
93
+ }}
94
+ >
95
+ <Typography variant="body2" color="text.secondary" textAlign="center">
96
+ Know someone who'd like to try? Share the study:
97
+ </Typography>
98
+
99
+ <Box
100
+ sx={{
101
+ display: 'flex',
102
+ alignItems: 'center',
103
+ gap: 1,
104
+ px: 2,
105
+ py: 1,
106
+ bgcolor: 'grey.900',
107
+ borderRadius: 1,
108
+ border: 1,
109
+ borderColor: 'divider',
110
+ width: '100%',
111
+ cursor: 'pointer',
112
+ '&:hover': { borderColor: 'primary.main' },
113
+ }}
114
+ onClick={handleCopy}
115
+ >
116
+ <Typography
117
+ variant="caption"
118
+ color="text.secondary"
119
+ sx={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
120
+ >
121
+ {STUDY_URL}
122
+ </Typography>
123
+ <ContentCopyIcon sx={{ fontSize: 16, color: 'text.secondary', flexShrink: 0 }} />
124
+ </Box>
125
+
126
+ <Box sx={{ display: 'flex', gap: 1.5, width: '100%' }}>
127
+ <Button
128
+ variant="outlined"
129
+ startIcon={<ContentCopyIcon />}
130
+ onClick={handleCopy}
131
+ sx={{ flex: 1 }}
132
+ >
133
+ Copy link
134
+ </Button>
135
+ <Button
136
+ variant="contained"
137
+ startIcon={<ShareIcon />}
138
+ onClick={handleShare}
139
+ sx={{ flex: 1 }}
140
+ >
141
+ Share
142
+ </Button>
143
+ </Box>
144
+ </Box>
145
+ </CardContent>
146
+ </Card>
147
+
148
+ <Snackbar
149
+ open={copied}
150
+ autoHideDuration={2500}
151
+ onClose={() => setCopied(false)}
152
+ message="Link copied to clipboard"
153
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
154
+ />
155
+ </Box>
156
+ );
157
+ }
frontend/src/pages/Trial.jsx ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { Box, Button, Typography, useMediaQuery, useTheme } from '@mui/material';
4
+ import ProgressBar from '../components/ProgressBar';
5
+ import SwipeCard from '../components/SwipeCard';
6
+
7
+ export default function Trial() {
8
+ const navigate = useNavigate();
9
+ const theme = useTheme();
10
+ const isDesktop = useMediaQuery(theme.breakpoints.up('md'));
11
+
12
+ const [trials, setTrials] = useState([]);
13
+ const [currentIndex, setCurrentIndex] = useState(0);
14
+ const [sessionId, setSessionId] = useState('');
15
+ const [imageLoaded, setImageLoaded] = useState(false);
16
+ const imageShownAt = useRef(Date.now());
17
+
18
+ // Load session from localStorage
19
+ useEffect(() => {
20
+ const sid = localStorage.getItem('session_id');
21
+ const trialsJson = localStorage.getItem('trials');
22
+ const idx = parseInt(localStorage.getItem('current_index') || '0', 10);
23
+
24
+ if (!sid || !trialsJson) {
25
+ navigate('/');
26
+ return;
27
+ }
28
+
29
+ const parsedTrials = JSON.parse(trialsJson);
30
+ setSessionId(sid);
31
+ setTrials(parsedTrials);
32
+ setCurrentIndex(idx);
33
+ }, [navigate]);
34
+
35
+ // Reset timer when trial changes
36
+ useEffect(() => {
37
+ imageShownAt.current = Date.now();
38
+ setImageLoaded(false);
39
+ }, [currentIndex]);
40
+
41
+ // Preload next 2 images
42
+ useEffect(() => {
43
+ if (trials.length === 0) return;
44
+ for (let offset = 1; offset <= 2; offset++) {
45
+ const nextIdx = currentIndex + offset;
46
+ if (nextIdx < trials.length) {
47
+ const img = new Image();
48
+ img.src = `/images/${trials[nextIdx].path}`;
49
+ }
50
+ }
51
+ }, [currentIndex, trials]);
52
+
53
+ const handleResponse = useCallback((response) => {
54
+ if (trials.length === 0 || currentIndex >= trials.length) return;
55
+
56
+ const trial = trials[currentIndex];
57
+ const responseTimeMs = Date.now() - imageShownAt.current;
58
+
59
+ // Fire and forget
60
+ fetch('/api/session/respond', {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({
64
+ session_id: sessionId,
65
+ trial_index: currentIndex,
66
+ image_id: trial.id,
67
+ label: trial.label,
68
+ response,
69
+ response_time_ms: responseTimeMs,
70
+ }),
71
+ }).catch(console.error);
72
+
73
+ const nextIndex = currentIndex + 1;
74
+
75
+ if (nextIndex >= trials.length) {
76
+ // Complete session
77
+ fetch('/api/session/complete', {
78
+ method: 'POST',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify({ session_id: sessionId }),
81
+ }).catch(console.error);
82
+
83
+ localStorage.setItem('current_index', String(nextIndex));
84
+ navigate('/done');
85
+ } else {
86
+ setCurrentIndex(nextIndex);
87
+ localStorage.setItem('current_index', String(nextIndex));
88
+ }
89
+ }, [trials, currentIndex, sessionId, navigate]);
90
+
91
+ // Keyboard shortcuts
92
+ useEffect(() => {
93
+ const handleKey = (e) => {
94
+ if (e.key === 'ArrowLeft') handleResponse('real');
95
+ else if (e.key === 'ArrowRight') handleResponse('fake');
96
+ };
97
+ window.addEventListener('keydown', handleKey);
98
+ return () => window.removeEventListener('keydown', handleKey);
99
+ }, [handleResponse]);
100
+
101
+ if (trials.length === 0) return null;
102
+ if (currentIndex >= trials.length) return null;
103
+
104
+ const currentTrial = trials[currentIndex];
105
+
106
+ return (
107
+ <Box
108
+ sx={{
109
+ height: '100vh',
110
+ display: 'flex',
111
+ flexDirection: 'column',
112
+ overflow: 'hidden',
113
+ }}
114
+ >
115
+ {/* Top bar */}
116
+ <Box sx={{ px: 2, pt: 1 }}>
117
+ <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 0.5 }}>
118
+ <Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
119
+ Color Turing Test
120
+ </Typography>
121
+ <Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
122
+ {currentIndex + 1} / {trials.length}
123
+ </Typography>
124
+ </Box>
125
+ <ProgressBar current={currentIndex + 1} total={trials.length} />
126
+ </Box>
127
+
128
+ {/* Main image area */}
129
+ <SwipeCard
130
+ onSwipeLeft={() => handleResponse('real')}
131
+ onSwipeRight={() => handleResponse('fake')}
132
+ >
133
+ <Box
134
+ sx={{
135
+ flex: 1,
136
+ display: 'flex',
137
+ alignItems: 'center',
138
+ justifyContent: 'center',
139
+ p: 2,
140
+ minHeight: 0,
141
+ }}
142
+ >
143
+ {/* Fixed-size container — all images render at the same visual footprint */}
144
+ <Box
145
+ sx={{
146
+ width: { xs: 'min(90vw, 90vh - 200px)', md: 'min(60vw, calc(100vh - 240px))' },
147
+ height: { xs: 'min(90vw, 90vh - 200px)', md: 'min(60vw, calc(100vh - 240px))' },
148
+ display: 'flex',
149
+ alignItems: 'center',
150
+ justifyContent: 'center',
151
+ borderRadius: 2,
152
+ overflow: 'hidden',
153
+ bgcolor: 'grey.900',
154
+ }}
155
+ >
156
+ <Box
157
+ component="img"
158
+ key={currentTrial.path}
159
+ src={`/images/${currentTrial.path}`}
160
+ alt="Test image"
161
+ onLoad={() => {
162
+ setImageLoaded(true);
163
+ imageShownAt.current = Date.now();
164
+ }}
165
+ sx={{
166
+ width: '100%',
167
+ height: '100%',
168
+ objectFit: 'contain',
169
+ opacity: imageLoaded ? 1 : 0,
170
+ transition: 'opacity 0.15s ease-in',
171
+ }}
172
+ />
173
+ </Box>
174
+ </Box>
175
+ </SwipeCard>
176
+
177
+ {/* Bottom area */}
178
+ <Box sx={{ p: 2, pb: 3 }}>
179
+ {/* Mobile: swipe hint + smaller tap buttons as fallback */}
180
+ {!isDesktop && (
181
+ <Typography
182
+ variant="caption"
183
+ color="text.secondary"
184
+ textAlign="center"
185
+ display="block"
186
+ sx={{ mb: 1.5, opacity: 0.5, fontSize: '0.72rem', letterSpacing: 1 }}
187
+ >
188
+ absent &#8592; swipe &#8594; present
189
+ </Typography>
190
+ )}
191
+
192
+ <Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
193
+ <Button
194
+ variant="outlined"
195
+ color="error"
196
+ size={isDesktop ? 'large' : 'medium'}
197
+ onClick={() => handleResponse('real')}
198
+ sx={{
199
+ flex: 1,
200
+ maxWidth: 240,
201
+ fontSize: isDesktop ? '1.1rem' : '0.95rem',
202
+ py: isDesktop ? 1.5 : 1,
203
+ borderWidth: 2,
204
+ '&:hover': { borderWidth: 2 },
205
+ }}
206
+ >
207
+ &#x2717; ABSENT
208
+ </Button>
209
+ <Button
210
+ variant="outlined"
211
+ color="success"
212
+ size={isDesktop ? 'large' : 'medium'}
213
+ onClick={() => handleResponse('fake')}
214
+ sx={{
215
+ flex: 1,
216
+ maxWidth: 240,
217
+ fontSize: isDesktop ? '1.1rem' : '0.95rem',
218
+ py: isDesktop ? 1.5 : 1,
219
+ borderWidth: 2,
220
+ '&:hover': { borderWidth: 2 },
221
+ }}
222
+ >
223
+ &#x2713; PRESENT
224
+ </Button>
225
+ </Box>
226
+
227
+ {/* Desktop: keyboard hint */}
228
+ {isDesktop && (
229
+ <Typography
230
+ variant="caption"
231
+ color="text.secondary"
232
+ textAlign="center"
233
+ display="block"
234
+ sx={{ mt: 1.5, opacity: 0.5, fontSize: '0.7rem' }}
235
+ >
236
+ &larr; Absent &nbsp;&nbsp;&nbsp;&nbsp; Present &rarr;
237
+ </Typography>
238
+ )}
239
+ </Box>
240
+ </Box>
241
+ );
242
+ }
frontend/src/pages/Tutorial.jsx ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import {
4
+ Box, Typography, Button, Card, CardContent, Chip, Fade
5
+ } from '@mui/material';
6
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
7
+ import CancelIcon from '@mui/icons-material/Cancel';
8
+
9
+ const ARTIFACT_CHIPS = [
10
+ 'Grayish', 'Color bleeding', 'Inconsistency',
11
+ 'Color artifacts', 'Incomplete', 'Wrong hue'
12
+ ];
13
+
14
+ function StepDots({ current, total }) {
15
+ return (
16
+ <Box sx={{ display: 'flex', gap: 1, justifyContent: 'center', mb: 3 }}>
17
+ {Array.from({ length: total }, (_, i) => (
18
+ <Box
19
+ key={i}
20
+ sx={{
21
+ width: 10,
22
+ height: 10,
23
+ borderRadius: '50%',
24
+ bgcolor: i === current ? 'primary.main' : 'rgba(255,255,255,0.2)',
25
+ transition: 'background-color 0.3s',
26
+ }}
27
+ />
28
+ ))}
29
+ </Box>
30
+ );
31
+ }
32
+
33
+ function AnswerButtons({ onAnswer, disabled }) {
34
+ return (
35
+ <Box sx={{ display: 'flex', gap: 2, justifyContent: 'center', mt: 2 }}>
36
+ <Button
37
+ variant="outlined"
38
+ color="error"
39
+ size="large"
40
+ disabled={disabled}
41
+ onClick={() => onAnswer('real')}
42
+ sx={{
43
+ flex: 1,
44
+ maxWidth: 220,
45
+ fontSize: '1rem',
46
+ borderWidth: 2,
47
+ '&:hover': { borderWidth: 2 },
48
+ }}
49
+ >
50
+ &#x2717; ABSENT
51
+ </Button>
52
+ <Button
53
+ variant="outlined"
54
+ color="success"
55
+ size="large"
56
+ disabled={disabled}
57
+ onClick={() => onAnswer('fake')}
58
+ sx={{
59
+ flex: 1,
60
+ maxWidth: 220,
61
+ fontSize: '1rem',
62
+ borderWidth: 2,
63
+ '&:hover': { borderWidth: 2 },
64
+ }}
65
+ >
66
+ &#x2713; PRESENT
67
+ </Button>
68
+ </Box>
69
+ );
70
+ }
71
+
72
+ function FeedbackPanel({ correct, isReal, children }) {
73
+ return (
74
+ <Fade in timeout={300}>
75
+ <Card
76
+ sx={{
77
+ mt: 2,
78
+ bgcolor: correct ? 'rgba(76, 175, 80, 0.1)' : 'rgba(244, 67, 54, 0.1)',
79
+ border: 1,
80
+ borderColor: correct ? 'success.main' : 'error.main',
81
+ }}
82
+ >
83
+ <CardContent sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.5, py: 2 }}>
84
+ {correct
85
+ ? <CheckCircleIcon color="success" />
86
+ : <CancelIcon color="error" />
87
+ }
88
+ <Box>
89
+ <Typography variant="body1" fontWeight={600}>
90
+ {correct ? 'Correct!' : 'Not quite!'}
91
+ </Typography>
92
+ <Typography variant="body2" color="text.secondary">
93
+ {children}
94
+ </Typography>
95
+ </Box>
96
+ </CardContent>
97
+ </Card>
98
+ </Fade>
99
+ );
100
+ }
101
+
102
+ export default function Tutorial() {
103
+ const navigate = useNavigate();
104
+ const [step, setStep] = useState(0);
105
+ const [practiceAnswer, setPracticeAnswer] = useState(null);
106
+
107
+ // Step 1: Reference image
108
+ if (step === 0) {
109
+ return (
110
+ <Box sx={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', p: 2, pt: 4 }}>
111
+ <StepDots current={0} total={3} />
112
+ <Typography variant="h5" mb={2} textAlign="center">
113
+ Common Colorization Artifacts
114
+ </Typography>
115
+
116
+ <Box
117
+ component="img"
118
+ src="/tutorial/imageArtifacts.png"
119
+ alt="Colorization artifact types"
120
+ sx={{
121
+ width: '100%',
122
+ maxWidth: 700,
123
+ borderRadius: 2,
124
+ mb: 2,
125
+ }}
126
+ />
127
+
128
+ <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center', mb: 2 }}>
129
+ {ARTIFACT_CHIPS.map((label) => (
130
+ <Chip
131
+ key={label}
132
+ label={label}
133
+ variant="outlined"
134
+ sx={{ borderColor: 'primary.main', color: 'primary.main' }}
135
+ />
136
+ ))}
137
+ </Box>
138
+
139
+ <Typography variant="body1" color="text.secondary" textAlign="center" maxWidth={600} mb={3}>
140
+ You'll see 50 images. Some may have colorization artifacts, some won't.
141
+ Your job: decide whether artifacts are present or absent.
142
+ </Typography>
143
+
144
+ <Button variant="contained" onClick={() => setStep(1)}>
145
+ Got it &rarr;
146
+ </Button>
147
+ </Box>
148
+ );
149
+ }
150
+
151
+ // Step 2: Practice REAL
152
+ if (step === 1) {
153
+ const isCorrect = practiceAnswer === 'real';
154
+ return (
155
+ <Box sx={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', p: 2, pt: 4 }}>
156
+ <StepDots current={1} total={3} />
157
+ <Typography variant="h5" mb={2} textAlign="center">
158
+ Are colorization artifacts present or absent?
159
+ </Typography>
160
+
161
+ <Box
162
+ component="img"
163
+ src="/images/gt/standard/coco/000000000019.jpg"
164
+ alt="Practice - real"
165
+ sx={{
166
+ width: '100%',
167
+ maxWidth: 600,
168
+ maxHeight: '50vh',
169
+ objectFit: 'contain',
170
+ borderRadius: 2,
171
+ mb: 2,
172
+ bgcolor: '#000',
173
+ }}
174
+ />
175
+
176
+ {!practiceAnswer && (
177
+ <AnswerButtons onAnswer={(ans) => setPracticeAnswer(ans)} disabled={false} />
178
+ )}
179
+
180
+ {practiceAnswer && (
181
+ <>
182
+ <FeedbackPanel correct={isCorrect} isReal>
183
+ {isCorrect
184
+ ? 'Correct — no artifacts. Natural color gradients and consistent shadows throughout.'
185
+ : 'No artifacts here — natural color gradients and accurate shadows throughout.'}
186
+ </FeedbackPanel>
187
+ <Button
188
+ variant="contained"
189
+ sx={{ mt: 2 }}
190
+ onClick={() => { setPracticeAnswer(null); setStep(2); }}
191
+ >
192
+ Next &rarr;
193
+ </Button>
194
+ </>
195
+ )}
196
+ </Box>
197
+ );
198
+ }
199
+
200
+ // Step 3: Practice FAKE
201
+ if (step === 2) {
202
+ const isCorrect = practiceAnswer === 'fake';
203
+ return (
204
+ <Box sx={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', p: 2, pt: 4 }}>
205
+ <StepDots current={2} total={3} />
206
+ <Typography variant="h5" mb={2} textAlign="center">
207
+ Are colorization artifacts present or absent?
208
+ </Typography>
209
+
210
+ <Box
211
+ component="img"
212
+ src="/images/bigcolor/standard/coco/000000000001_c468.jpg"
213
+ alt="Practice - fake"
214
+ sx={{
215
+ width: '100%',
216
+ maxWidth: 600,
217
+ maxHeight: '50vh',
218
+ objectFit: 'contain',
219
+ borderRadius: 2,
220
+ mb: 2,
221
+ bgcolor: '#000',
222
+ }}
223
+ />
224
+
225
+ {!practiceAnswer && (
226
+ <AnswerButtons onAnswer={(ans) => setPracticeAnswer(ans)} disabled={false} />
227
+ )}
228
+
229
+ {practiceAnswer && (
230
+ <>
231
+ <FeedbackPanel correct={isCorrect} isReal={false}>
232
+ {isCorrect
233
+ ? 'Artifacts present — color bleeding across object edges is the giveaway.'
234
+ : 'Artifacts are present here — look for unnatural color spilling across edges.'}
235
+ </FeedbackPanel>
236
+
237
+ <Card sx={{ mt: 2, p: 1 }}>
238
+ <Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 0.5, px: 1 }}>
239
+ Artifact type: Color bleeding
240
+ </Typography>
241
+ <Box
242
+ component="img"
243
+ src="/tutorial/imageArtifacts.png"
244
+ alt="Artifact reference"
245
+ sx={{ width: '100%', maxWidth: 300, borderRadius: 1 }}
246
+ />
247
+ </Card>
248
+
249
+ <Button
250
+ variant="contained"
251
+ sx={{ mt: 2, mb: 4 }}
252
+ onClick={() => navigate('/trial')}
253
+ >
254
+ Start the test (50 images) &rarr;
255
+ </Button>
256
+ </>
257
+ )}
258
+ </Box>
259
+ );
260
+ }
261
+
262
+ return null;
263
+ }
frontend/src/pages/Welcome.jsx ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import {
4
+ Box, Card, CardContent, Typography, Button, FormControl,
5
+ InputLabel, Select, MenuItem, CircularProgress, Fade
6
+ } from '@mui/material';
7
+
8
+ const PLATES = [
9
+ {
10
+ src: '/tutorial/colorblind/plate_demo.png',
11
+ question: 'What number do you see?',
12
+ options: ['12', '4', '10', "I can't tell"],
13
+ correctAnswer: '12',
14
+ },
15
+ {
16
+ src: '/tutorial/colorblind/plate_redgreen.png',
17
+ question: 'What number do you see?',
18
+ options: ['74', '21', "I can't tell", 'Something else'],
19
+ correctAnswer: '74',
20
+ field: 'cb_redgreen',
21
+ },
22
+ {
23
+ src: '/tutorial/colorblind/plate_blueyellow.png',
24
+ question: 'What number do you see?',
25
+ options: ['6', "I can't tell", '8', 'I see nothing'],
26
+ correctAnswer: '6',
27
+ field: 'cb_blueyellow',
28
+ },
29
+ ];
30
+
31
+ function classifyAnswer(plate, answer) {
32
+ if (answer === plate.correctAnswer) return 'normal';
33
+ if (answer === "I can't tell" || answer === 'I see nothing' || answer === 'Something else')
34
+ return 'unsure';
35
+ return 'deficient';
36
+ }
37
+
38
+ function detectDevice() {
39
+ const ua = navigator.userAgent;
40
+ if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet';
41
+ if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile';
42
+ return 'desktop';
43
+ }
44
+
45
+ export default function Welcome() {
46
+ const navigate = useNavigate();
47
+ const [plateStep, setPlateStep] = useState(0);
48
+ const [plateAnswers, setPlateAnswers] = useState(['', '', '']);
49
+ const [expertise, setExpertise] = useState('');
50
+ const [loading, setLoading] = useState(false);
51
+
52
+ const allPlatesDone = plateStep >= PLATES.length;
53
+
54
+ const handlePlateAnswer = (answer) => {
55
+ const updated = [...plateAnswers];
56
+ updated[plateStep] = answer;
57
+ setPlateAnswers(updated);
58
+ };
59
+
60
+ const handlePlateNext = () => {
61
+ setPlateStep((s) => s + 1);
62
+ };
63
+
64
+ const handleStart = async () => {
65
+ if (!allPlatesDone || !expertise) return;
66
+ setLoading(true);
67
+
68
+ // Classify colorblindness results
69
+ const cbRedgreen = classifyAnswer(PLATES[1], plateAnswers[1]);
70
+ const cbBlueyellow = classifyAnswer(PLATES[2], plateAnswers[2]);
71
+ const colorblind =
72
+ cbRedgreen !== 'normal' || cbBlueyellow !== 'normal' ? 1 : 0;
73
+ const device = detectDevice();
74
+
75
+ try {
76
+ const res = await fetch('/api/session/start', {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json' },
79
+ body: JSON.stringify({
80
+ expertise,
81
+ colorblind,
82
+ device,
83
+ cb_redgreen: cbRedgreen,
84
+ cb_blueyellow: cbBlueyellow,
85
+ }),
86
+ });
87
+ const data = await res.json();
88
+
89
+ localStorage.setItem('session_id', data.session_id);
90
+ localStorage.setItem('trials', JSON.stringify(data.trials));
91
+ localStorage.setItem('current_index', '0');
92
+
93
+ navigate('/tutorial');
94
+ } catch (err) {
95
+ console.error('Failed to start session:', err);
96
+ setLoading(false);
97
+ }
98
+ };
99
+
100
+ const currentPlate = !allPlatesDone ? PLATES[plateStep] : null;
101
+ const currentAnswer = !allPlatesDone ? plateAnswers[plateStep] : '';
102
+
103
+ return (
104
+ <Box
105
+ sx={{
106
+ minHeight: '100vh',
107
+ display: 'flex',
108
+ alignItems: 'center',
109
+ justifyContent: 'center',
110
+ p: 2,
111
+ }}
112
+ >
113
+ <Card sx={{ maxWidth: 520, width: '100%' }}>
114
+ <CardContent
115
+ sx={{
116
+ p: 4,
117
+ display: 'flex',
118
+ flexDirection: 'column',
119
+ alignItems: 'center',
120
+ gap: 3,
121
+ }}
122
+ >
123
+ <Typography
124
+ variant="h3"
125
+ textAlign="center"
126
+ sx={{ fontSize: { xs: '1.8rem', sm: '2.4rem' } }}
127
+ >
128
+ Can you spot colorization artifacts?
129
+ </Typography>
130
+ <Typography variant="body1" color="text.secondary" textAlign="center">
131
+ Look at 50 images and decide: are colorization artifacts present or absent?
132
+ Takes about 5 minutes.
133
+ </Typography>
134
+
135
+ {/* Colorblindness plates — sequential */}
136
+ {!allPlatesDone && currentPlate && (
137
+ <Fade in key={plateStep} timeout={300}>
138
+ <Box
139
+ sx={{
140
+ display: 'flex',
141
+ flexDirection: 'column',
142
+ alignItems: 'center',
143
+ gap: 1.5,
144
+ width: '100%',
145
+ }}
146
+ >
147
+ <Typography
148
+ variant="caption"
149
+ color="text.secondary"
150
+ sx={{ opacity: 0.6 }}
151
+ >
152
+ Vision check {plateStep + 1} of {PLATES.length}
153
+ </Typography>
154
+
155
+ <Box
156
+ component="img"
157
+ src={currentPlate.src}
158
+ alt="Color vision test plate"
159
+ sx={{
160
+ width: 300,
161
+ height: 300,
162
+ objectFit: 'contain',
163
+ borderRadius: '50%',
164
+ }}
165
+ />
166
+
167
+ <Typography variant="body2" color="text.secondary">
168
+ {currentPlate.question}
169
+ </Typography>
170
+
171
+ <Box
172
+ sx={{
173
+ display: 'flex',
174
+ gap: 1,
175
+ flexWrap: 'wrap',
176
+ justifyContent: 'center',
177
+ }}
178
+ >
179
+ {currentPlate.options.map((opt) => (
180
+ <Button
181
+ key={opt}
182
+ variant={currentAnswer === opt ? 'contained' : 'outlined'}
183
+ size="small"
184
+ onClick={() => handlePlateAnswer(opt)}
185
+ sx={{ minWidth: 80 }}
186
+ >
187
+ {opt}
188
+ </Button>
189
+ ))}
190
+ </Box>
191
+
192
+ <Button
193
+ variant="contained"
194
+ size="small"
195
+ disabled={!currentAnswer}
196
+ onClick={handlePlateNext}
197
+ sx={{ mt: 0.5 }}
198
+ >
199
+ Next &rarr;
200
+ </Button>
201
+ </Box>
202
+ </Fade>
203
+ )}
204
+
205
+ {/* Expertise + Start — shown after all plates */}
206
+ {allPlatesDone && (
207
+ <Fade in timeout={300}>
208
+ <Box
209
+ sx={{
210
+ display: 'flex',
211
+ flexDirection: 'column',
212
+ alignItems: 'center',
213
+ gap: 3,
214
+ width: '100%',
215
+ }}
216
+ >
217
+ <FormControl fullWidth>
218
+ <InputLabel>Your background</InputLabel>
219
+ <Select
220
+ value={expertise}
221
+ label="Your background"
222
+ onChange={(e) => setExpertise(e.target.value)}
223
+ >
224
+ <MenuItem value="General public">General public</MenuItem>
225
+ <MenuItem value="Photographer / Artist">
226
+ Photographer / Artist
227
+ </MenuItem>
228
+ <MenuItem value="Researcher / Academic">
229
+ Researcher / Academic
230
+ </MenuItem>
231
+ <MenuItem value="Computer Vision / ML expert">
232
+ Computer Vision / ML expert
233
+ </MenuItem>
234
+ </Select>
235
+ </FormControl>
236
+
237
+ <Button
238
+ variant="contained"
239
+ size="large"
240
+ fullWidth
241
+ disabled={!expertise || loading}
242
+ onClick={handleStart}
243
+ sx={{ mt: 1 }}
244
+ >
245
+ {loading ? (
246
+ <CircularProgress size={24} color="inherit" />
247
+ ) : (
248
+ 'Start \u2192'
249
+ )}
250
+ </Button>
251
+ </Box>
252
+ </Fade>
253
+ )}
254
+ </CardContent>
255
+ </Card>
256
+ </Box>
257
+ );
258
+ }
frontend/src/theme.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createTheme } from '@mui/material/styles';
2
+
3
+ const theme = createTheme({
4
+ palette: {
5
+ mode: 'dark',
6
+ primary: {
7
+ main: '#7C4DFF',
8
+ },
9
+ background: {
10
+ default: '#121212',
11
+ paper: '#1E1E1E',
12
+ },
13
+ success: {
14
+ main: '#4CAF50',
15
+ },
16
+ error: {
17
+ main: '#F44336',
18
+ },
19
+ },
20
+ typography: {
21
+ fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
22
+ h3: {
23
+ fontWeight: 700,
24
+ },
25
+ h5: {
26
+ fontWeight: 600,
27
+ },
28
+ },
29
+ shape: {
30
+ borderRadius: 12,
31
+ },
32
+ components: {
33
+ MuiButton: {
34
+ styleOverrides: {
35
+ root: {
36
+ textTransform: 'none',
37
+ fontWeight: 600,
38
+ borderRadius: 12,
39
+ padding: '12px 32px',
40
+ fontSize: '1rem',
41
+ },
42
+ },
43
+ },
44
+ MuiCard: {
45
+ styleOverrides: {
46
+ root: {
47
+ borderRadius: 16,
48
+ backgroundImage: 'none',
49
+ },
50
+ },
51
+ },
52
+ },
53
+ });
54
+
55
+ export default theme;
frontend/vite.config.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ proxy: {
8
+ '/api': 'http://localhost:8080',
9
+ '/images': 'http://localhost:8080',
10
+ '/tutorial': 'http://localhost:8080',
11
+ },
12
+ },
13
+ });
generate_colorblind_plates.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generate Ishihara-style pseudoisochromatic plates using PIL text for the digit mask.
3
+ Guaranteed correct digit shapes.
4
+ """
5
+ import numpy as np
6
+ from PIL import Image, ImageDraw, ImageFont
7
+ import random, math
8
+
9
+ def hex2rgb(h):
10
+ h = h.lstrip('#')
11
+ return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
12
+
13
+ def jitter_color(rgb, amount=20):
14
+ return tuple(max(0, min(255, c + random.randint(-amount, amount))) for c in rgb)
15
+
16
+ def make_text_mask(number_str, size, font_size=None):
17
+ """Render number_str as a clean white-on-black mask using PIL default font."""
18
+ if font_size is None:
19
+ font_size = int(size * 0.38) # big — ~212px tall for 560
20
+
21
+ mask = Image.new("L", (size, size), 0)
22
+ draw = ImageDraw.Draw(mask)
23
+
24
+ # Try to get a large font; fall back to default if not available
25
+ try:
26
+ font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", font_size)
27
+ except Exception:
28
+ try:
29
+ font = ImageFont.truetype("/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", font_size)
30
+ except Exception:
31
+ font = ImageFont.load_default()
32
+
33
+ # Get bounding box and centre text
34
+ bbox = draw.textbbox((0, 0), number_str, font=font)
35
+ tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
36
+ x = (size - tw) // 2 - bbox[0]
37
+ y = (size - th) // 2 - bbox[1]
38
+ draw.text((x, y), number_str, fill=255, font=font)
39
+
40
+ return np.array(mask) > 128
41
+
42
+
43
+ def generate_plate(
44
+ size=560,
45
+ number_str="74",
46
+ fg_colors=None,
47
+ bg_colors=None,
48
+ out_path="plate.png",
49
+ font_size=None,
50
+ ):
51
+ rng = random.Random(42)
52
+ mask = make_text_mask(number_str, size, font_size=font_size)
53
+
54
+ img = Image.new("RGB", (size, size), (215, 215, 215))
55
+ draw = ImageDraw.Draw(img)
56
+
57
+ cx, cy, radius = size // 2, size // 2, size // 2 - 6
58
+ draw.ellipse([cx-radius, cy-radius, cx+radius, cy+radius], fill=(210, 210, 210))
59
+
60
+ step = 9
61
+ dot_r_min, dot_r_max = 3, 7
62
+
63
+ for gx in range(dot_r_max, size - dot_r_max, step):
64
+ for gy in range(dot_r_max, size - dot_r_max, step):
65
+ px = gx + rng.randint(-3, 3)
66
+ py = gy + rng.randint(-3, 3)
67
+ if math.hypot(px - cx, py - cy) > radius - dot_r_max:
68
+ continue
69
+ r = rng.randint(dot_r_min, dot_r_max)
70
+ py_c = max(0, min(size-1, py))
71
+ px_c = max(0, min(size-1, px))
72
+ is_fg = bool(mask[py_c, px_c])
73
+ pool = fg_colors if is_fg else bg_colors
74
+ color = hex2rgb(rng.choice(pool))
75
+ color = jitter_color(color, 18)
76
+ draw.ellipse([px-r, py-r, px+r, py+r], fill=color)
77
+
78
+ draw.ellipse([cx-radius, cy-radius, cx+radius, cy+radius],
79
+ outline=(150,150,150), width=3)
80
+ img.save(out_path)
81
+ fg = int(mask.sum())
82
+ print(f" {out_path} number={number_str!r} fg_pixels={fg}")
83
+
84
+
85
+ OUT = "/home/ai24mtech02001/.openclaw/workspace/colorization-webapp/tutorial/colorblind"
86
+
87
+ print("Generating plates...")
88
+
89
+ # 1. DEMO — "12" vivid red vs blue, visible to everyone
90
+ generate_plate(
91
+ number_str="12",
92
+ fg_colors=["#C0392B","#E74C3C","#A93226","#B03A2E","#D44333"],
93
+ bg_colors=["#2471A3","#1A5276","#2E86C1","#1F618D","#5499C2","#2980B9"],
94
+ out_path=f"{OUT}/plate_demo.png",
95
+ )
96
+
97
+ # 2. RED-GREEN — "74"
98
+ # Normal: green pops on orange. Protan/Deutan: both look similar brownish.
99
+ generate_plate(
100
+ number_str="74",
101
+ fg_colors=["#4A7C00","#5B8C00","#6B9E0E","#527A00","#3D6B00","#618500"],
102
+ bg_colors=["#C47A1E","#D4892E","#BF6D10","#CC8022","#D99030",
103
+ "#B86008","#E09840","#A85500","#CB7818"],
104
+ out_path=f"{OUT}/plate_redgreen.png",
105
+ )
106
+
107
+ # 3. BLUE-YELLOW — "6"
108
+ # Normal: yellow pops on blue. Tritanope: similar.
109
+ generate_plate(
110
+ number_str="6",
111
+ fg_colors=["#E8C200","#F0CA00","#D4B000","#DCBA00","#F5D000","#EBC400"],
112
+ bg_colors=["#2C3E7A","#1C2D68","#3B4F90","#253580","#162870","#324488","#2A3C82"],
113
+ out_path=f"{OUT}/plate_blueyellow.png",
114
+ )
115
+
116
+ print("Done.")
image_samples/all_images_list.txt ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/instance/ADE_train_00000732.jpg
2
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/instance/ADE_train_00003558.jpg
3
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/instance/ADE_train_00004140.jpg
4
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/instance/ADE_train_00003253.jpg
5
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/instance/ADE_train_00005198.jpg
6
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/imagenet/ILSVRC2012_val_00000004.JPEG
7
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/imagenet/ILSVRC2012_val_00000005.JPEG
8
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/imagenet/ILSVRC2012_val_00000003.JPEG
9
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/imagenet/ILSVRC2012_val_00000002.JPEG
10
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/imagenet/ILSVRC2012_val_00000001.JPEG
11
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/coco/000000000057.jpg
12
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/coco/000000000016.jpg
13
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/coco/000000000001.jpg
14
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/coco/000000000063.jpg
15
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/standard/coco/000000000019.jpg
16
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/imagenet/ILSVRC2012_val_00000004.JPEG
17
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/imagenet/ILSVRC2012_val_00000005.JPEG
18
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/imagenet/ILSVRC2012_val_00000003.JPEG
19
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/imagenet/ILSVRC2012_val_00000002.JPEG
20
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/imagenet/ILSVRC2012_val_00000001.JPEG
21
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/coco/000000000057.jpg
22
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/coco/000000000016.jpg
23
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/coco/000000000001.jpg
24
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/coco/000000000063.jpg
25
+ /data/swarnim/eccv2026/image_reviews/image_samples/ddcolor/ortho/coco/000000000019.jpg
26
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/instance/ADE_train_00003253.png
27
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/instance/ADE_train_00003558.png
28
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/instance/ADE_train_00004140.png
29
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/instance/ADE_train_00005198.png
30
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/instance/ADE_train_00000732.png
31
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/imagenet/ILSVRC2012_val_00000003.png
32
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/imagenet/ILSVRC2012_val_00000005.png
33
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/imagenet/ILSVRC2012_val_00000001.png
34
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/imagenet/ILSVRC2012_val_00000002.png
35
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/imagenet/ILSVRC2012_val_00000004.png
36
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/coco/000000000019.png
37
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/coco/000000000016.png
38
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/coco/000000000063.png
39
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/coco/000000000001.png
40
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/standard/coco/000000000057.png
41
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/instance/ADE_train_00003253.png
42
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/instance/ADE_train_00003558.png
43
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/instance/ADE_train_00004140.png
44
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/instance/ADE_train_00005198.png
45
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/instance/ADE_train_00000732.png
46
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000003.png
47
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000005.png
48
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000001.png
49
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000002.png
50
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/imagenet/ILSVRC2012_val_00000004.png
51
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/coco/000000000019.png
52
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/coco/000000000016.png
53
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/coco/000000000063.png
54
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/coco/000000000001.png
55
+ /data/swarnim/eccv2026/image_reviews/image_samples/disco/ortho/coco/000000000057.png
56
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/instance/ADE_train_00003558_c564.jpg
57
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/instance/ADE_train_00005198_c762.jpg
58
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/instance/ADE_train_00000732_c579.jpg
59
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/instance/ADE_train_00003253_c750.jpg
60
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/instance/ADE_train_00004140_c564.jpg
61
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/imagenet/ILSVRC2012_val_00000001_c065.jpg
62
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/imagenet/ILSVRC2012_val_00000005_c516.jpg
63
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/imagenet/ILSVRC2012_val_00000003_c231.jpg
64
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/imagenet/ILSVRC2012_val_00000004_c809.jpg
65
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/imagenet/ILSVRC2012_val_00000002_c795.jpg
66
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/coco/000000000057_c752.jpg
67
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/coco/000000000001_c468.jpg
68
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/coco/000000000016_c981.jpg
69
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/coco/000000000063_c981.jpg
70
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/standard/coco/000000000019_c345.jpg
71
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/instance/ADE_train_00003558_c564.jpg
72
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/instance/ADE_train_00005198_c762.jpg
73
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/instance/ADE_train_00000732_c579.jpg
74
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/instance/ADE_train_00003253_c750.jpg
75
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/instance/ADE_train_00004140_c564.jpg
76
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000001_c065.jpg
77
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000005_c516.jpg
78
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000003_c231.jpg
79
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000004_c809.jpg
80
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000002_c795.jpg
81
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/coco/000000000057_c752.jpg
82
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/coco/000000000016_c981.jpg
83
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/coco/000000000019_c345.jpg
84
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/coco/000000000001_c675.jpg
85
+ /data/swarnim/eccv2026/image_reviews/image_samples/bigcolor/ortho/coco/000000000063_c522.jpg
86
+ /data/swarnim/eccv2026/image_reviews/image_samples/mixed/standard/coco/000000000001_colorized.jpg
87
+ /data/swarnim/eccv2026/image_reviews/image_samples/mixed/standard/coco/000000000019_colorized.jpg
88
+ /data/swarnim/eccv2026/image_reviews/image_samples/mixed/standard/coco/000000000057_colorized.jpg
89
+ /data/swarnim/eccv2026/image_reviews/image_samples/mixed/standard/coco/000000000063_colorized.jpg
90
+ /data/swarnim/eccv2026/image_reviews/image_samples/mixed/standard/coco/000000000016_colorized.jpg
91
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/instance/ADE_train_00003253.jpg.png
92
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/instance/ADE_train_00003558.jpg.png
93
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/instance/ADE_train_00004140.jpg.png
94
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/instance/ADE_train_00000732.jpg.png
95
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/instance/ADE_train_00005198.jpg.png
96
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000004.JPEG.png
97
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000002.JPEG.png
98
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000003.JPEG.png
99
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000001.JPEG.png
100
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/imagenet/ILSVRC2012_val_00000005.JPEG.png
101
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/coco/000000000057.jpg.png
102
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/coco/000000000019.jpg.png
103
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/coco/000000000016.jpg.png
104
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/coco/000000000063.jpg.png
105
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/standard/coco/000000000001.jpg.png
106
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/instance/ADE_train_00003253.jpg.png
107
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/instance/ADE_train_00003558.jpg.png
108
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/instance/ADE_train_00004140.jpg.png
109
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/instance/ADE_train_00000732.jpg.png
110
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/instance/ADE_train_00005198.jpg.png
111
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000004.JPEG.png
112
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000002.JPEG.png
113
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000003.JPEG.png
114
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000001.JPEG.png
115
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/imagenet/ILSVRC2012_val_00000005.JPEG.png
116
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/coco/000000000057.jpg.png
117
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/coco/000000000019.jpg.png
118
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/coco/000000000016.jpg.png
119
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/coco/000000000063.jpg.png
120
+ /data/swarnim/eccv2026/image_reviews/image_samples/unicolor/ortho/coco/000000000001.jpg.png
image_samples/bigcolor/ortho/coco/000000000001_c675.jpg ADDED
image_samples/bigcolor/ortho/coco/000000000016_c981.jpg ADDED
image_samples/bigcolor/ortho/coco/000000000019_c345.jpg ADDED
image_samples/bigcolor/ortho/coco/000000000057_c752.jpg ADDED
image_samples/bigcolor/ortho/coco/000000000063_c522.jpg ADDED
image_samples/bigcolor/ortho/coco/image_sources.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Image,Source_Path
2
+ 000000000001_c675.jpg,/data/swarnim/BigColor/results_5k/coco_bg/coco/000000000001_c675.jpg
3
+ 000000000016_c981.jpg,/data/swarnim/BigColor/results_5k/coco_bg/coco/000000000016_c981.jpg
4
+ 000000000019_c345.jpg,/data/swarnim/BigColor/results_5k/coco_bg/coco/000000000019_c345.jpg
5
+ 000000000057_c752.jpg,/data/swarnim/BigColor/results_5k/coco_bg/coco/000000000057_c752.jpg
6
+ 000000000063_c522.jpg,/data/swarnim/BigColor/results_5k/coco_bg/coco/000000000063_c522.jpg
image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000001_c065.jpg ADDED
image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000002_c795.jpg ADDED
image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000003_c231.jpg ADDED
image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000004_c809.jpg ADDED
image_samples/bigcolor/ortho/imagenet/ILSVRC2012_val_00000005_c516.jpg ADDED
image_samples/bigcolor/ortho/imagenet/image_sources.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Image,Source_Path
2
+ ILSVRC2012_val_00000001_c065.jpg,/data/swarnim/BigColor/results_5k/imagenet_bg/imagenet/ILSVRC2012_val_00000001_c065.jpg
3
+ ILSVRC2012_val_00000002_c795.jpg,/data/swarnim/BigColor/results_5k/imagenet_bg/imagenet/ILSVRC2012_val_00000002_c795.jpg
4
+ ILSVRC2012_val_00000003_c231.jpg,/data/swarnim/BigColor/results_5k/imagenet_bg/imagenet/ILSVRC2012_val_00000003_c231.jpg
5
+ ILSVRC2012_val_00000004_c809.jpg,/data/swarnim/BigColor/results_5k/imagenet_bg/imagenet/ILSVRC2012_val_00000004_c809.jpg
6
+ ILSVRC2012_val_00000005_c516.jpg,/data/swarnim/BigColor/results_5k/imagenet_bg/imagenet/ILSVRC2012_val_00000005_c516.jpg
image_samples/bigcolor/ortho/instance/ADE_train_00000732_c579.jpg ADDED
image_samples/bigcolor/ortho/instance/ADE_train_00003253_c750.jpg ADDED
image_samples/bigcolor/ortho/instance/ADE_train_00003558_c564.jpg ADDED
image_samples/bigcolor/ortho/instance/ADE_train_00004140_c564.jpg ADDED
image_samples/bigcolor/ortho/instance/ADE_train_00005198_c762.jpg ADDED
image_samples/bigcolor/ortho/instance/image_sources.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Image,Source_Path
2
+ ADE_train_00000732_c579.jpg,/data/swarnim/BigColor/results_5k/instance_bg/instance/ADE_train_00000732_c579.jpg
3
+ ADE_train_00003253_c750.jpg,/data/swarnim/BigColor/results_5k/instance_bg/instance/ADE_train_00003253_c750.jpg
4
+ ADE_train_00003558_c564.jpg,/data/swarnim/BigColor/results_5k/instance_bg/instance/ADE_train_00003558_c564.jpg
5
+ ADE_train_00004140_c564.jpg,/data/swarnim/BigColor/results_5k/instance_bg/instance/ADE_train_00004140_c564.jpg
6
+ ADE_train_00005198_c762.jpg,/data/swarnim/BigColor/results_5k/instance_bg/instance/ADE_train_00005198_c762.jpg
image_samples/bigcolor/standard/coco/000000000001_c468.jpg ADDED
image_samples/bigcolor/standard/coco/000000000016_c981.jpg ADDED