Soheib31 commited on
Commit
47a81c7
·
verified ·
1 Parent(s): f00cc68

Upload 27 files

Browse files
.bolt/config.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "template": "vite"
3
+ }
.gitignore ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+ env/
27
+
28
+ # IDEs and editors
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+ .DS_Store
34
+
35
+ # Project specific
36
+ uploads/
37
+ *.log
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy requirements and install dependencies
6
+ COPY ./backend/requirements.txt /app/
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # Install additional dependencies for document processing
10
+ # Note: These would be replaced with actual document processing libraries in production
11
+ RUN pip install --no-cache-dir pandas openpyxl python-docx
12
+
13
+ # Copy backend code
14
+ COPY ./backend /app/backend
15
+
16
+ # Copy frontend files
17
+ COPY ./frontend /app/frontend
18
+
19
+ # Create uploads directory
20
+ RUN mkdir -p /app/uploads
21
+
22
+ # Set environment variables
23
+ ENV PYTHONPATH=/app
24
+ ENV PORT=7860
25
+
26
+ # Expose the port
27
+ EXPOSE 7860
28
+
29
+ # Run the FastAPI app
30
+ CMD ["python", "backend/main.py"]
backend/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Backend package initialization
backend/__pycache__/main.cpython-312.pyc ADDED
Binary file (4.75 kB). View file
 
backend/main.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
2
+ from fastapi.staticfiles import StaticFiles
3
+ from fastapi.responses import JSONResponse
4
+ import uvicorn
5
+ import os
6
+ from typing import List, Optional
7
+ import shutil
8
+ from pathlib import Path
9
+ import uuid
10
+
11
+ # Import AI functionality modules
12
+ from modules.document_processor import process_document
13
+ from modules.image_processor import process_image
14
+
15
+ # Create FastAPI app
16
+ app = FastAPI(title="AI Document Analysis API")
17
+
18
+ # Create upload directory if it doesn't exist
19
+ UPLOAD_DIR = Path("uploads")
20
+ UPLOAD_DIR.mkdir(exist_ok=True)
21
+
22
+ # Mount static files - updated path to be relative to the current directory
23
+ app.mount("/static", StaticFiles(directory="frontend"), name="static")
24
+
25
+ @app.get("/")
26
+ def read_root():
27
+ return {"message": "AI Document Analysis API is running"}
28
+
29
+ @app.post("/api/analyze-document")
30
+ async def analyze_document(
31
+ file: UploadFile = File(...),
32
+ analysis_type: str = Form(...)
33
+ ):
34
+ # Validate file type
35
+ allowed_extensions = [".pdf", ".docx", ".pptx", ".xlsx", ".xls"]
36
+ file_ext = os.path.splitext(file.filename)[1].lower()
37
+
38
+ if file_ext not in allowed_extensions:
39
+ raise HTTPException(status_code=400, detail=f"File type not supported. Allowed types: {', '.join(allowed_extensions)}")
40
+
41
+ # Create unique filename
42
+ unique_filename = f"{uuid.uuid4()}{file_ext}"
43
+ file_path = UPLOAD_DIR / unique_filename
44
+
45
+ # Save uploaded file
46
+ with open(file_path, "wb") as buffer:
47
+ shutil.copyfileobj(file.file, buffer)
48
+
49
+ try:
50
+ # Process document based on analysis type
51
+ if analysis_type == "summarize":
52
+ result = process_document(str(file_path), "summarize")
53
+ else:
54
+ raise HTTPException(status_code=400, detail="Invalid analysis type")
55
+
56
+ # Return results
57
+ return {
58
+ "filename": file.filename,
59
+ "analysis_type": analysis_type,
60
+ "result": result
61
+ }
62
+ except Exception as e:
63
+ # Clean up file on error
64
+ if file_path.exists():
65
+ os.remove(file_path)
66
+ raise HTTPException(status_code=500, detail=str(e))
67
+
68
+ @app.post("/api/analyze-image")
69
+ async def analyze_image(
70
+ file: UploadFile = File(...),
71
+ analysis_type: str = Form(...)
72
+ ):
73
+ # Validate file type
74
+ allowed_extensions = [".jpg", ".jpeg", ".png"]
75
+ file_ext = os.path.splitext(file.filename)[1].lower()
76
+
77
+ if file_ext not in allowed_extensions:
78
+ raise HTTPException(status_code=400, detail=f"File type not supported. Allowed types: {', '.join(allowed_extensions)}")
79
+
80
+ # Create unique filename
81
+ unique_filename = f"{uuid.uuid4()}{file_ext}"
82
+ file_path = UPLOAD_DIR / unique_filename
83
+
84
+ # Save uploaded file
85
+ with open(file_path, "wb") as buffer:
86
+ shutil.copyfileobj(file.file, buffer)
87
+
88
+ try:
89
+ # Process image based on analysis type
90
+ if analysis_type == "caption":
91
+ result = process_image(str(file_path), "caption")
92
+ else:
93
+ raise HTTPException(status_code=400, detail="Invalid analysis type")
94
+
95
+ # Return results
96
+ return {
97
+ "filename": file.filename,
98
+ "analysis_type": analysis_type,
99
+ "result": result
100
+ }
101
+ except Exception as e:
102
+ # Clean up file on error
103
+ if file_path.exists():
104
+ os.remove(file_path)
105
+ raise HTTPException(status_code=500, detail=str(e))
106
+
107
+ # Add health check endpoint
108
+ @app.get("/health")
109
+ def health_check():
110
+ return {"status": "healthy"}
111
+
112
+ if __name__ == "__main__":
113
+ uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)
backend/modules/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Modules package initialization
backend/modules/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (168 Bytes). View file
 
backend/modules/__pycache__/document_processor.cpython-312.pyc ADDED
Binary file (2.76 kB). View file
 
backend/modules/__pycache__/image_processor.cpython-312.pyc ADDED
Binary file (2.2 kB). View file
 
backend/modules/document_processor.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ import tempfile
4
+ import logging
5
+
6
+ # Configure logging
7
+ logging.basicConfig(level=logging.INFO)
8
+ logger = logging.getLogger(__name__)
9
+
10
+ # This is a placeholder for actual Hugging Face model integration
11
+ # In a real implementation, you would import and use appropriate models
12
+ def process_document(file_path, analysis_type):
13
+ """
14
+ Process a document using Hugging Face models.
15
+
16
+ Args:
17
+ file_path (str): Path to the uploaded document
18
+ analysis_type (str): Type of analysis to perform (summarize, etc.)
19
+
20
+ Returns:
21
+ str: Result of document analysis
22
+ """
23
+ logger.info(f"Processing document: {file_path} with analysis type: {analysis_type}")
24
+
25
+ # Get file extension
26
+ file_ext = os.path.splitext(file_path)[1].lower()
27
+
28
+ # Extract text from document based on file type
29
+ extracted_text = extract_text_from_document(file_path, file_ext)
30
+
31
+ # For demonstration purposes, return a mock summary
32
+ # In a real implementation, you would use a Hugging Face model here
33
+ if analysis_type == "summarize":
34
+ # Mock summarization result
35
+ summary = f"This is a mock summary of the document. In a real implementation, this would be generated by a Hugging Face model like Llama-2-70b-chat-hf or similar.\n\nThe document appears to contain information about {get_mock_topic(extracted_text)}."
36
+ return summary
37
+ else:
38
+ return "Unsupported analysis type"
39
+
40
+ def extract_text_from_document(file_path, file_ext):
41
+ """
42
+ Extract text from different document types.
43
+
44
+ This is a placeholder that would be replaced with actual document parsing
45
+ libraries like Apache Tika, PyMuPDF, etc.
46
+ """
47
+ logger.info(f"Extracting text from {file_path}")
48
+
49
+ # In a real implementation, you would use libraries like:
50
+ # - PyMuPDF for PDFs
51
+ # - python-docx for DOCX files
52
+ # - python-pptx for PPTX files
53
+ # - openpyxl for Excel files
54
+
55
+ # Mock extraction based on file type
56
+ if file_ext == ".pdf":
57
+ return "This is mock extracted text from a PDF document."
58
+ elif file_ext == ".docx":
59
+ return "This is mock extracted text from a Word document."
60
+ elif file_ext == ".pptx":
61
+ return "This is mock extracted text from a PowerPoint presentation."
62
+ elif file_ext in [".xlsx", ".xls"]:
63
+ return "This is mock extracted tabular data from an Excel spreadsheet."
64
+ else:
65
+ return "Unknown document type"
66
+
67
+ def get_mock_topic(text):
68
+ """Generate a mock topic based on some simple heuristics."""
69
+ # In a real implementation, this might use keyword extraction or topic modeling
70
+ topics = ["business analysis", "scientific research", "financial data",
71
+ "project planning", "educational material"]
72
+
73
+ # Simple mock topic selection based on text length
74
+ return topics[len(text) % len(topics)]
backend/modules/image_processor.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ from pathlib import Path
4
+
5
+ # Configure logging
6
+ logging.basicConfig(level=logging.INFO)
7
+ logger = logging.getLogger(__name__)
8
+
9
+ # This is a placeholder for actual Hugging Face model integration
10
+ # In a real implementation, you would import and use appropriate models
11
+ def process_image(file_path, analysis_type):
12
+ """
13
+ Process an image using Hugging Face models.
14
+
15
+ Args:
16
+ file_path (str): Path to the uploaded image
17
+ analysis_type (str): Type of analysis to perform (caption, etc.)
18
+
19
+ Returns:
20
+ str: Result of image analysis
21
+ """
22
+ logger.info(f"Processing image: {file_path} with analysis type: {analysis_type}")
23
+
24
+ # For demonstration purposes, return a mock caption
25
+ # In a real implementation, you would use a Hugging Face model here
26
+ if analysis_type == "caption":
27
+ # Mock image captioning result
28
+ caption = generate_mock_caption(file_path)
29
+ return caption
30
+ else:
31
+ return "Unsupported analysis type"
32
+
33
+ def generate_mock_caption(image_path):
34
+ """
35
+ Generate a mock image caption.
36
+
37
+ In a real implementation, this would use a Hugging Face model like
38
+ Qwen/Qwen2.5-VL-7B-Instruct or similar for image captioning.
39
+ """
40
+ # Get file size as a simple way to generate different captions
41
+ file_size = os.path.getsize(image_path)
42
+
43
+ # List of mock captions
44
+ captions = [
45
+ "A beautiful landscape with mountains and a lake under blue sky.",
46
+ "A person working at a desk with a computer and various office supplies.",
47
+ "A busy city street with pedestrians and vehicles during the day.",
48
+ "A close-up photograph of a flower with vibrant colors and intricate details.",
49
+ "A group of people gathered around a table for what appears to be a business meeting.",
50
+ "A modern architectural building with glass and steel elements."
51
+ ]
52
+
53
+ # Select a caption based on file size
54
+ mock_caption = captions[file_size % len(captions)]
55
+
56
+ return f"{mock_caption}\n\nNote: This is a mock caption. In a real implementation, this would be generated by a vision-language model like Qwen/Qwen2.5-VL-7B-Instruct."
backend/requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn==0.23.2
3
+ python-multipart==0.0.6
4
+ aiofiles==23.2.1
5
+ Pillow==10.0.1
6
+ pydantic==2.4.2
7
+ python-dotenv==1.0.0
counter.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ export function setupCounter(element) {
2
+ let counter = 0
3
+ const setCounter = (count) => {
4
+ counter = count
5
+ element.innerHTML = `count is ${counter}`
6
+ }
7
+ element.addEventListener('click', () => setCounter(counter + 1))
8
+ setCounter(0)
9
+ }
frontend/css/animations.css ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Animations */
2
+
3
+ /* Spinner Animation */
4
+ @keyframes spin {
5
+ 0% { transform: rotate(0deg); }
6
+ 100% { transform: rotate(360deg); }
7
+ }
8
+
9
+ .spinner {
10
+ animation: spin 1s linear infinite;
11
+ }
12
+
13
+ /* Fade In Animation */
14
+ @keyframes fadeIn {
15
+ from { opacity: 0; }
16
+ to { opacity: 1; }
17
+ }
18
+
19
+ .fade-in {
20
+ animation: fadeIn 0.5s ease-in;
21
+ }
22
+
23
+ /* Slide Up Animation */
24
+ @keyframes slideUp {
25
+ from {
26
+ transform: translateY(20px);
27
+ opacity: 0;
28
+ }
29
+ to {
30
+ transform: translateY(0);
31
+ opacity: 1;
32
+ }
33
+ }
34
+
35
+ .slide-up {
36
+ animation: slideUp 0.5s ease-out;
37
+ }
38
+
39
+ /* Pulse Animation */
40
+ @keyframes pulse {
41
+ 0% { transform: scale(1); }
42
+ 50% { transform: scale(1.05); }
43
+ 100% { transform: scale(1); }
44
+ }
45
+
46
+ .pulse {
47
+ animation: pulse 2s infinite;
48
+ }
49
+
50
+ /* Scale Animation */
51
+ @keyframes scale {
52
+ from { transform: scale(0.95); }
53
+ to { transform: scale(1); }
54
+ }
55
+
56
+ .scale {
57
+ animation: scale 0.3s ease-out;
58
+ }
59
+
60
+ /* Button Hover Effects */
61
+ .analyze-btn:not(:disabled):hover {
62
+ transform: translateY(-2px);
63
+ box-shadow: 0 4px 6px rgba(37, 99, 235, 0.25);
64
+ }
65
+
66
+ .analyze-btn:not(:disabled):active {
67
+ transform: translateY(0);
68
+ }
69
+
70
+ /* Card Hover Effects */
71
+ .feature-card:hover .feature-icon {
72
+ transform: scale(1.1);
73
+ background-color: rgba(37, 99, 235, 0.15);
74
+ }
75
+
76
+ /* File Upload Animation */
77
+ .upload-area.dragover {
78
+ transform: scale(1.02);
79
+ }
80
+
81
+ /* Tab Button Animation */
82
+ .tab-btn {
83
+ transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.2s ease;
84
+ }
85
+
86
+ .tab-btn:hover:not(.active) {
87
+ transform: translateY(-2px);
88
+ }
89
+
90
+ .tab-btn.active {
91
+ transform: translateY(-3px);
92
+ }
93
+
94
+ /* Results Container Animation */
95
+ .results-container {
96
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
97
+ }
98
+
99
+ .results-container:hover {
100
+ box-shadow: var(--shadow-lg);
101
+ }
102
+
103
+ /* Section Transitions */
104
+ .section-title,
105
+ .feature-card,
106
+ .about-content p {
107
+ opacity: 0;
108
+ transform: translateY(20px);
109
+ transition: opacity 0.5s ease, transform 0.5s ease;
110
+ }
111
+
112
+ /* These will be activated by JavaScript when elements come into view */
113
+ .appear {
114
+ opacity: 1;
115
+ transform: translateY(0);
116
+ }
117
+
118
+ /* Hero Section Animation */
119
+ .hero {
120
+ background-size: 200% 200%;
121
+ animation: gradientAnimation 15s ease infinite;
122
+ }
123
+
124
+ @keyframes gradientAnimation {
125
+ 0% { background-position: 0% 50%; }
126
+ 50% { background-position: 100% 50%; }
127
+ 100% { background-position: 0% 50%; }
128
+ }
129
+
130
+ /* Logo Animation */
131
+ .logo i {
132
+ transition: transform 0.3s ease;
133
+ }
134
+
135
+ .logo:hover i {
136
+ transform: rotate(15deg);
137
+ }
138
+
139
+ /* Action Buttons Animation */
140
+ .action-btn {
141
+ transition: all 0.2s ease;
142
+ }
143
+
144
+ .action-btn:hover {
145
+ transform: translateY(-2px);
146
+ }
147
+
148
+ .action-btn:active {
149
+ transform: translateY(0);
150
+ }
151
+
152
+ /* File Selected Animation */
153
+ .file-selected {
154
+ border-color: var(--success-color) !important;
155
+ background-color: rgba(16, 185, 129, 0.05) !important;
156
+ }
157
+
158
+ .file-selected i {
159
+ color: var(--success-color);
160
+ }
frontend/css/styles.css ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #2563EB;
3
+ --primary-light: #3B82F6;
4
+ --primary-dark: #1D4ED8;
5
+ --accent-color: #7C3AED;
6
+ --accent-light: #8B5CF6;
7
+ --success-color: #10B981;
8
+ --warning-color: #F59E0B;
9
+ --error-color: #EF4444;
10
+ --text-dark: #1F2937;
11
+ --text-medium: #4B5563;
12
+ --text-light: #9CA3AF;
13
+ --background-color: #F9FAFB;
14
+ --card-bg: #FFFFFF;
15
+ --border-color: #E5E7EB;
16
+ --spacing-xs: 4px;
17
+ --spacing-sm: 8px;
18
+ --spacing-md: 16px;
19
+ --spacing-lg: 24px;
20
+ --spacing-xl: 32px;
21
+ --spacing-xxl: 48px;
22
+ --border-radius-sm: 4px;
23
+ --border-radius-md: 8px;
24
+ --border-radius-lg: 12px;
25
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
26
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
27
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
28
+ --font-family: 'Inter', sans-serif;
29
+ }
30
+
31
+ /* Base Styles */
32
+ * {
33
+ margin: 0;
34
+ padding: 0;
35
+ box-sizing: border-box;
36
+ }
37
+
38
+ html {
39
+ scroll-behavior: smooth;
40
+ }
41
+
42
+ body {
43
+ font-family: var(--font-family);
44
+ line-height: 1.5;
45
+ color: var(--text-dark);
46
+ background-color: var(--background-color);
47
+ }
48
+
49
+ .container {
50
+ width: 100%;
51
+ max-width: 1200px;
52
+ margin: 0 auto;
53
+ padding: 0 var(--spacing-md);
54
+ }
55
+
56
+ /* Typography */
57
+ h1, h2, h3, h4, h5, h6 {
58
+ font-weight: 600;
59
+ line-height: 1.2;
60
+ margin-bottom: var(--spacing-md);
61
+ }
62
+
63
+ h1 {
64
+ font-size: 2.5rem;
65
+ }
66
+
67
+ h2 {
68
+ font-size: 2rem;
69
+ }
70
+
71
+ h3 {
72
+ font-size: 1.5rem;
73
+ }
74
+
75
+ p {
76
+ margin-bottom: var(--spacing-md);
77
+ }
78
+
79
+ a {
80
+ color: var(--primary-color);
81
+ text-decoration: none;
82
+ transition: color 0.3s ease;
83
+ }
84
+
85
+ a:hover {
86
+ color: var(--primary-dark);
87
+ }
88
+
89
+ /* Button Styles */
90
+ button {
91
+ cursor: pointer;
92
+ font-family: var(--font-family);
93
+ font-size: 1rem;
94
+ border: none;
95
+ transition: all 0.3s ease;
96
+ }
97
+
98
+ .btn, .analyze-btn, .action-btn {
99
+ display: inline-flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ padding: var(--spacing-sm) var(--spacing-lg);
103
+ border-radius: var(--border-radius-md);
104
+ font-weight: 500;
105
+ }
106
+
107
+ .analyze-btn {
108
+ background-color: var(--primary-color);
109
+ color: white;
110
+ font-weight: 600;
111
+ padding: var(--spacing-md) var(--spacing-xl);
112
+ border-radius: var(--border-radius-md);
113
+ margin-top: var(--spacing-md);
114
+ width: 100%;
115
+ }
116
+
117
+ .analyze-btn:hover {
118
+ background-color: var(--primary-dark);
119
+ transform: translateY(-1px);
120
+ }
121
+
122
+ .analyze-btn:disabled {
123
+ background-color: var(--text-light);
124
+ cursor: not-allowed;
125
+ transform: none;
126
+ }
127
+
128
+ .action-btn {
129
+ padding: var(--spacing-sm) var(--spacing-md);
130
+ background-color: var(--card-bg);
131
+ color: var(--text-medium);
132
+ border: 1px solid var(--border-color);
133
+ margin-left: var(--spacing-sm);
134
+ }
135
+
136
+ .action-btn:hover {
137
+ background-color: var(--background-color);
138
+ color: var(--text-dark);
139
+ }
140
+
141
+ .action-btn i {
142
+ margin-right: 4px;
143
+ }
144
+
145
+ /* Header */
146
+ .header {
147
+ background-color: var(--card-bg);
148
+ box-shadow: var(--shadow-sm);
149
+ position: sticky;
150
+ top: 0;
151
+ z-index: 100;
152
+ }
153
+
154
+ .header .container {
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: space-between;
158
+ padding-top: var(--spacing-md);
159
+ padding-bottom: var(--spacing-md);
160
+ }
161
+
162
+ .logo {
163
+ display: flex;
164
+ align-items: center;
165
+ }
166
+
167
+ .logo i {
168
+ font-size: 1.8rem;
169
+ color: var(--primary-color);
170
+ margin-right: var(--spacing-sm);
171
+ }
172
+
173
+ .logo h1 {
174
+ font-size: 1.5rem;
175
+ font-weight: 700;
176
+ margin-bottom: 0;
177
+ }
178
+
179
+ .nav ul {
180
+ display: flex;
181
+ list-style: none;
182
+ }
183
+
184
+ .nav li {
185
+ margin-left: var(--spacing-lg);
186
+ }
187
+
188
+ .nav a {
189
+ color: var(--text-medium);
190
+ font-weight: 500;
191
+ padding: 0.5rem 0;
192
+ position: relative;
193
+ }
194
+
195
+ .nav a.active, .nav a:hover {
196
+ color: var(--primary-color);
197
+ }
198
+
199
+ .nav a.active::after, .nav a:hover::after {
200
+ content: '';
201
+ position: absolute;
202
+ bottom: -4px;
203
+ left: 0;
204
+ width: 100%;
205
+ height: 2px;
206
+ background-color: var(--primary-color);
207
+ }
208
+
209
+ /* Hero Section */
210
+ .hero {
211
+ padding: var(--spacing-xxl) 0;
212
+ text-align: center;
213
+ background: linear-gradient(120deg, var(--primary-light), var(--accent-color));
214
+ color: white;
215
+ }
216
+
217
+ .hero-title {
218
+ font-size: 3rem;
219
+ font-weight: 700;
220
+ margin-bottom: var(--spacing-md);
221
+ }
222
+
223
+ .hero-subtitle {
224
+ font-size: 1.2rem;
225
+ max-width: 800px;
226
+ margin: 0 auto var(--spacing-lg);
227
+ }
228
+
229
+ /* Upload Section */
230
+ .upload-section {
231
+ padding: var(--spacing-xxl) 0;
232
+ }
233
+
234
+ .tabs {
235
+ display: flex;
236
+ justify-content: center;
237
+ margin-bottom: var(--spacing-xl);
238
+ }
239
+
240
+ .tab-btn {
241
+ padding: var(--spacing-md) var(--spacing-xl);
242
+ background-color: var(--card-bg);
243
+ color: var(--text-medium);
244
+ border: 1px solid var(--border-color);
245
+ font-weight: 500;
246
+ }
247
+
248
+ .tab-btn:first-child {
249
+ border-radius: var(--border-radius-md) 0 0 var(--border-radius-md);
250
+ }
251
+
252
+ .tab-btn:last-child {
253
+ border-radius: 0 var(--border-radius-md) var(--border-radius-md) 0;
254
+ }
255
+
256
+ .tab-btn.active {
257
+ background-color: var(--primary-color);
258
+ color: white;
259
+ border-color: var(--primary-color);
260
+ }
261
+
262
+ .tab-content {
263
+ display: none;
264
+ }
265
+
266
+ .tab-content.active {
267
+ display: block;
268
+ }
269
+
270
+ .upload-container {
271
+ display: grid;
272
+ grid-template-columns: 1fr;
273
+ gap: var(--spacing-xl);
274
+ }
275
+
276
+ @media (min-width: 768px) {
277
+ .upload-container {
278
+ grid-template-columns: 2fr 1fr;
279
+ }
280
+ }
281
+
282
+ .upload-area {
283
+ display: flex;
284
+ flex-direction: column;
285
+ align-items: center;
286
+ justify-content: center;
287
+ text-align: center;
288
+ padding: var(--spacing-xxl);
289
+ background-color: var(--card-bg);
290
+ border: 2px dashed var(--border-color);
291
+ border-radius: var(--border-radius-lg);
292
+ cursor: pointer;
293
+ transition: all 0.3s ease;
294
+ }
295
+
296
+ .upload-area:hover {
297
+ border-color: var(--primary-light);
298
+ background-color: rgba(37, 99, 235, 0.05);
299
+ }
300
+
301
+ .upload-area.dragover {
302
+ border-color: var(--primary-color);
303
+ background-color: rgba(37, 99, 235, 0.1);
304
+ }
305
+
306
+ .upload-area i {
307
+ font-size: 3rem;
308
+ color: var(--primary-color);
309
+ margin-bottom: var(--spacing-md);
310
+ }
311
+
312
+ .upload-area h3 {
313
+ margin-bottom: var(--spacing-sm);
314
+ }
315
+
316
+ .upload-area .file-types {
317
+ font-size: 0.9rem;
318
+ color: var(--text-light);
319
+ margin-top: var(--spacing-md);
320
+ }
321
+
322
+ .analysis-options {
323
+ background-color: var(--card-bg);
324
+ padding: var(--spacing-xl);
325
+ border-radius: var(--border-radius-lg);
326
+ box-shadow: var(--shadow-md);
327
+ }
328
+
329
+ .radio-group {
330
+ margin-bottom: var(--spacing-lg);
331
+ }
332
+
333
+ .radio-option {
334
+ margin-bottom: var(--spacing-sm);
335
+ display: flex;
336
+ align-items: center;
337
+ }
338
+
339
+ .radio-option input[type="radio"] {
340
+ margin-right: var(--spacing-sm);
341
+ }
342
+
343
+ /* Results Container */
344
+ .results-container {
345
+ margin-top: var(--spacing-xl);
346
+ background-color: var(--card-bg);
347
+ border-radius: var(--border-radius-lg);
348
+ box-shadow: var(--shadow-md);
349
+ overflow: hidden;
350
+ }
351
+
352
+ .results-header {
353
+ display: flex;
354
+ justify-content: space-between;
355
+ align-items: center;
356
+ padding: var(--spacing-lg);
357
+ border-bottom: 1px solid var(--border-color);
358
+ }
359
+
360
+ .results-header h2 {
361
+ margin-bottom: 0;
362
+ }
363
+
364
+ .results-content {
365
+ padding: var(--spacing-xl);
366
+ white-space: pre-wrap;
367
+ line-height: 1.6;
368
+ }
369
+
370
+ /* Features Section */
371
+ .features {
372
+ padding: var(--spacing-xxl) 0;
373
+ background-color: var(--card-bg);
374
+ }
375
+
376
+ .section-title {
377
+ text-align: center;
378
+ margin-bottom: var(--spacing-xl);
379
+ }
380
+
381
+ .features-grid {
382
+ display: grid;
383
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
384
+ gap: var(--spacing-xl);
385
+ }
386
+
387
+ .feature-card {
388
+ background-color: var(--background-color);
389
+ padding: var(--spacing-xl);
390
+ border-radius: var(--border-radius-lg);
391
+ text-align: center;
392
+ box-shadow: var(--shadow-sm);
393
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
394
+ }
395
+
396
+ .feature-card:hover {
397
+ transform: translateY(-5px);
398
+ box-shadow: var(--shadow-lg);
399
+ }
400
+
401
+ .feature-icon {
402
+ display: inline-flex;
403
+ align-items: center;
404
+ justify-content: center;
405
+ width: 64px;
406
+ height: 64px;
407
+ background-color: rgba(37, 99, 235, 0.1);
408
+ border-radius: 50%;
409
+ margin-bottom: var(--spacing-md);
410
+ }
411
+
412
+ .feature-icon i {
413
+ font-size: 1.5rem;
414
+ color: var(--primary-color);
415
+ }
416
+
417
+ /* About Section */
418
+ .about {
419
+ padding: var(--spacing-xxl) 0;
420
+ }
421
+
422
+ .about-content {
423
+ max-width: 800px;
424
+ margin: 0 auto;
425
+ }
426
+
427
+ /* Footer */
428
+ .footer {
429
+ background-color: var(--text-dark);
430
+ color: white;
431
+ padding: var(--spacing-xl) 0;
432
+ text-align: center;
433
+ }
434
+
435
+ .footer p {
436
+ margin-bottom: 0;
437
+ }
438
+
439
+ .footer i {
440
+ color: var(--error-color);
441
+ }
442
+
443
+ /* Loading Overlay */
444
+ .loading-overlay {
445
+ position: fixed;
446
+ top: 0;
447
+ left: 0;
448
+ width: 100%;
449
+ height: 100%;
450
+ background-color: rgba(0, 0, 0, 0.7);
451
+ display: flex;
452
+ flex-direction: column;
453
+ align-items: center;
454
+ justify-content: center;
455
+ z-index: 1000;
456
+ color: white;
457
+ display: none;
458
+ }
459
+
460
+ .spinner {
461
+ width: 50px;
462
+ height: 50px;
463
+ border: 5px solid rgba(255, 255, 255, 0.3);
464
+ border-radius: 50%;
465
+ border-top-color: var(--primary-color);
466
+ margin-bottom: var(--spacing-md);
467
+ }
468
+
469
+ /* Responsive Styles */
470
+ @media (max-width: 768px) {
471
+ .hero-title {
472
+ font-size: 2.2rem;
473
+ }
474
+
475
+ .hero-subtitle {
476
+ font-size: 1rem;
477
+ }
478
+
479
+ .logo h1 {
480
+ font-size: 1.2rem;
481
+ }
482
+
483
+ .nav li {
484
+ margin-left: var(--spacing-md);
485
+ }
486
+
487
+ .upload-area {
488
+ padding: var(--spacing-lg);
489
+ }
490
+
491
+ .feature-card {
492
+ padding: var(--spacing-lg);
493
+ }
494
+ }
495
+
496
+ @media (max-width: 576px) {
497
+ .logo h1 {
498
+ display: none;
499
+ }
500
+
501
+ .nav a {
502
+ font-size: 0.9rem;
503
+ }
504
+
505
+ .tabs {
506
+ flex-direction: column;
507
+ width: 100%;
508
+ }
509
+
510
+ .tab-btn {
511
+ width: 100%;
512
+ border-radius: var(--border-radius-md) !important;
513
+ margin-bottom: var(--spacing-sm);
514
+ }
515
+
516
+ .feature-card {
517
+ padding: var(--spacing-md);
518
+ }
519
+ }
frontend/index.html ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>AI Document & Image Analysis</title>
7
+ <link rel="stylesheet" href="css/styles.css">
8
+ <link rel="stylesheet" href="css/animations.css">
9
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ </head>
12
+ <body>
13
+ <header class="header">
14
+ <div class="container">
15
+ <div class="logo">
16
+ <i class="fas fa-brain"></i>
17
+ <h1>AI Document Analyzer</h1>
18
+ </div>
19
+ <nav class="nav">
20
+ <ul>
21
+ <li><a href="#" class="active">Home</a></li>
22
+ <li><a href="#features">Features</a></li>
23
+ <li><a href="#about">About</a></li>
24
+ </ul>
25
+ </nav>
26
+ </div>
27
+ </header>
28
+
29
+ <main>
30
+ <section class="hero">
31
+ <div class="container">
32
+ <div class="hero-content">
33
+ <h1 class="hero-title">Powerful AI Document & Image Analysis</h1>
34
+ <p class="hero-subtitle">Upload documents and images for instant AI-powered analysis, summarization, and interpretation.</p>
35
+ </div>
36
+ </div>
37
+ </section>
38
+
39
+ <section class="upload-section">
40
+ <div class="container">
41
+ <div class="tabs">
42
+ <button class="tab-btn active" data-tab="document">Document Analysis</button>
43
+ <button class="tab-btn" data-tab="image">Image Analysis</button>
44
+ </div>
45
+
46
+ <div class="tab-content active" id="document-tab">
47
+ <div class="upload-container" id="document-upload">
48
+ <div class="upload-area" id="document-drop-area">
49
+ <i class="fas fa-file-alt"></i>
50
+ <h3>Upload Document</h3>
51
+ <p>Drag & drop your document here or click to browse</p>
52
+ <p class="file-types">Supported formats: PDF, DOCX, PPTX, Excel</p>
53
+ <input type="file" id="document-file-input" accept=".pdf,.docx,.pptx,.xlsx,.xls" hidden>
54
+ </div>
55
+
56
+ <div class="analysis-options">
57
+ <h3>Analysis Options</h3>
58
+ <div class="radio-group">
59
+ <div class="radio-option">
60
+ <input type="radio" id="doc-summarize" name="document-analysis" value="summarize" checked>
61
+ <label for="doc-summarize">Summarize Document</label>
62
+ </div>
63
+ </div>
64
+ <button class="analyze-btn" id="analyze-document-btn" disabled>Analyze Document</button>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <div class="tab-content" id="image-tab">
70
+ <div class="upload-container" id="image-upload">
71
+ <div class="upload-area" id="image-drop-area">
72
+ <i class="fas fa-image"></i>
73
+ <h3>Upload Image</h3>
74
+ <p>Drag & drop your image here or click to browse</p>
75
+ <p class="file-types">Supported formats: JPG, JPEG, PNG</p>
76
+ <input type="file" id="image-file-input" accept=".jpg,.jpeg,.png" hidden>
77
+ </div>
78
+
79
+ <div class="analysis-options">
80
+ <h3>Analysis Options</h3>
81
+ <div class="radio-group">
82
+ <div class="radio-option">
83
+ <input type="radio" id="img-caption" name="image-analysis" value="caption" checked>
84
+ <label for="img-caption">Generate Caption</label>
85
+ </div>
86
+ </div>
87
+ <button class="analyze-btn" id="analyze-image-btn" disabled>Analyze Image</button>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="results-container" id="results-container" style="display: none;">
93
+ <div class="results-header">
94
+ <h2>Analysis Results</h2>
95
+ <div class="results-actions">
96
+ <button id="copy-results" class="action-btn"><i class="fas fa-copy"></i> Copy</button>
97
+ <button id="close-results" class="action-btn"><i class="fas fa-times"></i> Close</button>
98
+ </div>
99
+ </div>
100
+ <div class="results-content" id="results-content">
101
+ <!-- Results will be displayed here -->
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </section>
106
+
107
+ <section class="features" id="features">
108
+ <div class="container">
109
+ <h2 class="section-title">Features</h2>
110
+ <div class="features-grid">
111
+ <div class="feature-card">
112
+ <div class="feature-icon">
113
+ <i class="fas fa-file-alt"></i>
114
+ </div>
115
+ <h3>Document Summarization</h3>
116
+ <p>Extract key information from documents and get concise summaries.</p>
117
+ </div>
118
+ <div class="feature-card">
119
+ <div class="feature-icon">
120
+ <i class="fas fa-image"></i>
121
+ </div>
122
+ <h3>Image Captioning</h3>
123
+ <p>Generate descriptive captions for images using AI vision models.</p>
124
+ </div>
125
+ <div class="feature-card">
126
+ <div class="feature-icon">
127
+ <i class="fas fa-bolt"></i>
128
+ </div>
129
+ <h3>Fast Processing</h3>
130
+ <p>Powered by state-of-the-art Hugging Face AI models for quick results.</p>
131
+ </div>
132
+ <div class="feature-card">
133
+ <div class="feature-icon">
134
+ <i class="fas fa-lock"></i>
135
+ </div>
136
+ <h3>Secure Analysis</h3>
137
+ <p>Your files are processed securely and not stored permanently.</p>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </section>
142
+
143
+ <section class="about" id="about">
144
+ <div class="container">
145
+ <h2 class="section-title">About</h2>
146
+ <div class="about-content">
147
+ <p>This AI Document Analyzer uses state-of-the-art models from Hugging Face to provide accurate document summarization and image captioning. Built with Python FastAPI and deployed on Hugging Face Spaces, this tool demonstrates the power of AI in document and image analysis.</p>
148
+ <p>The application leverages powerful language and vision models to extract meaning from your documents and images, providing valuable insights and saving you time.</p>
149
+ </div>
150
+ </div>
151
+ </section>
152
+ </main>
153
+
154
+ <footer class="footer">
155
+ <div class="container">
156
+ <p>&copy; 2025 AI Document Analyzer. Built with <i class="fas fa-heart"></i> and Hugging Face AI.</p>
157
+ </div>
158
+ </footer>
159
+
160
+ <div class="loading-overlay" id="loading-overlay">
161
+ <div class="spinner"></div>
162
+ <p>Processing your file...</p>
163
+ </div>
164
+
165
+ <script src="js/main.js"></script>
166
+ <script src="js/upload.js"></script>
167
+ <script src="js/results.js"></script>
168
+ </body>
169
+ </html>
frontend/js/main.js ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Main JavaScript file for AI Document Analyzer
3
+ */
4
+
5
+ document.addEventListener('DOMContentLoaded', () => {
6
+ // Initialize tabs
7
+ initTabs();
8
+
9
+ // Initialize animations
10
+ initAnimations();
11
+ });
12
+
13
+ /**
14
+ * Initialize tab functionality
15
+ */
16
+ function initTabs() {
17
+ const tabBtns = document.querySelectorAll('.tab-btn');
18
+ const tabContents = document.querySelectorAll('.tab-content');
19
+
20
+ tabBtns.forEach(btn => {
21
+ btn.addEventListener('click', () => {
22
+ // Remove active class from all buttons and contents
23
+ tabBtns.forEach(b => b.classList.remove('active'));
24
+ tabContents.forEach(c => c.classList.remove('active'));
25
+
26
+ // Add active class to clicked button and corresponding content
27
+ btn.classList.add('active');
28
+ const tabId = `${btn.dataset.tab}-tab`;
29
+ document.getElementById(tabId).classList.add('active');
30
+ });
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Initialize animations
36
+ */
37
+ function initAnimations() {
38
+ // Intersection Observer for scroll animations
39
+ const observer = new IntersectionObserver((entries) => {
40
+ entries.forEach(entry => {
41
+ if (entry.isIntersecting) {
42
+ entry.target.classList.add('appear');
43
+ }
44
+ });
45
+ }, {
46
+ threshold: 0.2
47
+ });
48
+
49
+ // Observe elements that should animate on scroll
50
+ const animatedElements = document.querySelectorAll('.section-title, .feature-card, .about-content p');
51
+ animatedElements.forEach(element => {
52
+ observer.observe(element);
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Show loading overlay
58
+ */
59
+ function showLoading() {
60
+ const loadingOverlay = document.getElementById('loading-overlay');
61
+ loadingOverlay.style.display = 'flex';
62
+ }
63
+
64
+ /**
65
+ * Hide loading overlay
66
+ */
67
+ function hideLoading() {
68
+ const loadingOverlay = document.getElementById('loading-overlay');
69
+ loadingOverlay.style.display = 'none';
70
+ }
71
+
72
+ /**
73
+ * Display error message
74
+ * @param {string} message - The error message to display
75
+ */
76
+ function showError(message) {
77
+ // Create error notification
78
+ const notification = document.createElement('div');
79
+ notification.className = 'error-notification fade-in';
80
+ notification.innerHTML = `
81
+ <div class="error-icon"><i class="fas fa-exclamation-circle"></i></div>
82
+ <div class="error-message">${message}</div>
83
+ `;
84
+
85
+ // Add to DOM
86
+ document.body.appendChild(notification);
87
+
88
+ // Remove after delay
89
+ setTimeout(() => {
90
+ notification.classList.add('fade-out');
91
+ setTimeout(() => {
92
+ document.body.removeChild(notification);
93
+ }, 500);
94
+ }, 4000);
95
+ }
96
+
97
+ /**
98
+ * Format file size in human-readable format
99
+ * @param {number} bytes - File size in bytes
100
+ * @returns {string} Formatted file size
101
+ */
102
+ function formatFileSize(bytes) {
103
+ if (bytes === 0) return '0 Bytes';
104
+
105
+ const k = 1024;
106
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
107
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
108
+
109
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
110
+ }
111
+
112
+ /**
113
+ * Get file extension from filename
114
+ * @param {string} filename - The name of the file
115
+ * @returns {string} The file extension
116
+ */
117
+ function getFileExtension(filename) {
118
+ return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2).toLowerCase();
119
+ }
120
+
121
+ /**
122
+ * Check if file type is allowed
123
+ * @param {string} fileType - The type of file
124
+ * @param {string} allowedTypes - Comma-separated list of allowed types
125
+ * @returns {boolean} Whether the file type is allowed
126
+ */
127
+ function isFileTypeAllowed(filename, allowedTypes) {
128
+ const extension = getFileExtension(filename);
129
+ const allowedExtensions = allowedTypes.split(',').map(type => type.trim().replace('.', '').toLowerCase());
130
+
131
+ return allowedExtensions.includes(extension);
132
+ }
frontend/js/results.js ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Results handling for AI Document Analyzer
3
+ */
4
+
5
+ document.addEventListener('DOMContentLoaded', () => {
6
+ // Initialize results functionality
7
+ initResults();
8
+ });
9
+
10
+ /**
11
+ * Initialize results functionality
12
+ */
13
+ function initResults() {
14
+ const resultsContainer = document.getElementById('results-container');
15
+ const resultsContent = document.getElementById('results-content');
16
+ const copyBtn = document.getElementById('copy-results');
17
+ const closeBtn = document.getElementById('close-results');
18
+
19
+ // Copy results
20
+ copyBtn.addEventListener('click', () => {
21
+ const text = resultsContent.textContent;
22
+ navigator.clipboard.writeText(text)
23
+ .then(() => {
24
+ // Show copy success animation
25
+ copyBtn.innerHTML = '<i class="fas fa-check"></i> Copied!';
26
+ setTimeout(() => {
27
+ copyBtn.innerHTML = '<i class="fas fa-copy"></i> Copy';
28
+ }, 2000);
29
+ })
30
+ .catch(err => {
31
+ showError('Failed to copy: ' + err);
32
+ });
33
+ });
34
+
35
+ // Close results
36
+ closeBtn.addEventListener('click', () => {
37
+ resultsContainer.style.display = 'none';
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Display analysis results
43
+ * @param {Object} data - The analysis results data
44
+ */
45
+ function displayResults(data) {
46
+ const resultsContainer = document.getElementById('results-container');
47
+ const resultsContent = document.getElementById('results-content');
48
+
49
+ // Format the results
50
+ let formattedResults = '';
51
+
52
+ // Add file info
53
+ formattedResults += `File: ${data.filename}\n`;
54
+ formattedResults += `Analysis Type: ${formatAnalysisType(data.analysis_type)}\n\n`;
55
+
56
+ // Add result
57
+ formattedResults += `${data.result}\n`;
58
+
59
+ // Set content
60
+ resultsContent.textContent = formattedResults;
61
+
62
+ // Show results container with animation
63
+ resultsContainer.style.display = 'block';
64
+ resultsContainer.classList.add('slide-up');
65
+
66
+ // Scroll to results
67
+ resultsContainer.scrollIntoView({ behavior: 'smooth' });
68
+ }
69
+
70
+ /**
71
+ * Format analysis type for display
72
+ * @param {string} type - The analysis type
73
+ * @returns {string} Formatted analysis type
74
+ */
75
+ function formatAnalysisType(type) {
76
+ const typeMap = {
77
+ 'summarize': 'Document Summarization',
78
+ 'caption': 'Image Captioning'
79
+ };
80
+
81
+ return typeMap[type] || type;
82
+ }
frontend/js/upload.js ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Upload functionality for AI Document Analyzer
3
+ */
4
+
5
+ document.addEventListener('DOMContentLoaded', () => {
6
+ // Initialize document upload
7
+ initDocumentUpload();
8
+
9
+ // Initialize image upload
10
+ initImageUpload();
11
+ });
12
+
13
+ /**
14
+ * Initialize document upload functionality
15
+ */
16
+ function initDocumentUpload() {
17
+ const dropArea = document.getElementById('document-drop-area');
18
+ const fileInput = document.getElementById('document-file-input');
19
+ const analyzeBtn = document.getElementById('analyze-document-btn');
20
+
21
+ let selectedFile = null;
22
+
23
+ // Handle drag and drop events
24
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
25
+ dropArea.addEventListener(eventName, (e) => {
26
+ e.preventDefault();
27
+ e.stopPropagation();
28
+ });
29
+ });
30
+
31
+ // Add dragover class
32
+ dropArea.addEventListener('dragenter', () => {
33
+ dropArea.classList.add('dragover');
34
+ });
35
+
36
+ dropArea.addEventListener('dragover', () => {
37
+ dropArea.classList.add('dragover');
38
+ });
39
+
40
+ // Remove dragover class
41
+ dropArea.addEventListener('dragleave', () => {
42
+ dropArea.classList.remove('dragover');
43
+ });
44
+
45
+ // Handle file drop
46
+ dropArea.addEventListener('drop', (e) => {
47
+ dropArea.classList.remove('dragover');
48
+ const file = e.dataTransfer.files[0];
49
+ handleDocumentFile(file);
50
+ });
51
+
52
+ // Handle click to browse
53
+ dropArea.addEventListener('click', () => {
54
+ fileInput.click();
55
+ });
56
+
57
+ // Handle file selection
58
+ fileInput.addEventListener('change', () => {
59
+ const file = fileInput.files[0];
60
+ handleDocumentFile(file);
61
+ });
62
+
63
+ // Handle analyze button click
64
+ analyzeBtn.addEventListener('click', () => {
65
+ if (selectedFile) {
66
+ const analysisType = document.querySelector('input[name="document-analysis"]:checked').value;
67
+ uploadDocumentForAnalysis(selectedFile, analysisType);
68
+ }
69
+ });
70
+
71
+ /**
72
+ * Handle document file selection
73
+ * @param {File} file - The selected file
74
+ */
75
+ function handleDocumentFile(file) {
76
+ if (!file) return;
77
+
78
+ // Check if file type is allowed
79
+ const allowedTypes = '.pdf,.docx,.pptx,.xlsx,.xls';
80
+ if (!isFileTypeAllowed(file.name, allowedTypes)) {
81
+ showError('File type not supported. Please upload a PDF, DOCX, PPTX, or Excel file.');
82
+ return;
83
+ }
84
+
85
+ // Update UI to show selected file
86
+ dropArea.classList.add('file-selected');
87
+ dropArea.innerHTML = `
88
+ <i class="fas fa-file-alt"></i>
89
+ <h3>${file.name}</h3>
90
+ <p>${formatFileSize(file.size)}</p>
91
+ <p class="file-types">Click to change file</p>
92
+ `;
93
+
94
+ // Enable analyze button
95
+ analyzeBtn.disabled = false;
96
+
97
+ // Store selected file
98
+ selectedFile = file;
99
+ }
100
+
101
+ /**
102
+ * Upload document for analysis
103
+ * @param {File} file - The document file to analyze
104
+ * @param {string} analysisType - The type of analysis to perform
105
+ */
106
+ function uploadDocumentForAnalysis(file, analysisType) {
107
+ showLoading();
108
+
109
+ const formData = new FormData();
110
+ formData.append('file', file);
111
+ formData.append('analysis_type', analysisType);
112
+
113
+ fetch('/api/analyze-document', {
114
+ method: 'POST',
115
+ body: formData
116
+ })
117
+ .then(response => {
118
+ if (!response.ok) {
119
+ return response.json().then(data => {
120
+ throw new Error(data.detail || 'Error analyzing document');
121
+ });
122
+ }
123
+ return response.json();
124
+ })
125
+ .then(data => {
126
+ hideLoading();
127
+ displayResults(data);
128
+ })
129
+ .catch(error => {
130
+ hideLoading();
131
+ showError(error.message);
132
+ });
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Initialize image upload functionality
138
+ */
139
+ function initImageUpload() {
140
+ const dropArea = document.getElementById('image-drop-area');
141
+ const fileInput = document.getElementById('image-file-input');
142
+ const analyzeBtn = document.getElementById('analyze-image-btn');
143
+
144
+ let selectedFile = null;
145
+
146
+ // Handle drag and drop events
147
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
148
+ dropArea.addEventListener(eventName, (e) => {
149
+ e.preventDefault();
150
+ e.stopPropagation();
151
+ });
152
+ });
153
+
154
+ // Add dragover class
155
+ dropArea.addEventListener('dragenter', () => {
156
+ dropArea.classList.add('dragover');
157
+ });
158
+
159
+ dropArea.addEventListener('dragover', () => {
160
+ dropArea.classList.add('dragover');
161
+ });
162
+
163
+ // Remove dragover class
164
+ dropArea.addEventListener('dragleave', () => {
165
+ dropArea.classList.remove('dragover');
166
+ });
167
+
168
+ // Handle file drop
169
+ dropArea.addEventListener('drop', (e) => {
170
+ dropArea.classList.remove('dragover');
171
+ const file = e.dataTransfer.files[0];
172
+ handleImageFile(file);
173
+ });
174
+
175
+ // Handle click to browse
176
+ dropArea.addEventListener('click', () => {
177
+ fileInput.click();
178
+ });
179
+
180
+ // Handle file selection
181
+ fileInput.addEventListener('change', () => {
182
+ const file = fileInput.files[0];
183
+ handleImageFile(file);
184
+ });
185
+
186
+ // Handle analyze button click
187
+ analyzeBtn.addEventListener('click', () => {
188
+ if (selectedFile) {
189
+ const analysisType = document.querySelector('input[name="image-analysis"]:checked').value;
190
+ uploadImageForAnalysis(selectedFile, analysisType);
191
+ }
192
+ });
193
+
194
+ /**
195
+ * Handle image file selection
196
+ * @param {File} file - The selected file
197
+ */
198
+ function handleImageFile(file) {
199
+ if (!file) return;
200
+
201
+ // Check if file type is allowed
202
+ const allowedTypes = '.jpg,.jpeg,.png';
203
+ if (!isFileTypeAllowed(file.name, allowedTypes)) {
204
+ showError('File type not supported. Please upload a JPG, JPEG, or PNG file.');
205
+ return;
206
+ }
207
+
208
+ // Create a preview of the image
209
+ const reader = new FileReader();
210
+ reader.onload = function(e) {
211
+ dropArea.classList.add('file-selected');
212
+ dropArea.innerHTML = `
213
+ <img src="${e.target.result}" alt="Preview" style="max-width: 100%; max-height: 200px; margin-bottom: 16px;">
214
+ <h3>${file.name}</h3>
215
+ <p>${formatFileSize(file.size)}</p>
216
+ <p class="file-types">Click to change image</p>
217
+ `;
218
+ };
219
+ reader.readAsDataURL(file);
220
+
221
+ // Enable analyze button
222
+ analyzeBtn.disabled = false;
223
+
224
+ // Store selected file
225
+ selectedFile = file;
226
+ }
227
+
228
+ /**
229
+ * Upload image for analysis
230
+ * @param {File} file - The image file to analyze
231
+ * @param {string} analysisType - The type of analysis to perform
232
+ */
233
+ function uploadImageForAnalysis(file, analysisType) {
234
+ showLoading();
235
+
236
+ const formData = new FormData();
237
+ formData.append('file', file);
238
+ formData.append('analysis_type', analysisType);
239
+
240
+ fetch('/api/analyze-image', {
241
+ method: 'POST',
242
+ body: formData
243
+ })
244
+ .then(response => {
245
+ if (!response.ok) {
246
+ return response.json().then(data => {
247
+ throw new Error(data.detail || 'Error analyzing image');
248
+ });
249
+ }
250
+ return response.json();
251
+ })
252
+ .then(data => {
253
+ hideLoading();
254
+ displayResults(data);
255
+ })
256
+ .catch(error => {
257
+ hideLoading();
258
+ showError(error.message);
259
+ });
260
+ }
261
+ }
index.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>AI Document Analysis Web Application</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/main.js"></script>
12
+ </body>
13
+ </html>
javascript.svg ADDED
main.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import './style.css'
2
+ import javascriptLogo from './javascript.svg'
3
+ import viteLogo from '/vite.svg'
4
+ import { setupCounter } from './counter.js'
5
+
6
+ document.querySelector('#app').innerHTML = `
7
+ <div>
8
+ <a href="https://vitejs.dev" target="_blank">
9
+ <img src="${viteLogo}" class="logo" alt="Vite logo" />
10
+ </a>
11
+ <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank">
12
+ <img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" />
13
+ </a>
14
+ <h1>Hello Vite!</h1>
15
+ <div class="card">
16
+ <button id="counter" type="button"></button>
17
+ </div>
18
+ <p class="read-the-docs">
19
+ Click on the Vite logo to learn more
20
+ </p>
21
+ </div>
22
+ `
23
+
24
+ setupCounter(document.querySelector('#counter'))
package-lock.json ADDED
@@ -0,0 +1,912 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vite-starter",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "vite-starter",
9
+ "version": "0.0.0",
10
+ "devDependencies": {
11
+ "vite": "^5.4.2"
12
+ }
13
+ },
14
+ "node_modules/@esbuild/aix-ppc64": {
15
+ "version": "0.21.5",
16
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
17
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
18
+ "cpu": [
19
+ "ppc64"
20
+ ],
21
+ "dev": true,
22
+ "license": "MIT",
23
+ "optional": true,
24
+ "os": [
25
+ "aix"
26
+ ],
27
+ "engines": {
28
+ "node": ">=12"
29
+ }
30
+ },
31
+ "node_modules/@esbuild/android-arm": {
32
+ "version": "0.21.5",
33
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
34
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
35
+ "cpu": [
36
+ "arm"
37
+ ],
38
+ "dev": true,
39
+ "license": "MIT",
40
+ "optional": true,
41
+ "os": [
42
+ "android"
43
+ ],
44
+ "engines": {
45
+ "node": ">=12"
46
+ }
47
+ },
48
+ "node_modules/@esbuild/android-arm64": {
49
+ "version": "0.21.5",
50
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
51
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
52
+ "cpu": [
53
+ "arm64"
54
+ ],
55
+ "dev": true,
56
+ "license": "MIT",
57
+ "optional": true,
58
+ "os": [
59
+ "android"
60
+ ],
61
+ "engines": {
62
+ "node": ">=12"
63
+ }
64
+ },
65
+ "node_modules/@esbuild/android-x64": {
66
+ "version": "0.21.5",
67
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
68
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
69
+ "cpu": [
70
+ "x64"
71
+ ],
72
+ "dev": true,
73
+ "license": "MIT",
74
+ "optional": true,
75
+ "os": [
76
+ "android"
77
+ ],
78
+ "engines": {
79
+ "node": ">=12"
80
+ }
81
+ },
82
+ "node_modules/@esbuild/darwin-arm64": {
83
+ "version": "0.21.5",
84
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
85
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
86
+ "cpu": [
87
+ "arm64"
88
+ ],
89
+ "dev": true,
90
+ "license": "MIT",
91
+ "optional": true,
92
+ "os": [
93
+ "darwin"
94
+ ],
95
+ "engines": {
96
+ "node": ">=12"
97
+ }
98
+ },
99
+ "node_modules/@esbuild/darwin-x64": {
100
+ "version": "0.21.5",
101
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
102
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
103
+ "cpu": [
104
+ "x64"
105
+ ],
106
+ "dev": true,
107
+ "license": "MIT",
108
+ "optional": true,
109
+ "os": [
110
+ "darwin"
111
+ ],
112
+ "engines": {
113
+ "node": ">=12"
114
+ }
115
+ },
116
+ "node_modules/@esbuild/freebsd-arm64": {
117
+ "version": "0.21.5",
118
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
119
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
120
+ "cpu": [
121
+ "arm64"
122
+ ],
123
+ "dev": true,
124
+ "license": "MIT",
125
+ "optional": true,
126
+ "os": [
127
+ "freebsd"
128
+ ],
129
+ "engines": {
130
+ "node": ">=12"
131
+ }
132
+ },
133
+ "node_modules/@esbuild/freebsd-x64": {
134
+ "version": "0.21.5",
135
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
136
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
137
+ "cpu": [
138
+ "x64"
139
+ ],
140
+ "dev": true,
141
+ "license": "MIT",
142
+ "optional": true,
143
+ "os": [
144
+ "freebsd"
145
+ ],
146
+ "engines": {
147
+ "node": ">=12"
148
+ }
149
+ },
150
+ "node_modules/@esbuild/linux-arm": {
151
+ "version": "0.21.5",
152
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
153
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
154
+ "cpu": [
155
+ "arm"
156
+ ],
157
+ "dev": true,
158
+ "license": "MIT",
159
+ "optional": true,
160
+ "os": [
161
+ "linux"
162
+ ],
163
+ "engines": {
164
+ "node": ">=12"
165
+ }
166
+ },
167
+ "node_modules/@esbuild/linux-arm64": {
168
+ "version": "0.21.5",
169
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
170
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
171
+ "cpu": [
172
+ "arm64"
173
+ ],
174
+ "dev": true,
175
+ "license": "MIT",
176
+ "optional": true,
177
+ "os": [
178
+ "linux"
179
+ ],
180
+ "engines": {
181
+ "node": ">=12"
182
+ }
183
+ },
184
+ "node_modules/@esbuild/linux-ia32": {
185
+ "version": "0.21.5",
186
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
187
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
188
+ "cpu": [
189
+ "ia32"
190
+ ],
191
+ "dev": true,
192
+ "license": "MIT",
193
+ "optional": true,
194
+ "os": [
195
+ "linux"
196
+ ],
197
+ "engines": {
198
+ "node": ">=12"
199
+ }
200
+ },
201
+ "node_modules/@esbuild/linux-loong64": {
202
+ "version": "0.21.5",
203
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
204
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
205
+ "cpu": [
206
+ "loong64"
207
+ ],
208
+ "dev": true,
209
+ "license": "MIT",
210
+ "optional": true,
211
+ "os": [
212
+ "linux"
213
+ ],
214
+ "engines": {
215
+ "node": ">=12"
216
+ }
217
+ },
218
+ "node_modules/@esbuild/linux-mips64el": {
219
+ "version": "0.21.5",
220
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
221
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
222
+ "cpu": [
223
+ "mips64el"
224
+ ],
225
+ "dev": true,
226
+ "license": "MIT",
227
+ "optional": true,
228
+ "os": [
229
+ "linux"
230
+ ],
231
+ "engines": {
232
+ "node": ">=12"
233
+ }
234
+ },
235
+ "node_modules/@esbuild/linux-ppc64": {
236
+ "version": "0.21.5",
237
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
238
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
239
+ "cpu": [
240
+ "ppc64"
241
+ ],
242
+ "dev": true,
243
+ "license": "MIT",
244
+ "optional": true,
245
+ "os": [
246
+ "linux"
247
+ ],
248
+ "engines": {
249
+ "node": ">=12"
250
+ }
251
+ },
252
+ "node_modules/@esbuild/linux-riscv64": {
253
+ "version": "0.21.5",
254
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
255
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
256
+ "cpu": [
257
+ "riscv64"
258
+ ],
259
+ "dev": true,
260
+ "license": "MIT",
261
+ "optional": true,
262
+ "os": [
263
+ "linux"
264
+ ],
265
+ "engines": {
266
+ "node": ">=12"
267
+ }
268
+ },
269
+ "node_modules/@esbuild/linux-s390x": {
270
+ "version": "0.21.5",
271
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
272
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
273
+ "cpu": [
274
+ "s390x"
275
+ ],
276
+ "dev": true,
277
+ "license": "MIT",
278
+ "optional": true,
279
+ "os": [
280
+ "linux"
281
+ ],
282
+ "engines": {
283
+ "node": ">=12"
284
+ }
285
+ },
286
+ "node_modules/@esbuild/linux-x64": {
287
+ "version": "0.21.5",
288
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
289
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
290
+ "cpu": [
291
+ "x64"
292
+ ],
293
+ "dev": true,
294
+ "license": "MIT",
295
+ "optional": true,
296
+ "os": [
297
+ "linux"
298
+ ],
299
+ "engines": {
300
+ "node": ">=12"
301
+ }
302
+ },
303
+ "node_modules/@esbuild/netbsd-x64": {
304
+ "version": "0.21.5",
305
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
306
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
307
+ "cpu": [
308
+ "x64"
309
+ ],
310
+ "dev": true,
311
+ "license": "MIT",
312
+ "optional": true,
313
+ "os": [
314
+ "netbsd"
315
+ ],
316
+ "engines": {
317
+ "node": ">=12"
318
+ }
319
+ },
320
+ "node_modules/@esbuild/openbsd-x64": {
321
+ "version": "0.21.5",
322
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
323
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
324
+ "cpu": [
325
+ "x64"
326
+ ],
327
+ "dev": true,
328
+ "license": "MIT",
329
+ "optional": true,
330
+ "os": [
331
+ "openbsd"
332
+ ],
333
+ "engines": {
334
+ "node": ">=12"
335
+ }
336
+ },
337
+ "node_modules/@esbuild/sunos-x64": {
338
+ "version": "0.21.5",
339
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
340
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
341
+ "cpu": [
342
+ "x64"
343
+ ],
344
+ "dev": true,
345
+ "license": "MIT",
346
+ "optional": true,
347
+ "os": [
348
+ "sunos"
349
+ ],
350
+ "engines": {
351
+ "node": ">=12"
352
+ }
353
+ },
354
+ "node_modules/@esbuild/win32-arm64": {
355
+ "version": "0.21.5",
356
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
357
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
358
+ "cpu": [
359
+ "arm64"
360
+ ],
361
+ "dev": true,
362
+ "license": "MIT",
363
+ "optional": true,
364
+ "os": [
365
+ "win32"
366
+ ],
367
+ "engines": {
368
+ "node": ">=12"
369
+ }
370
+ },
371
+ "node_modules/@esbuild/win32-ia32": {
372
+ "version": "0.21.5",
373
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
374
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
375
+ "cpu": [
376
+ "ia32"
377
+ ],
378
+ "dev": true,
379
+ "license": "MIT",
380
+ "optional": true,
381
+ "os": [
382
+ "win32"
383
+ ],
384
+ "engines": {
385
+ "node": ">=12"
386
+ }
387
+ },
388
+ "node_modules/@esbuild/win32-x64": {
389
+ "version": "0.21.5",
390
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
391
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
392
+ "cpu": [
393
+ "x64"
394
+ ],
395
+ "dev": true,
396
+ "license": "MIT",
397
+ "optional": true,
398
+ "os": [
399
+ "win32"
400
+ ],
401
+ "engines": {
402
+ "node": ">=12"
403
+ }
404
+ },
405
+ "node_modules/@rollup/rollup-android-arm-eabi": {
406
+ "version": "4.40.0",
407
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
408
+ "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
409
+ "cpu": [
410
+ "arm"
411
+ ],
412
+ "dev": true,
413
+ "license": "MIT",
414
+ "optional": true,
415
+ "os": [
416
+ "android"
417
+ ]
418
+ },
419
+ "node_modules/@rollup/rollup-android-arm64": {
420
+ "version": "4.40.0",
421
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz",
422
+ "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
423
+ "cpu": [
424
+ "arm64"
425
+ ],
426
+ "dev": true,
427
+ "license": "MIT",
428
+ "optional": true,
429
+ "os": [
430
+ "android"
431
+ ]
432
+ },
433
+ "node_modules/@rollup/rollup-darwin-arm64": {
434
+ "version": "4.40.0",
435
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz",
436
+ "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
437
+ "cpu": [
438
+ "arm64"
439
+ ],
440
+ "dev": true,
441
+ "license": "MIT",
442
+ "optional": true,
443
+ "os": [
444
+ "darwin"
445
+ ]
446
+ },
447
+ "node_modules/@rollup/rollup-darwin-x64": {
448
+ "version": "4.40.0",
449
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz",
450
+ "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
451
+ "cpu": [
452
+ "x64"
453
+ ],
454
+ "dev": true,
455
+ "license": "MIT",
456
+ "optional": true,
457
+ "os": [
458
+ "darwin"
459
+ ]
460
+ },
461
+ "node_modules/@rollup/rollup-freebsd-arm64": {
462
+ "version": "4.40.0",
463
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz",
464
+ "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
465
+ "cpu": [
466
+ "arm64"
467
+ ],
468
+ "dev": true,
469
+ "license": "MIT",
470
+ "optional": true,
471
+ "os": [
472
+ "freebsd"
473
+ ]
474
+ },
475
+ "node_modules/@rollup/rollup-freebsd-x64": {
476
+ "version": "4.40.0",
477
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz",
478
+ "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
479
+ "cpu": [
480
+ "x64"
481
+ ],
482
+ "dev": true,
483
+ "license": "MIT",
484
+ "optional": true,
485
+ "os": [
486
+ "freebsd"
487
+ ]
488
+ },
489
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
490
+ "version": "4.40.0",
491
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz",
492
+ "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
493
+ "cpu": [
494
+ "arm"
495
+ ],
496
+ "dev": true,
497
+ "license": "MIT",
498
+ "optional": true,
499
+ "os": [
500
+ "linux"
501
+ ]
502
+ },
503
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
504
+ "version": "4.40.0",
505
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz",
506
+ "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
507
+ "cpu": [
508
+ "arm"
509
+ ],
510
+ "dev": true,
511
+ "license": "MIT",
512
+ "optional": true,
513
+ "os": [
514
+ "linux"
515
+ ]
516
+ },
517
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
518
+ "version": "4.40.0",
519
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz",
520
+ "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
521
+ "cpu": [
522
+ "arm64"
523
+ ],
524
+ "dev": true,
525
+ "license": "MIT",
526
+ "optional": true,
527
+ "os": [
528
+ "linux"
529
+ ]
530
+ },
531
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
532
+ "version": "4.40.0",
533
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz",
534
+ "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
535
+ "cpu": [
536
+ "arm64"
537
+ ],
538
+ "dev": true,
539
+ "license": "MIT",
540
+ "optional": true,
541
+ "os": [
542
+ "linux"
543
+ ]
544
+ },
545
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
546
+ "version": "4.40.0",
547
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz",
548
+ "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
549
+ "cpu": [
550
+ "loong64"
551
+ ],
552
+ "dev": true,
553
+ "license": "MIT",
554
+ "optional": true,
555
+ "os": [
556
+ "linux"
557
+ ]
558
+ },
559
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
560
+ "version": "4.40.0",
561
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz",
562
+ "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
563
+ "cpu": [
564
+ "ppc64"
565
+ ],
566
+ "dev": true,
567
+ "license": "MIT",
568
+ "optional": true,
569
+ "os": [
570
+ "linux"
571
+ ]
572
+ },
573
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
574
+ "version": "4.40.0",
575
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz",
576
+ "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
577
+ "cpu": [
578
+ "riscv64"
579
+ ],
580
+ "dev": true,
581
+ "license": "MIT",
582
+ "optional": true,
583
+ "os": [
584
+ "linux"
585
+ ]
586
+ },
587
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
588
+ "version": "4.40.0",
589
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
590
+ "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
591
+ "cpu": [
592
+ "riscv64"
593
+ ],
594
+ "dev": true,
595
+ "license": "MIT",
596
+ "optional": true,
597
+ "os": [
598
+ "linux"
599
+ ]
600
+ },
601
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
602
+ "version": "4.40.0",
603
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz",
604
+ "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
605
+ "cpu": [
606
+ "s390x"
607
+ ],
608
+ "dev": true,
609
+ "license": "MIT",
610
+ "optional": true,
611
+ "os": [
612
+ "linux"
613
+ ]
614
+ },
615
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
616
+ "version": "4.40.0",
617
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
618
+ "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
619
+ "cpu": [
620
+ "x64"
621
+ ],
622
+ "dev": true,
623
+ "license": "MIT",
624
+ "optional": true,
625
+ "os": [
626
+ "linux"
627
+ ]
628
+ },
629
+ "node_modules/@rollup/rollup-linux-x64-musl": {
630
+ "version": "4.40.0",
631
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz",
632
+ "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
633
+ "cpu": [
634
+ "x64"
635
+ ],
636
+ "dev": true,
637
+ "license": "MIT",
638
+ "optional": true,
639
+ "os": [
640
+ "linux"
641
+ ]
642
+ },
643
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
644
+ "version": "4.40.0",
645
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz",
646
+ "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
647
+ "cpu": [
648
+ "arm64"
649
+ ],
650
+ "dev": true,
651
+ "license": "MIT",
652
+ "optional": true,
653
+ "os": [
654
+ "win32"
655
+ ]
656
+ },
657
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
658
+ "version": "4.40.0",
659
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz",
660
+ "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
661
+ "cpu": [
662
+ "ia32"
663
+ ],
664
+ "dev": true,
665
+ "license": "MIT",
666
+ "optional": true,
667
+ "os": [
668
+ "win32"
669
+ ]
670
+ },
671
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
672
+ "version": "4.40.0",
673
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz",
674
+ "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
675
+ "cpu": [
676
+ "x64"
677
+ ],
678
+ "dev": true,
679
+ "license": "MIT",
680
+ "optional": true,
681
+ "os": [
682
+ "win32"
683
+ ]
684
+ },
685
+ "node_modules/@types/estree": {
686
+ "version": "1.0.7",
687
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
688
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
689
+ "dev": true,
690
+ "license": "MIT"
691
+ },
692
+ "node_modules/esbuild": {
693
+ "version": "0.21.5",
694
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
695
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
696
+ "dev": true,
697
+ "hasInstallScript": true,
698
+ "license": "MIT",
699
+ "bin": {
700
+ "esbuild": "bin/esbuild"
701
+ },
702
+ "engines": {
703
+ "node": ">=12"
704
+ },
705
+ "optionalDependencies": {
706
+ "@esbuild/aix-ppc64": "0.21.5",
707
+ "@esbuild/android-arm": "0.21.5",
708
+ "@esbuild/android-arm64": "0.21.5",
709
+ "@esbuild/android-x64": "0.21.5",
710
+ "@esbuild/darwin-arm64": "0.21.5",
711
+ "@esbuild/darwin-x64": "0.21.5",
712
+ "@esbuild/freebsd-arm64": "0.21.5",
713
+ "@esbuild/freebsd-x64": "0.21.5",
714
+ "@esbuild/linux-arm": "0.21.5",
715
+ "@esbuild/linux-arm64": "0.21.5",
716
+ "@esbuild/linux-ia32": "0.21.5",
717
+ "@esbuild/linux-loong64": "0.21.5",
718
+ "@esbuild/linux-mips64el": "0.21.5",
719
+ "@esbuild/linux-ppc64": "0.21.5",
720
+ "@esbuild/linux-riscv64": "0.21.5",
721
+ "@esbuild/linux-s390x": "0.21.5",
722
+ "@esbuild/linux-x64": "0.21.5",
723
+ "@esbuild/netbsd-x64": "0.21.5",
724
+ "@esbuild/openbsd-x64": "0.21.5",
725
+ "@esbuild/sunos-x64": "0.21.5",
726
+ "@esbuild/win32-arm64": "0.21.5",
727
+ "@esbuild/win32-ia32": "0.21.5",
728
+ "@esbuild/win32-x64": "0.21.5"
729
+ }
730
+ },
731
+ "node_modules/fsevents": {
732
+ "version": "2.3.3",
733
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
734
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
735
+ "dev": true,
736
+ "hasInstallScript": true,
737
+ "license": "MIT",
738
+ "optional": true,
739
+ "os": [
740
+ "darwin"
741
+ ],
742
+ "engines": {
743
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
744
+ }
745
+ },
746
+ "node_modules/nanoid": {
747
+ "version": "3.3.11",
748
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
749
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
750
+ "dev": true,
751
+ "funding": [
752
+ {
753
+ "type": "github",
754
+ "url": "https://github.com/sponsors/ai"
755
+ }
756
+ ],
757
+ "license": "MIT",
758
+ "bin": {
759
+ "nanoid": "bin/nanoid.cjs"
760
+ },
761
+ "engines": {
762
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
763
+ }
764
+ },
765
+ "node_modules/picocolors": {
766
+ "version": "1.1.1",
767
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
768
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
769
+ "dev": true,
770
+ "license": "ISC"
771
+ },
772
+ "node_modules/postcss": {
773
+ "version": "8.5.3",
774
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
775
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
776
+ "dev": true,
777
+ "funding": [
778
+ {
779
+ "type": "opencollective",
780
+ "url": "https://opencollective.com/postcss/"
781
+ },
782
+ {
783
+ "type": "tidelift",
784
+ "url": "https://tidelift.com/funding/github/npm/postcss"
785
+ },
786
+ {
787
+ "type": "github",
788
+ "url": "https://github.com/sponsors/ai"
789
+ }
790
+ ],
791
+ "license": "MIT",
792
+ "dependencies": {
793
+ "nanoid": "^3.3.8",
794
+ "picocolors": "^1.1.1",
795
+ "source-map-js": "^1.2.1"
796
+ },
797
+ "engines": {
798
+ "node": "^10 || ^12 || >=14"
799
+ }
800
+ },
801
+ "node_modules/rollup": {
802
+ "version": "4.40.0",
803
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
804
+ "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
805
+ "dev": true,
806
+ "license": "MIT",
807
+ "dependencies": {
808
+ "@types/estree": "1.0.7"
809
+ },
810
+ "bin": {
811
+ "rollup": "dist/bin/rollup"
812
+ },
813
+ "engines": {
814
+ "node": ">=18.0.0",
815
+ "npm": ">=8.0.0"
816
+ },
817
+ "optionalDependencies": {
818
+ "@rollup/rollup-android-arm-eabi": "4.40.0",
819
+ "@rollup/rollup-android-arm64": "4.40.0",
820
+ "@rollup/rollup-darwin-arm64": "4.40.0",
821
+ "@rollup/rollup-darwin-x64": "4.40.0",
822
+ "@rollup/rollup-freebsd-arm64": "4.40.0",
823
+ "@rollup/rollup-freebsd-x64": "4.40.0",
824
+ "@rollup/rollup-linux-arm-gnueabihf": "4.40.0",
825
+ "@rollup/rollup-linux-arm-musleabihf": "4.40.0",
826
+ "@rollup/rollup-linux-arm64-gnu": "4.40.0",
827
+ "@rollup/rollup-linux-arm64-musl": "4.40.0",
828
+ "@rollup/rollup-linux-loongarch64-gnu": "4.40.0",
829
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0",
830
+ "@rollup/rollup-linux-riscv64-gnu": "4.40.0",
831
+ "@rollup/rollup-linux-riscv64-musl": "4.40.0",
832
+ "@rollup/rollup-linux-s390x-gnu": "4.40.0",
833
+ "@rollup/rollup-linux-x64-gnu": "4.40.0",
834
+ "@rollup/rollup-linux-x64-musl": "4.40.0",
835
+ "@rollup/rollup-win32-arm64-msvc": "4.40.0",
836
+ "@rollup/rollup-win32-ia32-msvc": "4.40.0",
837
+ "@rollup/rollup-win32-x64-msvc": "4.40.0",
838
+ "fsevents": "~2.3.2"
839
+ }
840
+ },
841
+ "node_modules/source-map-js": {
842
+ "version": "1.2.1",
843
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
844
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
845
+ "dev": true,
846
+ "license": "BSD-3-Clause",
847
+ "engines": {
848
+ "node": ">=0.10.0"
849
+ }
850
+ },
851
+ "node_modules/vite": {
852
+ "version": "5.4.18",
853
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
854
+ "integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==",
855
+ "dev": true,
856
+ "license": "MIT",
857
+ "dependencies": {
858
+ "esbuild": "^0.21.3",
859
+ "postcss": "^8.4.43",
860
+ "rollup": "^4.20.0"
861
+ },
862
+ "bin": {
863
+ "vite": "bin/vite.js"
864
+ },
865
+ "engines": {
866
+ "node": "^18.0.0 || >=20.0.0"
867
+ },
868
+ "funding": {
869
+ "url": "https://github.com/vitejs/vite?sponsor=1"
870
+ },
871
+ "optionalDependencies": {
872
+ "fsevents": "~2.3.3"
873
+ },
874
+ "peerDependencies": {
875
+ "@types/node": "^18.0.0 || >=20.0.0",
876
+ "less": "*",
877
+ "lightningcss": "^1.21.0",
878
+ "sass": "*",
879
+ "sass-embedded": "*",
880
+ "stylus": "*",
881
+ "sugarss": "*",
882
+ "terser": "^5.4.0"
883
+ },
884
+ "peerDependenciesMeta": {
885
+ "@types/node": {
886
+ "optional": true
887
+ },
888
+ "less": {
889
+ "optional": true
890
+ },
891
+ "lightningcss": {
892
+ "optional": true
893
+ },
894
+ "sass": {
895
+ "optional": true
896
+ },
897
+ "sass-embedded": {
898
+ "optional": true
899
+ },
900
+ "stylus": {
901
+ "optional": true
902
+ },
903
+ "sugarss": {
904
+ "optional": true
905
+ },
906
+ "terser": {
907
+ "optional": true
908
+ }
909
+ }
910
+ }
911
+ }
912
+ }
package.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vite-starter",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "devDependencies": {
12
+ "vite": "^5.4.2"
13
+ }
14
+ }
public/vite.svg ADDED
style.css ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ a {
17
+ font-weight: 500;
18
+ color: #646cff;
19
+ text-decoration: inherit;
20
+ }
21
+ a:hover {
22
+ color: #535bf2;
23
+ }
24
+
25
+ body {
26
+ margin: 0;
27
+ display: flex;
28
+ place-items: center;
29
+ min-width: 320px;
30
+ min-height: 100vh;
31
+ }
32
+
33
+ h1 {
34
+ font-size: 3.2em;
35
+ line-height: 1.1;
36
+ }
37
+
38
+ #app {
39
+ max-width: 1280px;
40
+ margin: 0 auto;
41
+ padding: 2rem;
42
+ text-align: center;
43
+ }
44
+
45
+ .logo {
46
+ height: 6em;
47
+ padding: 1.5em;
48
+ will-change: filter;
49
+ transition: filter 300ms;
50
+ }
51
+ .logo:hover {
52
+ filter: drop-shadow(0 0 2em #646cffaa);
53
+ }
54
+ .logo.vanilla:hover {
55
+ filter: drop-shadow(0 0 2em #f7df1eaa);
56
+ }
57
+
58
+ .card {
59
+ padding: 2em;
60
+ }
61
+
62
+ .read-the-docs {
63
+ color: #888;
64
+ }
65
+
66
+ button {
67
+ border-radius: 8px;
68
+ border: 1px solid transparent;
69
+ padding: 0.6em 1.2em;
70
+ font-size: 1em;
71
+ font-weight: 500;
72
+ font-family: inherit;
73
+ background-color: #1a1a1a;
74
+ cursor: pointer;
75
+ transition: border-color 0.25s;
76
+ }
77
+ button:hover {
78
+ border-color: #646cff;
79
+ }
80
+ button:focus,
81
+ button:focus-visible {
82
+ outline: 4px auto -webkit-focus-ring-color;
83
+ }
84
+
85
+ @media (prefers-color-scheme: light) {
86
+ :root {
87
+ color: #213547;
88
+ background-color: #ffffff;
89
+ }
90
+ a:hover {
91
+ color: #747bff;
92
+ }
93
+ button {
94
+ background-color: #f9f9f9;
95
+ }
96
+ }