Pathakkunal commited on
Commit
23ddaac
·
0 Parent(s):

StyleSync AI - Rebranded codebase

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitignore ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Credentials and Secrets
2
+ .env
3
+
4
+ # Python Compiled Files
5
+ __pycache__/
6
+ *.py[cod]
7
+
8
+ # Virtual Environments
9
+ venv/
10
+ .venv/
11
+
12
+ # System Files
13
+ .DS_Store
14
+ Thumbs.db
15
+
16
+ # Logs
17
+ *.log
18
+
19
+ # Editor Directories
20
+ .vscode/
21
+ .idea/
22
+
23
+ *.jpg
24
+
25
+ stitch_merchflow_ai_dashboard.zip
26
+ screen.jpg
27
+ test_image.jpg
28
+ *.zip
29
+ *.jpeg
30
+ *.png
.vite/deps/_metadata.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "hash": "7678463b",
3
+ "configHash": "aaeefbd1",
4
+ "lockfileHash": "e3b0c442",
5
+ "browserHash": "7b5c57bd",
6
+ "optimized": {},
7
+ "chunks": {}
8
+ }
.vite/deps/package.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "type": "module"
3
+ }
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+ WORKDIR /code
3
+ COPY ./requirements.txt /code/requirements.txt
4
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
5
+ COPY . /code
6
+ # Fix permissions for libraries that write to home
7
+ RUN mkdir -p /tmp/home
8
+ ENV HOME=/tmp/home
9
+ # Start the FastAPI server on port 7860 (required by Hugging Face)
10
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: StyleSync AI
3
+ emoji: 👟
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ <div align="center">
11
+
12
+ # 🚀 StyleSync AI
13
+
14
+ ### Autonomous E-Commerce Catalog Intelligence
15
+
16
+ ![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)
17
+ ![FastAPI](https://img.shields.io/badge/FastAPI-005571?style=for-the-badge&logo=fastapi&logoColor=white)
18
+ ![Google Gemini](https://img.shields.io/badge/Gemini_2.5_Flash-4285F4?style=for-the-badge&logo=google&logoColor=white)
19
+ ![Pinecone](https://img.shields.io/badge/Pinecone-000000?style=for-the-badge&logo=pinecone&logoColor=white)
20
+ ![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white)
21
+
22
+ ---
23
+
24
+ *A multi-agent AI pipeline that converts raw product imagery into enterprise-grade, SEO-optimized e-commerce catalogs in seconds.*
25
+
26
+ </div>
27
+
28
+ ---
29
+
30
+ ## 🎯 Core Value Proposition
31
+
32
+ StyleSync AI eliminates the manual bottleneck of product catalog creation. By orchestrating **Computer Vision**, **Retrieval-Augmented Generation (RAG)**, and **Large Language Models** in a seamless autonomous pipeline, it delivers production-ready product listings — from a single image upload — with zero human intervention.
33
+
34
+ ---
35
+
36
+ ## 🏗️ System Architecture
37
+
38
+ The system employs a high-performance, event-driven architecture orchestrated by **FastAPI**:
39
+
40
+ ### 👁️ Visual Analyst Agent
41
+ - **Function**: Zero-shot product image analysis
42
+ - **Process**: Extracts granular visual attributes — dominant colors, material composition, design style, branding elements, and product classification
43
+ - **Engine**: `Gemini 2.5 Flash` via the unified **Google GenAI SDK** (`google-genai`)
44
+
45
+ ### 🧠 Semantic Memory Agent
46
+ - **Function**: RAG-based keyword retrieval with intelligent fallback
47
+ - **Process**: Vectorizes visual attributes to query a high-dimensional index, retrieving historically high-converting SEO keywords and market trends. When the database has no match for a niche, the **Intelligence Fallback** system autonomously generates keywords via Gemini — ensuring **0% empty results**
48
+ - **Engine**: `Pinecone Vector DB` with `gemini-embedding-001` embeddings (768 dimensions)
49
+
50
+ ### ✍️ Writer Agent
51
+ - **Function**: High-conversion copy synthesis
52
+ - **Process**: Fuses visual intelligence with retrieved market data to generate persuasive, conversion-optimized titles, descriptions, and feature bullet points
53
+ - **Engine**: `Meta Llama 3.3 70B` (via Groq Cloud)
54
+
55
+ ### ⚙️ Pipeline Orchestrator
56
+ - **Function**: Async pipeline management & delivery
57
+ - **Process**: Handles non-blocking agent execution, error propagation, and API lifecycle management. Results are delivered instantly through the **Premium Glassmorphism UI**
58
+ - **Engine**: `FastAPI` with async/await architecture
59
+
60
+ ---
61
+
62
+ ## 🖥️ Production Interface
63
+
64
+ StyleSync AI ships with a **Premium Glassmorphism UI** built for instant catalog generation:
65
+
66
+ - 🎨 Frosted-glass aesthetic with dynamic gradient backgrounds
67
+ - 📤 Drag-and-drop image upload with real-time processing feedback
68
+ - 📊 Structured JSON output display for visual data, SEO keywords, and generated listings
69
+ - 📱 Fully responsive design across desktop, tablet, and mobile
70
+
71
+ ---
72
+
73
+ ## 🛠️ Technology Stack
74
+
75
+ | Layer | Technology | Purpose |
76
+ |-------|-----------|---------|
77
+ | **Runtime** | Python 3.10+ | Core language |
78
+ | **Framework** | FastAPI | Async API orchestration |
79
+ | **AI SDK** | `google-genai` (Unified) | Vision & embedding inference |
80
+ | **Vision Model** | Gemini 2.5 Flash | Product image analysis |
81
+ | **Embeddings** | `gemini-embedding-001` | 768-dim vector generation |
82
+ | **Vector DB** | Pinecone (Serverless) | Semantic keyword retrieval |
83
+ | **LLM** | Llama 3.3 70B (Groq) | Copywriting synthesis |
84
+ | **UI** | Glassmorphism / Tailwind CSS | Production dashboard |
85
+ | **Deployment** | Docker / Hugging Face Spaces | Containerized hosting |
86
+
87
+ ---
88
+
89
+ ## 📋 System Updates & Technical Milestones
90
+
91
+ | Date | Milestone |
92
+ |------|-----------|
93
+ | **Feb 2026** | ✅ Full migration from deprecated `google-generativeai` to unified `google-genai` SDK |
94
+ | **Feb 2026** | ✅ Vision model upgraded to `Gemini 2.5 Flash` for industry-leading latency |
95
+ | **Feb 2026** | ✅ Pinecone vector alignment to `768 dimensions` with `gemini-embedding-001` for mathematical precision |
96
+ | **Feb 2026** | ✅ **Intelligence Fallback** system deployed — guarantees 0% empty SEO keyword results |
97
+ | **Feb 2026** | ✅ n8n webhook decoupled — pipeline relies strictly on the Glassmorphism UI for delivery |
98
+ | **Feb 2026** | ✅ Production Glassmorphism dashboard launched |
99
+
100
+ ---
101
+
102
+ ## 🚀 Quick Start
103
+
104
+ ### 1. Environment Configuration
105
+
106
+ Create a `.env` file in the project root with the following keys:
107
+
108
+ ```env
109
+ GEMINI_API_KEY=your_google_genai_api_key
110
+ GROQ_API_KEY=your_groq_cloud_api_key
111
+ PINECONE_API_KEY=your_pinecone_api_key
112
+ ```
113
+
114
+ ### 2. Installation
115
+
116
+ ```bash
117
+ pip install -r requirements.txt
118
+ ```
119
+
120
+ ### 3. Launch
121
+
122
+ ```bash
123
+ python main.py
124
+ ```
125
+
126
+ The production dashboard will be available at `http://localhost:7860`.
127
+
128
+ ---
129
+
130
+ ## 📁 Project Structure
131
+
132
+ ```
133
+ StyleSync-AI/
134
+ ├── main.py # FastAPI orchestrator & pipeline logic
135
+ ├── dashboard.html # Glassmorphism production UI
136
+ ├── Dockerfile # Container deployment config
137
+ ├── requirements.txt # Python dependencies
138
+ ├── agents/
139
+ │ ├── visual_analyst.py # Gemini 2.5 Flash vision agent
140
+ │ ├── memory_agent.py # Pinecone RAG + embedding agent
141
+ │ └── writer_agent.py # Llama 3.3 copywriting agent
142
+ └── .env # Environment variables (not tracked)
143
+ ```
144
+
145
+ ---
146
+
147
+ <div align="center">
148
+
149
+ **StyleSync AI** — Autonomous catalog intelligence for modern e-commerce.
150
+
151
+ </div>
agents/__init__.py ADDED
File without changes
agents/manager.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import time
3
+ import os
4
+ import datetime
5
+ from agents.trend_spotter import TrendSpotter
6
+ from agents.visionary import Visionary
7
+
8
+ class MerchManager:
9
+ def __init__(self):
10
+ self.trend_spotter = TrendSpotter()
11
+ self.visionary = Visionary()
12
+ self.results_dir = "results"
13
+ if not os.path.exists(self.results_dir):
14
+ os.makedirs(self.results_dir)
15
+
16
+ def generate_batch(self, niche: str) -> str:
17
+ # Step 1: Get slogans
18
+ print(f"🔍 Analyzing trends for niche: {niche}...")
19
+ slogans = self.trend_spotter.get_trends(niche)
20
+
21
+ results = []
22
+
23
+ # Step 2: Generate art prompts
24
+ print(f"🎨 Generating designs for {len(slogans)} slogans...")
25
+ for i, slogan in enumerate(slogans):
26
+ print(f"Generating design {i+1}/{len(slogans)}...")
27
+ prompt = self.visionary.generate_art_prompt(slogan, niche)
28
+ results.append({
29
+ "Niche": niche,
30
+ "Slogan": slogan,
31
+ "Art Prompt": prompt
32
+ })
33
+ time.sleep(10)
34
+
35
+ # Step 3 & 4: Save to CSV
36
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
37
+ filename = f"merch_batch_{niche}_{timestamp}.csv"
38
+ filepath = os.path.join(self.results_dir, filename)
39
+
40
+ with open(filepath, mode='w', newline='', encoding='utf-8') as file:
41
+ writer = csv.DictWriter(file, fieldnames=["Niche", "Slogan", "Art Prompt"])
42
+ writer.writeheader()
43
+ writer.writerows(results)
44
+
45
+ print(f"✅ Batch complete! Saved to {filepath}")
46
+ return filename
agents/memory_agent.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from dotenv import load_dotenv
4
+ from pinecone import Pinecone, ServerlessSpec
5
+ from google import genai
6
+
7
+ load_dotenv()
8
+
9
+ class MemoryAgent:
10
+ def __init__(self):
11
+ # Configure Gemini
12
+ self.gemini_api_key = os.getenv("GEMINI_API_KEY")
13
+ if not self.gemini_api_key:
14
+ raise ValueError("GEMINI_API_KEY not found")
15
+ self.client = genai.Client(api_key=self.gemini_api_key)
16
+
17
+ # Configure Pinecone
18
+ self.pinecone_api_key = os.getenv("PINECONE_API_KEY")
19
+ if not self.pinecone_api_key:
20
+ raise ValueError("PINECONE_API_KEY not found")
21
+
22
+ self.pc = Pinecone(api_key=self.pinecone_api_key)
23
+ self.index_name = "stylesync-index"
24
+
25
+ # Check and create index
26
+ existing_indexes = [i.name for i in self.pc.list_indexes()]
27
+ if self.index_name not in existing_indexes:
28
+ print(f"Creating index {self.index_name}...")
29
+ self.pc.create_index(
30
+ name=self.index_name,
31
+ dimension=768, # gemini-embedding-001 output dimension
32
+ metric='cosine',
33
+ spec=ServerlessSpec(
34
+ cloud='aws',
35
+ region='us-east-1'
36
+ )
37
+ )
38
+ # Wait for index to be ready
39
+ while not self.pc.describe_index(self.index_name).status['ready']:
40
+ time.sleep(1)
41
+ print("Index created.")
42
+
43
+ self.index = self.pc.Index(self.index_name)
44
+
45
+ def _get_embedding(self, text):
46
+ # Using gemini-embedding-001
47
+ result = self.client.models.embed_content(
48
+ model="gemini-embedding-001",
49
+ contents=text,
50
+ config=genai.types.EmbedContentConfig(output_dimensionality=768)
51
+ )
52
+ return result.embeddings[0].values
53
+
54
+ def seed_database(self):
55
+ # Check if empty
56
+ stats = self.index.describe_index_stats()
57
+ if stats.total_vector_count > 0:
58
+ print("Database already seeded.")
59
+ return
60
+
61
+ print("Seeding database...")
62
+ items = [
63
+ {
64
+ "id": "item1",
65
+ "text": "Running Shoe",
66
+ "keywords": "breathable, shock absorption, marathon training, lightweight"
67
+ },
68
+ {
69
+ "id": "item2",
70
+ "text": "Graphic T-Shirt",
71
+ "keywords": "100% cotton, vintage wash, pre-shrunk, soft feel"
72
+ },
73
+ {
74
+ "id": "item3",
75
+ "text": "Leather Wallet",
76
+ "keywords": "genuine leather, RFID blocking, minimalist, bifold"
77
+ }
78
+ ]
79
+
80
+ vectors = []
81
+ for item in items:
82
+ embedding = self._get_embedding(item['text'])
83
+ vectors.append({
84
+ "id": item['id'],
85
+ "values": embedding,
86
+ "metadata": {"keywords": item['keywords'], "text": item['text']}
87
+ })
88
+
89
+ self.index.upsert(vectors=vectors)
90
+ print(f"Seeded {len(vectors)} items.")
91
+
92
+ def retrieve_keywords(self, query_text: str):
93
+ try:
94
+ query_embedding = self._get_embedding(query_text)
95
+
96
+ results = self.index.query(
97
+ vector=query_embedding,
98
+ top_k=5,
99
+ include_metadata=True
100
+ )
101
+ return [m.metadata['keywords'] for m in results.matches if m.metadata and 'keywords' in m.metadata]
102
+ except Exception as e:
103
+ print(f"❌ Keyword Retrieval Failed: {e}")
104
+ return []
agents/visual_analyst.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ from google import genai
5
+ from dotenv import load_dotenv
6
+
7
+ load_dotenv()
8
+
9
+ class VisualAnalyst:
10
+ def __init__(self):
11
+ self.api_key = os.getenv("GEMINI_API_KEY")
12
+ if not self.api_key:
13
+ raise ValueError("GEMINI_API_KEY not found")
14
+
15
+ self.client = genai.Client(api_key=self.api_key)
16
+ self.model_name = "gemini-2.5-flash"
17
+ print(f"✅ VisualAnalyst stored Gemini model: {self.model_name}")
18
+
19
+ async def analyze_image(self, image_path: str):
20
+ try:
21
+ # Upload the file to Gemini
22
+ # Note: For efficiency in production, files should be managed (uploads/deletes)
23
+ # but for this agentic flow, we'll upload per request or assume local path usage helper if needed.
24
+ # However, the standard `model.generate_content` can take PIL images or file objects directly for some sdk versions,
25
+ # but using the File API is cleaner for 1.5 Flash multi-modal.
26
+ # Let's use the simpler PIL integration if available, or just path if the SDK supports it.
27
+ # actually, standard genai usage for images usually involves PIL or uploading.
28
+ # Let's try the PIL approach first as it's often more direct for local scripts.
29
+ import PIL.Image
30
+ img = PIL.Image.open(image_path)
31
+
32
+ user_prompt = (
33
+ "Analyze this product image. "
34
+ "Return ONLY valid JSON with keys: main_color, product_type, design_style, visual_features."
35
+ )
36
+
37
+ # We'll stick to prompt engineering for now to match the "Return ONLY valid JSON" instruction.
38
+ response = self.client.models.generate_content(
39
+ model=self.model_name,
40
+ contents=[user_prompt, img]
41
+ )
42
+
43
+ response_text = response.text
44
+
45
+ # Use regex to find the JSON block robustly
46
+ match = re.search(r'\{.*\}', response_text, re.DOTALL)
47
+ if match:
48
+ cleaned_content = match.group(0)
49
+ else:
50
+ cleaned_content = response_text
51
+
52
+ return json.loads(cleaned_content.strip())
53
+
54
+ except Exception as e:
55
+ print(f"❌ Analysis Failed: {e}")
56
+ return {
57
+ "main_color": "Unknown",
58
+ "product_type": "Unknown",
59
+ "design_style": "Unknown",
60
+ "visual_features": [f"Error: {str(e)}"]
61
+ }
agents/writer_agent.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from groq import Groq
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ class WriterAgent:
9
+ def __init__(self):
10
+ self.api_key = os.getenv("GROQ_API_KEY")
11
+ if not self.api_key:
12
+ raise ValueError("GROQ_API_KEY not found in environment variables")
13
+ self.client = Groq(api_key=self.api_key)
14
+ self.model = "llama-3.3-70b-versatile"
15
+
16
+ def write_listing(self, visual_data: dict, seo_keywords: list) -> dict:
17
+ system_prompt = (
18
+ "You are an expert e-commerce copywriter. "
19
+ "Write a persuasive product listing based on these visual attributes and SEO keywords. "
20
+ "Return JSON with keys: title, description, bullet_points."
21
+ )
22
+
23
+ user_content = f"""
24
+ Visual Attributes: {json.dumps(visual_data, indent=2)}
25
+
26
+ SEO Keywords: {', '.join(seo_keywords)}
27
+
28
+ Please generate the listing in JSON format.
29
+ """
30
+
31
+ try:
32
+ completion = self.client.chat.completions.create(
33
+ model=self.model,
34
+ messages=[
35
+ {"role": "system", "content": system_prompt},
36
+ {"role": "user", "content": user_content}
37
+ ],
38
+ temperature=0.7,
39
+ response_format={"type": "json_object"},
40
+ timeout=15.0
41
+ )
42
+
43
+ response_text = completion.choices[0].message.content
44
+ return json.loads(response_text)
45
+
46
+ except Exception as e:
47
+ print(f"Error generating listing: {e}")
48
+ return {"error": str(e)}
apply_qa_fixes.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+
4
+ def patch_main():
5
+ with open("main.py", "r", encoding="utf-8") as f:
6
+ content = f.read()
7
+
8
+ old_pattern = r'return JSONResponse\(\s*content=\{\s*"error": str\(e\),\s*"type": type\(e\)\.__name__,\s*"details": error_details\s*\},\s*status_code=500\s*\)'
9
+ new_text = '''return JSONResponse(
10
+ content={
11
+ "error": "An internal server error occurred.",
12
+ "type": type(e).__name__
13
+ },
14
+ status_code=500
15
+ )'''
16
+ content = re.sub(old_pattern, new_text, content[1:] if content.startswith('\ufeff') else content)
17
+
18
+ with open("main.py", "w", encoding="utf-8") as f:
19
+ f.write(content)
20
+ print("Patched main.py")
21
+
22
+
23
+ def patch_memory_agent():
24
+ path = "agents/memory_agent.py"
25
+ with open(path, "r", encoding="utf-8") as f:
26
+ content = f.read()
27
+
28
+ old_func = r'def retrieve_keywords\(.*?: str\):.*?return \[m\.metadata\[\'keywords\'\] for m in results\.matches if m\.metadata and \'keywords\' in m\.metadata\]'
29
+ new_func = '''def retrieve_keywords(self, query_text: str):
30
+ try:
31
+ query_embedding = self._get_embedding(query_text)
32
+
33
+ results = self.index.query(
34
+ vector=query_embedding,
35
+ top_k=5,
36
+ include_metadata=True
37
+ )
38
+ return [m.metadata['keywords'] for m in results.matches if m.metadata and 'keywords' in m.metadata]
39
+ except Exception as e:
40
+ print(f"❌ Keyword Retrieval Failed: {e}")
41
+ return []'''
42
+
43
+ content = re.sub(old_func, new_func, content, flags=re.DOTALL)
44
+
45
+ with open(path, "w", encoding="utf-8") as f:
46
+ f.write(content)
47
+ print("Patched agents/memory_agent.py")
48
+
49
+
50
+ def patch_visual_analyst():
51
+ path = "agents/visual_analyst.py"
52
+ with open(path, "r", encoding="utf-8") as f:
53
+ content = f.read()
54
+
55
+ # Add timeout
56
+ content = content.replace(
57
+ "response = self.model.generate_content([user_prompt, img])",
58
+ "response = self.model.generate_content([user_prompt, img], request_options={'timeout': 15.0})"
59
+ )
60
+
61
+ if "import re" not in content:
62
+ content = content.replace("import json", "import json\nimport re")
63
+
64
+ # Rewrite JSON parsing
65
+ old_parsing = r'# Clean up potential markdown code fences.*?return json\.loads\(cleaned_content\.strip\(\)\)'
66
+ new_parsing = '''# Use regex to find the JSON block robustly
67
+ match = re.search(r'\\{.*\\}', response_text, re.DOTALL)
68
+ if match:
69
+ cleaned_content = match.group(0)
70
+ else:
71
+ cleaned_content = response_text
72
+
73
+ return json.loads(cleaned_content.strip())'''
74
+
75
+ content = re.sub(old_parsing, new_parsing, content, flags=re.DOTALL)
76
+
77
+ with open(path, "w", encoding="utf-8") as f:
78
+ f.write(content)
79
+ print("Patched agents/visual_analyst.py")
80
+
81
+
82
+ def patch_writer_agent():
83
+ path = "agents/writer_agent.py"
84
+ with open(path, "r", encoding="utf-8") as f:
85
+ content = f.read()
86
+
87
+ old_completion = r'completion = self\.client\.chat\.completions\.create\(\s*model=self\.model,\s*messages=\[\s*\{"role": "system", "content": system_prompt\},\s*\{"role": "user", "content": user_content\}\s*\],\s*temperature=0\.7,\s*response_format=\{"type": "json_object"\}\s*\)'
88
+
89
+ new_completion = '''completion = self.client.chat.completions.create(
90
+ model=self.model,
91
+ messages=[
92
+ {"role": "system", "content": system_prompt},
93
+ {"role": "user", "content": user_content}
94
+ ],
95
+ temperature=0.7,
96
+ response_format={"type": "json_object"},
97
+ timeout=15.0
98
+ )'''
99
+
100
+ content = re.sub(old_completion, new_completion, content)
101
+
102
+ with open(path, "w", encoding="utf-8") as f:
103
+ f.write(content)
104
+ print("Patched agents/writer_agent.py")
105
+
106
+ if __name__ == "__main__":
107
+ patch_main()
108
+ patch_memory_agent()
109
+ patch_visual_analyst()
110
+ patch_writer_agent()
111
+ print("All file patching completed successfully.")
args.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "projectId": "13453765122851154258",
3
+ "screenId": "cf3af7f8a9e74daf85096241ed88c75c"
4
+ }
check_basic.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+
10
+ print(f"Testing token with microsoft/resnet-50")
11
+
12
+ try:
13
+ # Pass the URL directly as the input (InferenceClient handles URLs for image tasks)
14
+ result = client.image_classification(
15
+ model="microsoft/resnet-50",
16
+ image="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
17
+ )
18
+ print("Success:", result)
19
+ except Exception as e:
20
+ print("Failed:", e)
check_gemini.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import google.generativeai as genai
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
8
+ genai.configure(api_key=api_key)
9
+
10
+ print("Listing available Gemini models...")
11
+ try:
12
+ for m in genai.list_models():
13
+ if 'generateContent' in m.supported_generation_methods:
14
+ print(m.name)
15
+ except Exception as e:
16
+ print(f"List models failed: {e}")
17
+
18
+ model_name = "gemini-1.5-flash"
19
+ print(f"\nTesting model: {model_name}")
20
+
21
+ try:
22
+ model = genai.GenerativeModel(model_name)
23
+ response = model.generate_content("Hello, can you see this?")
24
+ print("Response:", response.text)
25
+ except Exception as e:
26
+ print(f"Test failed: {e}")
check_gemini_clean.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import google.generativeai as genai
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
8
+ genai.configure(api_key=api_key)
9
+
10
+ candidates = [
11
+ "gemini-2.0-flash",
12
+ "gemini-2.0-flash-exp",
13
+ "models/gemini-2.0-flash"
14
+ ]
15
+
16
+ for model_name in candidates:
17
+ print(f"\nTesting model: {model_name}")
18
+ try:
19
+ model = genai.GenerativeModel(model_name)
20
+ response = model.generate_content("Hello")
21
+ print(f"✅ Success with {model_name}: {response.text}")
22
+ break
23
+ except Exception as e:
24
+ print(f"❌ Failed with {model_name}: {e}")
check_groq.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from groq import Groq
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
7
+
8
+ print("Listing Groq models...")
9
+ models = client.models.list()
10
+ for m in models.data:
11
+ print(m.id)
check_groq_models.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from groq import Groq
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ try:
8
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
9
+ models = client.models.list()
10
+ print("Available Models:")
11
+ for model in models.data:
12
+ print(f"- {model.id}")
13
+ except Exception as e:
14
+ print(f"Error listing models: {e}")
check_groq_vision.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from groq import Groq
3
+ from dotenv import load_dotenv
4
+ import base64
5
+
6
+ load_dotenv()
7
+
8
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
9
+ model = "llama-3.2-11b-vision-preview"
10
+
11
+ print(f"Testing Groq Vision model: {model}")
12
+
13
+ # Test 1: Image URL
14
+ print("\n--- Test 1: Image URL ---")
15
+ try:
16
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
17
+ completion = client.chat.completions.create(
18
+ model=model,
19
+ messages=[
20
+ {
21
+ "role": "user",
22
+ "content": [
23
+ {"type": "text", "text": "What's in this image?"},
24
+ {"type": "image_url", "image_url": {"url": image_url}},
25
+ ],
26
+ }
27
+ ],
28
+ temperature=1,
29
+ max_tokens=1024,
30
+ top_p=1,
31
+ stream=False,
32
+ stop=None,
33
+ )
34
+ print("Response:", completion.choices[0].message.content)
35
+ except Exception as e:
36
+ print("Groq Vision failed:", e)
check_idefics.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+ model = "HuggingFaceM4/idefics2-8b"
10
+
11
+ print(f"Testing model: {model}")
12
+
13
+ # Test 1: Image URL
14
+ print("\n--- Test 1: Image URL ---")
15
+ try:
16
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
17
+ messages = [
18
+ {
19
+ "role": "user",
20
+ "content": [
21
+ {"type": "image_url", "image_url": {"url": image_url}},
22
+ {"type": "text", "text": "What is in this image?"}
23
+ ]
24
+ }
25
+ ]
26
+ completion = client.chat.completions.create(
27
+ model=model,
28
+ messages=messages,
29
+ max_tokens=100
30
+ )
31
+ print("Response:", completion.choices[0].message.content)
32
+ except Exception as e:
33
+ print("Image URL failed:", e)
check_idefics_raw.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ api_key = os.getenv("HF_TOKEN")
9
+ model = "HuggingFaceM4/idefics2-8b"
10
+ url = f"https://router.huggingface.co/models/{model}"
11
+
12
+ headers = {"Authorization": f"Bearer {api_key}"}
13
+
14
+ print(f"Testing URL: {url}")
15
+
16
+ # Test A: Simple text inputs
17
+ print("\n--- Test A: Simple Text ---")
18
+ response = requests.post(url, headers=headers, json={"inputs": "Hello"})
19
+ print(f"Status: {response.status_code}")
20
+ print("Response:", response.text)
21
+
22
+ # Test B: Formatted inputs (Standard for some VLM APIs)
23
+ # Often they accept { "inputs": "User: ...", "parameters": ... }
24
+ print("\n--- Test B: Formatted Prompt ---")
25
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
26
+ prompt = f"User: ![]({image_url}) Describe this image.<end_of_utterance>\nAssistant:"
27
+ response = requests.post(url, headers=headers, json={"inputs": prompt, "parameters": {"max_new_tokens": 50}})
28
+ print(f"Status: {response.status_code}")
29
+ print("Response:", response.text)
check_idefics_v2.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+ model = "HuggingFaceM4/idefics2-8b"
10
+
11
+ print(f"Testing model: {model}")
12
+
13
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
14
+
15
+ # Format for Idefics2:
16
+ # User: ![](<image_url>) <text><end_of_utterance>\nAssistant:
17
+ prompt = f"User: ![]({image_url}) Describe this image.<end_of_utterance>\nAssistant:"
18
+
19
+ print(f"\n--- Testing with text_generation and specific prompt ---")
20
+ print(f"Prompt: {prompt}")
21
+
22
+ try:
23
+ # Use text_generation for models that don't support chat
24
+ response = client.text_generation(
25
+ prompt=prompt,
26
+ model=model,
27
+ max_new_tokens=100
28
+ )
29
+ print("Response:", response)
30
+ except Exception as e:
31
+ print("Failed:", e)
check_idefics_v3.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import traceback
3
+ from huggingface_hub import InferenceClient
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ api_key = os.getenv("HF_TOKEN")
9
+ client = InferenceClient(api_key=api_key)
10
+ model = "HuggingFaceM4/idefics2-8b"
11
+
12
+ print(f"Testing model: {model}")
13
+
14
+ print("\n--- Test 1: Image to Text (Captioning) ---")
15
+ try:
16
+ # This might work if the API treats it as captioning
17
+ res = client.image_to_text(
18
+ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true",
19
+ model=model
20
+ )
21
+ print("Response:", res)
22
+ except Exception:
23
+ traceback.print_exc()
24
+
25
+ print("\n--- Test 2: Text Generation (Simple) ---")
26
+ try:
27
+ res = client.text_generation("describe a car", model=model, max_new_tokens=50)
28
+ print("Response:", res)
29
+ except Exception:
30
+ traceback.print_exc()
check_llama.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+ model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
10
+
11
+ print(f"Testing model: {model}")
12
+
13
+ # Test 1: Image URL (Llama Vision)
14
+ print("\n--- Test 1: Image URL ---")
15
+ try:
16
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
17
+ messages = [
18
+ {
19
+ "role": "user",
20
+ "content": [
21
+ {"type": "image_url", "image_url": {"url": image_url}},
22
+ {"type": "text", "text": "What is in this image?"}
23
+ ]
24
+ }
25
+ ]
26
+ completion = client.chat.completions.create(
27
+ model=model,
28
+ messages=messages,
29
+ max_tokens=100
30
+ )
31
+ print("Response:", completion.choices[0].message.content)
32
+ except Exception as e:
33
+ print("Image URL failed:", e)
check_llava.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+ model = "llava-hf/llava-1.5-7b-hf"
10
+
11
+ print(f"Testing model: {model}")
12
+
13
+ # Test 1: Image URL
14
+ print("\n--- Test 1: Image URL ---")
15
+ try:
16
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
17
+ messages = [
18
+ {
19
+ "role": "user",
20
+ "content": [
21
+ {"type": "image_url", "image_url": {"url": image_url}},
22
+ {"type": "text", "text": "What is in this image?"}
23
+ ]
24
+ }
25
+ ]
26
+ completion = client.chat.completions.create(
27
+ model=model,
28
+ messages=messages,
29
+ max_tokens=100
30
+ )
31
+ print("Response:", completion.choices[0].message.content)
32
+ except Exception as e:
33
+ print("Image URL failed:", e)
check_models.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import os
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("GEMINI_API_KEY")
8
+ if not api_key:
9
+ print("No API key found")
10
+ else:
11
+ genai.configure(api_key=api_key)
12
+ print("Listing models...")
13
+ for m in genai.list_models():
14
+ if 'generateContent' in m.supported_generation_methods:
15
+ print(m.name)
check_models_list.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import os
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("GEMINI_API_KEY")
8
+ if not api_key:
9
+ print("❌ API Key not found")
10
+ else:
11
+ genai.configure(api_key=api_key)
12
+ print("Listing available models...")
13
+ for m in genai.list_models():
14
+ if 'generateContent' in m.supported_generation_methods:
15
+ print(m.name)
check_qwen.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ api_key = os.getenv("HF_TOKEN")
8
+ client = InferenceClient(api_key=api_key)
9
+ model = "Qwen/Qwen2-VL-7B-Instruct"
10
+
11
+ print(f"Testing model: {model}")
12
+
13
+ # Test 1: Text only
14
+ print("\n--- Test 1: Text Only ---")
15
+ try:
16
+ messages = [
17
+ {"role": "user", "content": "Hello, are you working?"}
18
+ ]
19
+ completion = client.chat.completions.create(
20
+ model=model,
21
+ messages=messages,
22
+ max_tokens=100
23
+ )
24
+ print("Response:", completion.choices[0].message.content)
25
+ except Exception as e:
26
+ print("Text only failed:", e)
27
+
28
+ # Test 2: Image (using a public URL to avoid base64 issues first)
29
+ print("\n--- Test 2: Image URL ---")
30
+ try:
31
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
32
+ messages = [
33
+ {
34
+ "role": "user",
35
+ "content": [
36
+ {"type": "image_url", "image_url": {"url": image_url}},
37
+ {"type": "text", "text": "What is in this image?"}
38
+ ]
39
+ }
40
+ ]
41
+ completion = client.chat.completions.create(
42
+ model=model,
43
+ messages=messages,
44
+ max_tokens=100
45
+ )
46
+ print("Response:", completion.choices[0].message.content)
47
+ except Exception as e:
48
+ print("Image URL failed:", e)
check_qwen_raw.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ api_key = os.getenv("HF_TOKEN")
9
+ model = "Qwen/Qwen2-VL-7B-Instruct"
10
+ # Update URL to router
11
+ url = f"https://router.huggingface.co/models/{model}"
12
+
13
+ headers = {"Authorization": f"Bearer {api_key}"}
14
+
15
+ print(f"Testing URL: {url}")
16
+
17
+ # Test 1: Simple text generation payload (inputs string)
18
+ data_text = {
19
+ "inputs": "Hello",
20
+ "parameters": {"max_new_tokens": 50}
21
+ }
22
+ print("\n--- Test 1: Text Generation (inputs string) ---")
23
+ response = requests.post(url, headers=headers, json=data_text)
24
+ print(f"Status: {response.status_code}")
25
+ print("Response:", response.text)
26
+
27
+ # Test 2: VQA format
28
+ data_vqa = {
29
+ "inputs": {
30
+ "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true",
31
+ "question": "What is in this image?"
32
+ }
33
+ }
34
+ print("\n--- Test 2: VQA Format ---")
35
+ response = requests.post(url, headers=headers, json=data_vqa)
36
+ print(f"Status: {response.status_code}")
37
+ print("Response:", response.text)
38
+
39
+ # Test 3: Chat Completions API (OpenAI style)
40
+ url_chat = f"https://router.huggingface.co/models/{model}/v1/chat/completions"
41
+ print(f"\nTesting URL: {url_chat}")
42
+ data_chat = {
43
+ "model": model, # Sometimes required in body
44
+ "messages": [
45
+ {"role": "user", "content": "Hello"}
46
+ ],
47
+ "max_tokens": 50
48
+ }
49
+ print("\n--- Test 3: Chat Completion ---")
50
+ response = requests.post(url_chat, headers=headers, json=data_chat)
51
+ print(f"Status: {response.status_code}")
52
+ print("Response:", response.text)
clean_binary_deploy.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+
4
+ def main():
5
+ files_to_delete = [
6
+ "stitch_stylesync_ai_dashboard.zip",
7
+ "screen.jpg",
8
+ "test_image.jpg"
9
+ ]
10
+
11
+ # Delete Local Binaries
12
+ print("Deleting local binaries...")
13
+ for filename in files_to_delete:
14
+ try:
15
+ os.remove(filename)
16
+ print(f"Deleted: {filename}")
17
+ except FileNotFoundError:
18
+ print(f"File not found (already deleted): {filename}")
19
+ except Exception as e:
20
+ print(f"Error deleting {filename}: {e}")
21
+
22
+ # Update .gitignore
23
+ print("Updating .gitignore...")
24
+ gitignore_path = ".gitignore"
25
+
26
+ # Read existing content to avoid duplicate entries
27
+ existing_ignores = []
28
+ try:
29
+ if os.path.exists(gitignore_path):
30
+ with open(gitignore_path, "r", encoding="utf-8") as f:
31
+ existing_ignores = f.read().splitlines()
32
+ except Exception as e:
33
+ print(f"Warning: Could not read .gitignore: {e}")
34
+
35
+ ignores_to_add = ["*.zip", "*.jpg"]
36
+
37
+ try:
38
+ with open(gitignore_path, "a", encoding="utf-8") as f:
39
+ for ignore in ignores_to_add:
40
+ if ignore not in existing_ignores:
41
+ # add a newline if files doesn't end with one, but simplier to just write
42
+ f.write(f"\n{ignore}\n")
43
+ print(f"Added '{ignore}' to .gitignore")
44
+ else:
45
+ print(f"'{ignore}' already in .gitignore")
46
+ except Exception as e:
47
+ print(f"Error updating .gitignore: {e}")
48
+
49
+ # The Orphan Branch Strategy
50
+ print("\nExecuting Git Orphan Branch Strategy...")
51
+
52
+ commands = [
53
+ "git checkout --orphan hf_clean_deploy_v2",
54
+ "git add .",
55
+ 'git commit -m "UI Update: Glassmorphism Design & Binary Cleanup"'
56
+ ]
57
+
58
+ for cmd in commands:
59
+ print(f"Running: {cmd}")
60
+ subprocess.run(cmd, shell=True, check=False)
61
+
62
+ push_cmd = "git push --force space hf_clean_deploy_v2:main"
63
+ print(f"\nRunning final push command: {push_cmd}")
64
+
65
+ result = subprocess.run(push_cmd, shell=True, capture_output=True, text=True)
66
+
67
+ # Expected Output
68
+ print("\n--- Push Command STDOUT ---")
69
+ print(result.stdout)
70
+ print("--- Push Command STDERR ---")
71
+ print(result.stderr)
72
+ print("---------------------------\n")
73
+
74
+ if result.returncode == 0:
75
+ print("✅ Force push successful!")
76
+ else:
77
+ print("❌ Force push failed (see STDERR above).")
78
+
79
+ if __name__ == "__main__":
80
+ main()
code.html ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html class="dark" lang="en"><head>
3
+ <meta charset="utf-8"/>
4
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
5
+ <title>StyleSync AI Dashboard</title>
6
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
7
+ <link href="https://fonts.googleapis.com" rel="preconnect"/>
8
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Spline+Sans:wght@300;400;500;600;700&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "primary": "#3b82f6", // Electric Blue
18
+ "primary-hover": "#2563eb",
19
+ "secondary": "#6366f1", // Indigo accent
20
+ "background-light": "#f8fafc",
21
+ "background-dark": "#0f172a", // Deep Slate
22
+ "surface-dark": "#1e293b", // Slate 800
23
+ "surface-darker": "#020617", // Slate 950
24
+ "border-dark": "#334155", // Slate 700
25
+ },
26
+ fontFamily: {
27
+ "display": ["Spline Sans", "sans-serif"],
28
+ "mono": ["monospace"]
29
+ },
30
+ borderRadius: {"DEFAULT": "1rem", "lg": "2rem", "xl": "3rem", "full": "9999px"},
31
+ },
32
+ },
33
+ }
34
+ </script>
35
+ <style>.custom-scrollbar::-webkit-scrollbar {
36
+ width: 8px;
37
+ height: 8px;
38
+ }
39
+ .custom-scrollbar::-webkit-scrollbar-track {
40
+ background: #020617;
41
+ }
42
+ .custom-scrollbar::-webkit-scrollbar-thumb {
43
+ background: #334155;
44
+ border-radius: 4px;
45
+ }
46
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
47
+ background: #3b82f6;
48
+ }
49
+ </style>
50
+ </head>
51
+ <body class="font-display bg-background-light dark:bg-background-dark text-slate-900 dark:text-white antialiased overflow-hidden h-screen flex flex-col">
52
+ <header class="flex-none flex items-center justify-between whitespace-nowrap border-b border-solid border-border-dark px-6 py-4 bg-background-dark z-10">
53
+ <div class="flex items-center gap-3 text-white">
54
+ <div class="flex items-center justify-center size-10 rounded-full bg-primary/10 text-primary">
55
+ <span class="material-symbols-outlined text-2xl">all_inclusive</span>
56
+ </div>
57
+ <div>
58
+ <h2 class="text-white text-xl font-bold leading-tight tracking-tight">StyleSync AI</h2>
59
+ <span class="text-xs text-primary/60 font-medium uppercase tracking-wider">Enterprise Edition</span>
60
+ </div>
61
+ </div>
62
+ <button class="flex min-w-[84px] cursor-pointer items-center justify-center overflow-hidden rounded-full h-10 px-6 bg-primary hover:bg-primary-hover transition-colors text-white text-sm font-bold leading-normal tracking-[0.015em]">
63
+ <span class="material-symbols-outlined mr-2 text-lg">rocket_launch</span>
64
+ <span class="truncate">Deploy</span>
65
+ </button>
66
+ </header>
67
+ <main class="flex-1 flex overflow-hidden">
68
+ <div class="flex-1 flex flex-col border-r border-border-dark min-w-[400px] overflow-y-auto custom-scrollbar p-8">
69
+ <div class="max-w-2xl w-full mx-auto flex flex-col gap-6 h-full">
70
+ <div class="flex items-center justify-between">
71
+ <h2 class="text-white tracking-light text-[28px] font-bold leading-tight">Input Data</h2>
72
+ <span class="bg-surface-dark text-white px-3 py-1 rounded-full text-xs font-medium border border-border-dark">Step 1 of 2</span>
73
+ </div>
74
+ <div class="flex flex-col flex-1 max-h-[300px] min-h-[200px]">
75
+ <div class="group relative flex flex-col items-center justify-center gap-4 rounded-xl border-2 border-dashed border-slate-600 hover:border-primary/50 hover:bg-surface-dark transition-all cursor-pointer h-full w-full px-6 py-8">
76
+ <div class="size-16 rounded-full bg-surface-dark group-hover:bg-primary/20 flex items-center justify-center transition-colors border border-border-dark group-hover:border-primary/30">
77
+ <span class="material-symbols-outlined text-3xl text-primary">cloud_upload</span>
78
+ </div>
79
+ <div class="flex flex-col items-center gap-1">
80
+ <p class="text-white text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
81
+ <p class="text-slate-400 text-sm font-normal text-center">Supports JPG, PNG, WEBP</p>
82
+ </div>
83
+ <button class="mt-2 flex items-center justify-center rounded-full h-9 px-4 bg-slate-700 hover:bg-slate-600 text-white text-xs font-bold transition-colors">
84
+ Browse Files
85
+ </button>
86
+ <div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/10 pointer-events-none rounded-xl"></div>
87
+ </div>
88
+ </div>
89
+ <div class="flex flex-col gap-3 flex-1">
90
+ <label class="flex items-center justify-between">
91
+ <span class="text-white text-base font-medium">Raw Product Specs</span>
92
+ <span class="text-xs text-slate-400">JSON or Plain Text</span>
93
+ </label>
94
+ <div class="relative flex-1">
95
+ <textarea class="form-input w-full h-full resize-none rounded-xl text-white placeholder:text-slate-500 focus:outline-0 focus:ring-2 focus:ring-primary/50 border border-border-dark bg-surface-dark p-4 text-base font-normal leading-relaxed font-mono" placeholder="Enter fabric details, dimensions, and SKU...
96
+ Example:
97
+ Material: 100% Recycled Polyester
98
+ Fit: Regular
99
+ SKU: JK-2024-WTR"></textarea>
100
+ </div>
101
+ </div>
102
+ <div class="pt-2">
103
+ <button class="group relative w-full cursor-pointer overflow-hidden rounded-xl h-16 bg-gradient-to-r from-primary to-blue-700 hover:from-blue-500 hover:to-primary transition-all shadow-[0_0_20px_rgba(59,130,246,0.3)]">
104
+ <div class="absolute inset-0 flex items-center justify-center gap-3">
105
+ <span class="text-white text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span>
106
+ <span class="material-symbols-outlined text-white group-hover:translate-x-1 transition-transform">arrow_forward</span>
107
+ </div>
108
+ <div class="absolute top-0 -inset-full h-full w-1/2 z-5 block transform -skew-x-12 bg-gradient-to-r from-transparent to-white opacity-20 group-hover:animate-shine"></div>
109
+ </button>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ <div class="flex-1 flex flex-col bg-surface-darker p-8 overflow-hidden relative">
114
+ <div class="absolute inset-0 opacity-5 pointer-events-none" style="background-image: radial-gradient(#64748b 1px, transparent 1px); background-size: 24px 24px;"></div>
115
+ <div class="max-w-3xl w-full mx-auto flex flex-col gap-6 h-full relative z-10">
116
+ <div class="flex items-center justify-between">
117
+ <h2 class="text-white tracking-light text-[28px] font-bold leading-tight">Generated Output</h2>
118
+ <div class="flex gap-2">
119
+ <button class="p-2 hover:bg-surface-dark rounded-lg text-slate-400 hover:text-white transition-colors">
120
+ <span class="material-symbols-outlined text-xl">content_copy</span>
121
+ </button>
122
+ <button class="p-2 hover:bg-surface-dark rounded-lg text-slate-400 hover:text-white transition-colors">
123
+ <span class="material-symbols-outlined text-xl">download</span>
124
+ </button>
125
+ </div>
126
+ </div>
127
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
128
+ <div class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
129
+ <div class="relative size-3">
130
+ <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-500 opacity-75"></span>
131
+ <span class="relative inline-flex rounded-full size-3 bg-purple-500"></span>
132
+ </div>
133
+ <div class="flex flex-col overflow-hidden">
134
+ <span class="text-xs text-slate-400 truncate">Vision Agent</span>
135
+ <span class="text-sm font-bold text-white truncate">Gemini Pro 1.5</span>
136
+ </div>
137
+ <span class="material-symbols-outlined text-purple-500 ml-auto text-lg">visibility</span>
138
+ </div>
139
+ <div class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
140
+ <div class="relative size-3">
141
+ <span class="relative inline-flex rounded-full size-3 bg-primary"></span>
142
+ </div>
143
+ <div class="flex flex-col overflow-hidden">
144
+ <span class="text-xs text-slate-400 truncate">Reasoning Agent</span>
145
+ <span class="text-sm font-bold text-white truncate">Llama 3 70B</span>
146
+ </div>
147
+ <span class="material-symbols-outlined text-primary ml-auto text-lg">psychology</span>
148
+ </div>
149
+ <div class="flex items-center gap-3 p-3 rounded-lg bg-surface-dark border border-border-dark shadow-sm">
150
+ <div class="relative size-3">
151
+ <span class="relative inline-flex rounded-full size-3 bg-cyan-500"></span>
152
+ </div>
153
+ <div class="flex flex-col overflow-hidden">
154
+ <span class="text-xs text-slate-400 truncate">SEO Context</span>
155
+ <span class="text-sm font-bold text-white truncate">Pinecone DB</span>
156
+ </div>
157
+ <span class="material-symbols-outlined text-cyan-500 ml-auto text-lg">database</span>
158
+ </div>
159
+ </div>
160
+ <div class="flex-1 rounded-xl bg-[#0d1117] border border-border-dark flex flex-col overflow-hidden shadow-2xl">
161
+ <div class="flex items-center justify-between px-4 py-2 bg-surface-dark border-b border-border-dark">
162
+ <span class="text-xs font-mono text-slate-400">output.json</span>
163
+ <div class="flex gap-1.5">
164
+ <div class="size-2.5 rounded-full bg-red-500/20"></div>
165
+ <div class="size-2.5 rounded-full bg-yellow-500/20"></div>
166
+ <div class="size-2.5 rounded-full bg-green-500/20"></div>
167
+ </div>
168
+ </div>
169
+ <div class="flex-1 p-4 overflow-auto custom-scrollbar font-mono text-sm leading-6">
170
+ <pre><code class="language-json"><span class="text-slate-500">1</span> <span class="text-yellow-500">{</span>
171
+ <span class="text-slate-500">2</span> <span class="text-primary">"product_analysis"</span><span class="text-white">:</span> <span class="text-yellow-500">{</span>
172
+ <span class="text-slate-500">3</span> <span class="text-primary">"title"</span><span class="text-white">:</span> <span class="text-sky-300">"Apex Terrain All-Weather Performance Jacket"</span><span class="text-white">,</span>
173
+ <span class="text-slate-500">4</span> <span class="text-primary">"category"</span><span class="text-white">:</span> <span class="text-sky-300">"Outerwear / Men's / Technical Shells"</span><span class="text-white">,</span>
174
+ <span class="text-slate-500">5</span> <span class="text-primary">"features"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
175
+ <span class="text-slate-500">6</span> <span class="text-sky-300">"Gore-Tex Pro Membrane"</span><span class="text-white">,</span>
176
+ <span class="text-slate-500">7</span> <span class="text-sky-300">"Articulated Sleeves"</span><span class="text-white">,</span>
177
+ <span class="text-slate-500">8</span> <span class="text-sky-300">"Helmet-Compatible Hood"</span>
178
+ <span class="text-slate-500">9</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
179
+ <span class="text-slate-500">10</span> <span class="text-primary">"seo_tags"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
180
+ <span class="text-slate-500">11</span> <span class="text-sky-300">"#hikinggear"</span><span class="text-white">,</span> <span class="text-sky-300">"#waterproof"</span><span class="text-white">,</span> <span class="text-sky-300">"#adventure"</span>
181
+ <span class="text-slate-500">12</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
182
+ <span class="text-slate-500">13</span> <span class="text-primary">"sentiment_score"</span><span class="text-white">:</span> <span class="text-purple-400">0.98</span><span class="text-white">,</span>
183
+ <span class="text-slate-500">14</span> <span class="text-primary">"market_fit"</span><span class="text-white">:</span> <span class="text-sky-300">"High Demand"</span>
184
+ <span class="text-slate-500">15</span> <span class="text-yellow-500">}</span><span class="text-white">,</span>
185
+ <span class="text-slate-500">16</span> <span class="text-primary">"deployment_status"</span><span class="text-white">:</span> <span class="text-sky-300">"Ready"</span>
186
+ <span class="text-slate-500">17</span> <span class="text-yellow-500">}</span></code></pre>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </main>
192
+ <script>
193
+ tailwind.config.theme.extend.animation = {
194
+ shine: 'shine 1s',
195
+ }
196
+ tailwind.config.theme.extend.keyframes = {
197
+ shine: {
198
+ '100%': { left: '125%' },
199
+ }
200
+ }
201
+ </script>
202
+
203
+ </body></html>
connect_n8n.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+
4
+ def update_requirements():
5
+ req_file = "requirements.txt"
6
+ if not os.path.exists(req_file):
7
+ with open(req_file, "w") as f:
8
+ f.write("httpx\n")
9
+ print(f"Created {req_file} with httpx.")
10
+ return
11
+
12
+ with open(req_file, "r") as f:
13
+ content = f.read()
14
+
15
+ if "httpx" not in content:
16
+ with open(req_file, "a") as f:
17
+ f.write("\nhttpx\n")
18
+ print("Appended httpx to requirements.txt.")
19
+ else:
20
+ print("httpx already in requirements.txt.")
21
+
22
+ def update_main():
23
+ main_content = r'''import os
24
+ import httpx
25
+ import asyncio
26
+ from fastapi import FastAPI, UploadFile, File
27
+ from fastapi.responses import HTMLResponse, JSONResponse
28
+ from dotenv import load_dotenv
29
+ # Import Agents
30
+ from agents.visual_analyst import VisualAnalyst
31
+ from agents.memory_agent import MemoryAgent
32
+ from agents.writer_agent import WriterAgent
33
+ load_dotenv()
34
+ app = FastAPI()
35
+ # Initialize Agents
36
+ try:
37
+ visual_agent = VisualAnalyst()
38
+ memory_agent = MemoryAgent()
39
+ writer_agent = WriterAgent()
40
+ memory_agent.seed_database()
41
+ print("✅ All Agents Online")
42
+ except Exception as e:
43
+ print(f"⚠️ Agent Startup Warning: {e}")
44
+ @app.get("/", response_class=HTMLResponse)
45
+ async def read_root():
46
+ try:
47
+ with open("dashboard.html", "r") as f:
48
+ return f.read()
49
+ except FileNotFoundError:
50
+ return "<h1>Error: dashboard.html not found</h1>"
51
+ @app.post("/generate-catalog")
52
+ async def generate_catalog(file: UploadFile = File(...)):
53
+ try:
54
+ # 1. Save Temp File
55
+ os.makedirs("uploads", exist_ok=True)
56
+ file_path = f"uploads/{file.filename}"
57
+ with open(file_path, "wb") as f:
58
+ f.write(await file.read())
59
+ # 2. Run AI Pipeline
60
+ visual_data = await visual_agent.analyze_image(file_path)
61
+
62
+ query = f"{visual_data.get('main_color', '')} {visual_data.get('product_type', 'product')}"
63
+ seo_keywords = memory_agent.retrieve_keywords(query)
64
+
65
+ listing = writer_agent.write_listing(visual_data, seo_keywords)
66
+
67
+ # 3. Construct Final Payload
68
+ final_data = {
69
+ "visual_data": visual_data,
70
+ "seo_keywords": seo_keywords,
71
+ "listing": listing
72
+ }
73
+ # 4. ⚡ N8N AUTOMATION TRIGGER ⚡
74
+ n8n_url = os.getenv("N8N_WEBHOOK_URL")
75
+ if n8n_url:
76
+ print(f"🚀 Sending data to N8N: {n8n_url}")
77
+ # Fire and forget (don't make the user wait for n8n)
78
+ asyncio.create_task(send_to_n8n(n8n_url, final_data))
79
+
80
+ # Cleanup
81
+ if os.path.exists(file_path):
82
+ os.remove(file_path)
83
+
84
+ return JSONResponse(content=final_data)
85
+ except Exception as e:
86
+ return JSONResponse(content={"error": str(e)}, status_code=500)
87
+ # Async Helper to send data without blocking
88
+ async def send_to_n8n(url, data):
89
+ try:
90
+ async with httpx.AsyncClient() as client:
91
+ await client.post(url, json=data, timeout=5.0)
92
+ print("✅ N8N Webhook Sent Successfully")
93
+ except Exception as e:
94
+ print(f"❌ N8N Webhook Failed: {e}")
95
+ if __name__ == "__main__":
96
+ import uvicorn
97
+ uvicorn.run(app, host="0.0.0.0", port=7860)
98
+ '''
99
+ with open("main.py", "w", encoding="utf-8") as f:
100
+ f.write(main_content)
101
+ print("Updated main.py with N8N integration logic.")
102
+
103
+ def deploy():
104
+ try:
105
+ subprocess.run(["git", "add", "."], check=True)
106
+ # Check if there are changes to commit
107
+ status = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True)
108
+ if status.stdout.strip():
109
+ subprocess.run(["git", "commit", "-m", "Add N8N Integration"], check=True)
110
+ print("Git commit successful.")
111
+ else:
112
+ print("No changes to commit.")
113
+
114
+ print("Pushing to space...")
115
+ subprocess.run(["git", "push", "space", "clean_deploy:main"], check=True)
116
+ print("✅ Successfully deployed to Hugging Face Space.")
117
+
118
+ except subprocess.CalledProcessError as e:
119
+ print(f"❌ Deployment failed: {e}")
120
+
121
+ if __name__ == "__main__":
122
+ print("Starting N8N Integration Setup...")
123
+ update_requirements()
124
+ update_main()
125
+ deploy()
126
+ print("✅ connect_n8n.py completed.")
create_dockerfile.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import sys
3
+
4
+ def run_command(command):
5
+ print(f"Running: {command}")
6
+ try:
7
+ # shell=True allows us to run the command string exactly as provided
8
+ subprocess.run(command, shell=True, check=True)
9
+ except subprocess.CalledProcessError as e:
10
+ print(f"Error executing command '{command}': {e}")
11
+ sys.exit(1)
12
+
13
+ def main():
14
+ # 1. Create Dockerfile
15
+ dockerfile_content = """FROM python:3.9
16
+ WORKDIR /code
17
+ COPY ./requirements.txt /code/requirements.txt
18
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
19
+ COPY . /code
20
+ # Fix permissions for libraries that write to home
21
+ RUN mkdir -p /tmp/home
22
+ ENV HOME=/tmp/home
23
+ # Start the FastAPI server on port 7860 (required by Hugging Face)
24
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
25
+ """
26
+
27
+ print("Creating Dockerfile...")
28
+ try:
29
+ with open("Dockerfile", "w", newline='\n') as f:
30
+ f.write(dockerfile_content)
31
+ print("Dockerfile created successfully.")
32
+ except Exception as e:
33
+ print(f"Failed to create Dockerfile: {e}")
34
+ sys.exit(1)
35
+
36
+ # 2. Push to Space
37
+ print("Executing Git commands...")
38
+ commands = [
39
+ 'git add Dockerfile',
40
+ 'git commit -m "Add Dockerfile for Hugging Face deployment"',
41
+ 'git push -f space clean_deploy:main'
42
+ ]
43
+
44
+ for cmd in commands:
45
+ run_command(cmd)
46
+
47
+ print("\ncreate_dockerfile.py execution completed.")
48
+
49
+ if __name__ == "__main__":
50
+ main()
dashboard.html ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html class="dark" lang="en"><head>
3
+ <meta charset="utf-8"/>
4
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
5
+ <title>StyleSync AI Dashboard</title>
6
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
7
+ <link href="https://fonts.googleapis.com" rel="preconnect"/>
8
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "primary": "#f59e0b",
18
+ "primary-hover": "#d97706",
19
+ "glass-border": "rgba(255, 255, 255, 0.12)",
20
+ "glass-surface": "rgba(255, 255, 255, 0.03)",
21
+ "glass-surface-hover": "rgba(255, 255, 255, 0.07)",
22
+ "charcoal": "#0a0a0a",
23
+ "amber-accent": "#fbbf24"
24
+ },
25
+ fontFamily: {
26
+ "display": ["Plus Jakarta Sans", "sans-serif"],
27
+ "mono": ["monospace"]
28
+ },
29
+ borderRadius: { "DEFAULT": "1rem", "lg": "1.5rem", "xl": "2rem", "2xl": "3rem", "full": "9999px" },
30
+ backdropBlur: {
31
+ 'xs': '2px',
32
+ },
33
+ screens: {
34
+ 'xs': '480px',
35
+ },
36
+ animation: {
37
+ 'pulse-slow': 'pulse-slow 3s infinite',
38
+ 'glow-pulse': 'glow-pulse 3s infinite',
39
+ 'copy-success': 'copy-success 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
40
+ },
41
+ keyframes: {
42
+ 'pulse-slow': {
43
+ '0%, 100%': { transform: 'scale(1)' },
44
+ '50%': { transform: 'scale(1.02)' },
45
+ },
46
+ 'glow-pulse': {
47
+ '0%, 100%': { boxShadow: '0 0 0 0 rgba(245, 158, 11, 0.4)' },
48
+ '50%': { boxShadow: '0 0 20px 5px rgba(251, 191, 36, 0.5)' },
49
+ },
50
+ 'copy-success': {
51
+ '0%': { transform: 'scale(0.8)', opacity: '0' },
52
+ '100%': { transform: 'scale(1)', opacity: '1' },
53
+ }
54
+ }
55
+ },
56
+ },
57
+ }
58
+ </script>
59
+ <style type="text/tailwindcss">
60
+ .custom-scrollbar::-webkit-scrollbar {
61
+ width: 6px;
62
+ height: 6px;
63
+ }
64
+ .custom-scrollbar::-webkit-scrollbar-track {
65
+ background: transparent;
66
+ }
67
+ .custom-scrollbar::-webkit-scrollbar-thumb {
68
+ background: rgba(255, 255, 255, 0.1);
69
+ border-radius: 99px;
70
+ }
71
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
72
+ background: rgba(255, 255, 255, 0.2);
73
+ }
74
+ body {
75
+ background-color: #0a0a0a;
76
+ background-image:
77
+ radial-gradient(at 0% 0%, rgba(20, 20, 20, 1) 0, transparent 50%),
78
+ radial-gradient(at 50% -10%, rgba(217, 119, 6, 0.15) 0, transparent 60%),
79
+ radial-gradient(at 100% 0%, rgba(251, 191, 36, 0.1) 0, transparent 50%),
80
+ radial-gradient(at 80% 80%, rgba(217, 119, 6, 0.12) 0, transparent 50%);
81
+ background-attachment: fixed;
82
+ min-height: 100vh;
83
+ }
84
+ .glass-panel {
85
+ background: rgba(15, 15, 15, 0.75);
86
+ backdrop-filter: blur(20px);
87
+ -webkit-backdrop-filter: blur(20px);
88
+ border: 1px solid rgba(255, 255, 255, 0.08);
89
+ }
90
+ @media (max-width: 768px) {
91
+ .glass-panel {
92
+ backdrop-filter: blur(14px);
93
+ -webkit-backdrop-filter: blur(14px);
94
+ background: rgba(10, 10, 10, 0.85);
95
+ }
96
+ }
97
+ .glass-card {
98
+ background: rgba(255, 255, 255, 0.02);
99
+ border: 1px solid rgba(255, 255, 255, 0.05);
100
+ backdrop-filter: blur(12px);
101
+ transition: all 0.3s ease;
102
+ }
103
+ @media (hover: hover) {
104
+ .glass-card:hover {
105
+ border-color: rgba(251, 191, 36, 0.3);
106
+ box-shadow: 0 0 25px rgba(245, 158, 11, 0.15);
107
+ background: rgba(255, 255, 255, 0.04);
108
+ }
109
+ }
110
+ .neon-textarea {
111
+ background: rgba(255, 255, 255, 0.03);
112
+ border: 1px solid rgba(255, 255, 255, 0.08);
113
+ backdrop-filter: blur(16px);
114
+ -webkit-backdrop-filter: blur(16px);
115
+ transition: all 0.3s ease;
116
+ }
117
+ .neon-textarea:focus {
118
+ outline: none;
119
+ border-color: rgba(251, 191, 36, 0.5);
120
+ box-shadow: 0 0 15px rgba(245, 158, 11, 0.25), inset 0 0 5px rgba(245, 158, 11, 0.05);
121
+ background: rgba(255, 255, 255, 0.06);
122
+ }
123
+ .copy-active {
124
+ box-shadow: 0 0 15px rgba(251, 191, 36, 0.6) !important;
125
+ border-color: rgba(251, 191, 36, 0.4) !important;
126
+ background: rgba(251, 191, 36, 0.1) !important;
127
+ }
128
+ </style>
129
+ <style>
130
+ body {
131
+ min-height: max(884px, 100dvh);
132
+ }
133
+ </style>
134
+ </head>
135
+ <body class="font-display text-white antialiased overflow-x-hidden min-h-screen flex flex-col selection:bg-amber-500 selection:text-black">
136
+ <header class="flex-none z-30 px-4 py-3 lg:px-6 lg:py-4 border-b border-white/5 glass-panel sticky top-0">
137
+ <div class="max-w-[1600px] mx-auto flex items-center justify-between gap-4">
138
+ <div class="flex items-center gap-3 lg:gap-4 flex-1 min-w-0">
139
+ <div class="flex-none flex items-center justify-center size-9 lg:size-10 rounded-xl bg-gradient-to-br from-amber-400 to-amber-700 shadow-lg shadow-amber-900/20 text-white">
140
+ <span class="material-symbols-outlined text-xl lg:text-2xl">all_inclusive</span>
141
+ </div>
142
+ <div class="min-w-0">
143
+ <h2 class="text-white text-base lg:text-xl font-bold leading-tight tracking-tight truncate">StyleSync AI</h2>
144
+ <div class="flex items-center gap-2">
145
+ <span class="size-1.5 rounded-full bg-amber-500 animate-pulse flex-none"></span>
146
+ <span class="text-[10px] text-amber-200/60 font-bold uppercase tracking-wider truncate">Enterprise Edition</span>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ <a class="hidden sm:inline-flex flex-none items-center justify-center rounded-full h-9 lg:h-10 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-xs lg:text-sm font-bold transition-all" href="/">
151
+ <span class="material-symbols-outlined text-lg lg:mr-2">arrow_back</span>
152
+ <span class="hidden lg:inline">Back to Home</span>
153
+ </a>
154
+ <button class="group relative flex-none flex cursor-pointer items-center justify-center overflow-hidden rounded-full h-9 lg:h-10 px-4 lg:px-6 bg-gradient-to-r from-amber-600 to-amber-800 hover:from-amber-500 hover:to-amber-700 border border-white/10 shadow-[0_0_15px_rgba(245,158,11,0.3)] transition-all" id="deployBtn">
155
+ <div class="absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity"></div>
156
+ <span class="material-symbols-outlined lg:mr-2 text-lg relative z-10">rocket_launch</span>
157
+ <span class="hidden lg:inline truncate text-sm font-bold tracking-wide relative z-10">Deploy</span>
158
+ </button>
159
+ </div>
160
+ </header>
161
+ <main class="flex-1 flex flex-col lg:flex-row relative z-10">
162
+ <div class="fixed top-1/4 -left-20 w-64 h-64 lg:w-96 lg:h-96 bg-amber-900/10 rounded-full blur-[120px] pointer-events-none z-0"></div>
163
+ <div class="fixed bottom-1/4 -right-20 w-64 h-64 lg:w-96 lg:h-96 bg-orange-900/5 rounded-full blur-[120px] pointer-events-none z-0"></div>
164
+ <div class="flex-none lg:flex-1 flex flex-col lg:border-r border-white/5 p-4 lg:p-8 bg-black/40 lg:overflow-y-auto lg:h-[calc(100vh-73px)] relative z-10">
165
+ <div class="max-w-xl w-full mx-auto flex flex-col gap-4 lg:gap-6 h-full">
166
+ <div class="flex items-center justify-between mb-2">
167
+ <h2 class="text-white text-xl lg:text-3xl font-bold tracking-tight">Input Data</h2>
168
+ <span class="bg-white/5 text-amber-200/80 px-2.5 py-1 rounded-full text-[10px] lg:text-xs font-semibold border border-white/10 backdrop-blur-sm">Step 1 of 2</span>
169
+ </div>
170
+ <div class="flex flex-col flex-none">
171
+ <input accept=".jpg,.jpeg,.png,.webp" class="hidden" id="fileInput" type="file"/>
172
+ <div class="group relative flex flex-col items-center justify-center gap-3 lg:gap-4 rounded-2xl lg:rounded-3xl border-2 border-dashed border-white/10 hover:border-amber-500/50 hover:bg-white/5 transition-all cursor-pointer min-h-[200px] lg:min-h-[260px] px-4 py-6 lg:px-6 lg:py-8 glass-card" id="dropZone">
173
+ <div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
174
+ <div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
175
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
176
+ </div>
177
+ <div class="flex flex-col items-center gap-1 relative z-10">
178
+ <p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
179
+ <p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
180
+ </div>
181
+ <button class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide" id="browseBtn">
182
+ Browse Files
183
+ </button>
184
+ </div>
185
+ </div>
186
+ <div class="flex flex-col gap-2 lg:gap-3 flex-1">
187
+ <label class="flex items-center justify-between px-1">
188
+ <span class="text-white text-sm lg:text-base font-semibold">Raw Product Specs</span>
189
+ <span class="text-[10px] lg:text-xs text-neutral-500 font-medium bg-black/40 px-2 py-0.5 rounded border border-white/5">JSON or Plain Text</span>
190
+ </label>
191
+ <div class="relative w-full">
192
+ <textarea class="form-input w-full min-h-[140px] lg:h-full lg:min-h-[180px] resize-y lg:resize-none rounded-xl lg:rounded-2xl text-neutral-100 placeholder:text-neutral-600 neon-textarea p-4 lg:p-5 text-sm leading-relaxed font-mono shadow-inner transition-all duration-300" id="productSpecs" placeholder="// Enter fabric details, dimensions, and SKU..."></textarea>
193
+ </div>
194
+ </div>
195
+ <div class="pt-2 pb-6 lg:pb-0">
196
+ <button class="group relative w-full cursor-pointer overflow-hidden rounded-xl lg:rounded-2xl h-12 lg:h-14 bg-gradient-to-r from-amber-600 via-amber-700 to-amber-800 hover:from-amber-500 hover:to-amber-700 transition-all border border-white/10 animate-pulse-slow animate-glow-pulse" id="startBtn">
197
+ <div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3 relative z-10">
198
+ <span class="text-white text-base lg:text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span>
199
+ <span class="material-symbols-outlined text-white text-lg lg:text-xl group-hover:translate-x-1 transition-transform">arrow_forward</span>
200
+ </div>
201
+ </button>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ <div class="flex-none lg:flex-1 flex flex-col p-4 lg:p-8 bg-black/60 lg:overflow-y-auto lg:h-[calc(100vh-73px)] relative">
206
+ <div class="absolute inset-0 opacity-[0.02] pointer-events-none" style="background-image: linear-gradient(to right, #ffffff 1px, transparent 1px), linear-gradient(to bottom, #ffffff 1px, transparent 1px); background-size: 40px 40px;"></div>
207
+ <div class="max-w-3xl w-full mx-auto flex flex-col gap-4 lg:gap-6 h-full relative z-10 pb-8 lg:pb-0">
208
+ <div class="flex items-center justify-between mb-2">
209
+ <h2 class="text-white text-xl lg:text-3xl font-bold tracking-tight">Generated Output</h2>
210
+ <div class="flex items-center gap-2">
211
+ <span class="hidden xs:inline-block bg-white/5 text-amber-200 px-2.5 py-1 rounded-full text-[10px] lg:text-xs font-semibold border border-white/10 backdrop-blur-sm">Step 2 of 2</span>
212
+ <div class="hidden xs:block h-6 w-px bg-white/10 mx-1"></div>
213
+ <button class="size-8 lg:size-9 flex items-center justify-center hover:bg-white/10 rounded-lg text-neutral-500 hover:text-amber-400 transition-all duration-300 border border-transparent" id="copyBtn" title="Copy JSON">
214
+ <span class="material-symbols-outlined text-lg lg:text-xl transition-all duration-300" id="copyIcon">content_copy</span>
215
+ </button>
216
+ <button class="size-8 lg:size-9 flex items-center justify-center hover:bg-white/10 rounded-lg text-neutral-500 hover:text-amber-400 transition-colors" id="downloadBtn" title="Download JSON">
217
+ <span class="material-symbols-outlined text-lg lg:text-xl">download</span>
218
+ </button>
219
+ </div>
220
+ </div>
221
+ <div class="grid grid-cols-1 xs:grid-cols-3 gap-3">
222
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
223
+ <div class="relative size-3 flex-none">
224
+ <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-500 opacity-75"></span>
225
+ <span class="relative inline-flex rounded-full size-3 bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.5)]"></span>
226
+ </div>
227
+ <div class="flex flex-col overflow-hidden min-w-0">
228
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">Vision Agent</span>
229
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Gemini Pro 1.5</span>
230
+ </div>
231
+ </div>
232
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
233
+ <div class="relative size-3 flex-none">
234
+ <span class="relative inline-flex rounded-full size-3 bg-amber-600 shadow-[0_0_10px_rgba(217,119,6,0.5)]"></span>
235
+ </div>
236
+ <div class="flex flex-col overflow-hidden min-w-0">
237
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">Reasoning Agent</span>
238
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Llama 3 70B</span>
239
+ </div>
240
+ </div>
241
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
242
+ <div class="relative size-3 flex-none">
243
+ <span class="relative inline-flex rounded-full size-3 bg-neutral-600 shadow-[0_0_10px_rgba(82,82,82,0.5)]"></span>
244
+ </div>
245
+ <div class="flex flex-col overflow-hidden min-w-0">
246
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">SEO Context</span>
247
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Pinecone DB</span>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ <div class="flex-none lg:flex-1 min-h-[400px] lg:min-h-0 rounded-xl lg:rounded-2xl bg-black/60 backdrop-blur-xl border border-white/5 flex flex-col overflow-hidden shadow-2xl relative glass-card group/output transition-all duration-500">
252
+ <div class="absolute top-0 right-0 w-64 h-64 bg-amber-500/5 rounded-full blur-[100px] pointer-events-none"></div>
253
+ <div class="flex items-center justify-between px-3 py-2.5 lg:px-4 lg:py-3 bg-white/5 border-b border-white/5">
254
+ <span class="text-[10px] lg:text-xs font-mono text-neutral-500 flex items-center gap-2">
255
+ <span class="material-symbols-outlined text-xs lg:text-sm">code</span>
256
+ output.json
257
+ </span>
258
+ <div class="flex gap-1.5">
259
+ <div class="size-2.5 lg:size-3 rounded-full bg-neutral-800 border border-white/10"></div>
260
+ <div class="size-2.5 lg:size-3 rounded-full bg-neutral-800 border border-white/10"></div>
261
+ <div class="size-2.5 lg:size-3 rounded-full bg-amber-500/20 border border-amber-500/30"></div>
262
+ </div>
263
+ </div>
264
+ <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
265
+ <pre><code class="language-json block" id="jsonOutput"><span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">01</span><span class="text-amber-400">{</span>
266
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">02</span> <span class="text-amber-600">"product_analysis"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">{</span>
267
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">03</span> <span class="text-amber-600">"title"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Noir Elite Series Artisan Timepiece"</span><span class="text-neutral-400">,</span>
268
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">04</span> <span class="text-amber-600">"category"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Luxury / Accessories"</span><span class="text-neutral-400">,</span>
269
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">05</span> <span class="text-amber-600">"features"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
270
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">06</span> <span class="text-amber-200">"Obsidian Finish"</span><span class="text-neutral-400">,</span>
271
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">07</span> <span class="text-amber-200">"Golden Accents"</span><span class="text-neutral-400">,</span>
272
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">08</span> <span class="text-amber-200">"Smart Haptic Interface"</span>
273
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">09</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
274
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">10</span> <span class="text-amber-600">"seo_tags"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
275
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">11</span> <span class="text-amber-200">"#luxurywear"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#amberstyle"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#premiumtech"</span>
276
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">12</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
277
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">13</span> <span class="text-amber-600">"sentiment_score"</span><span class="text-neutral-400">:</span> <span class="text-amber-500">0.99</span><span class="text-neutral-400">,</span>
278
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">14</span> <span class="text-amber-600">"market_fit"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Exceptional"</span>
279
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">15</span> <span class="text-amber-400">}</span><span class="text-neutral-400">,</span>
280
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">16</span> <span class="text-amber-600">"deployment_status"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Authorized"</span>
281
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">17</span><span class="text-amber-400">}</span></code></pre>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </main>
287
+ <footer class="flex-none flex items-center justify-center py-4 bg-black/80 border-t border-white/5 backdrop-blur-md z-10">
288
+ <span class="text-[10px] lg:text-xs text-neutral-600 font-medium tracking-wide">© 2026 StyleSync AI — All Rights Reserved</span>
289
+ </footer>
290
+ <script>
291
+ tailwind.config.theme.extend.animation = { shine: 'shine 1.5s infinite' }
292
+ tailwind.config.theme.extend.keyframes = {
293
+ shine: { '0%': { left: '-100%' }, '100%': { left: '200%' } }
294
+ }
295
+ const dropZone = document.getElementById('dropZone');
296
+ const fileInput = document.getElementById('fileInput');
297
+ const startBtn = document.getElementById('startBtn');
298
+ const jsonOutput = document.getElementById('jsonOutput');
299
+ const deployBtn = document.getElementById('deployBtn');
300
+ const copyBtn = document.getElementById('copyBtn');
301
+ const copyIcon = document.getElementById('copyIcon');
302
+ let selectedFile = null;
303
+ let isCatalogGenerated = false;
304
+ const defaultDropZoneContent = `
305
+ <div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
306
+ <div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
307
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
308
+ </div>
309
+ <div class="flex flex-col items-center gap-1 relative z-10">
310
+ <p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
311
+ <p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
312
+ </div>
313
+ <button id="browseBtn" class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide">
314
+ Browse Files
315
+ </button>
316
+ `;
317
+ function initDropZone() {
318
+ const currentBrowseBtn = document.getElementById('browseBtn');
319
+ if (currentBrowseBtn) {
320
+ currentBrowseBtn.addEventListener('click', (e) => {
321
+ e.preventDefault(); e.stopPropagation(); fileInput.click();
322
+ });
323
+ }
324
+ }
325
+ initDropZone();
326
+ fileInput.addEventListener('change', (e) => {
327
+ if (e.target.files.length > 0) handleFile(e.target.files[0]);
328
+ });
329
+ dropZone.addEventListener('dragover', (e) => {
330
+ e.preventDefault(); dropZone.classList.add('border-amber-500', 'bg-amber-500/5');
331
+ });
332
+ dropZone.addEventListener('dragleave', (e) => {
333
+ e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
334
+ });
335
+ dropZone.addEventListener('drop', (e) => {
336
+ e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
337
+ if (e.dataTransfer.files.length > 0) {
338
+ fileInput.files = e.dataTransfer.files;
339
+ handleFile(e.dataTransfer.files[0]);
340
+ }
341
+ });
342
+ function handleFile(file) {
343
+ selectedFile = file;
344
+ dropZone.innerHTML = `
345
+ <div class="flex flex-col items-center justify-center gap-4 z-10">
346
+ <div class="size-14 lg:size-16 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center">
347
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">check_circle</span>
348
+ </div>
349
+ <div class="flex flex-col items-center gap-1">
350
+ <p class="text-white text-base lg:text-lg font-bold text-center">${file.name}</p>
351
+ <p class="text-neutral-500 text-xs lg:text-sm text-center">${(file.size / 1024).toFixed(1)} KB</p>
352
+ </div>
353
+ <button id="removeFileBtn" class="mt-2 flex items-center justify-center gap-2 rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 transition-all text-[10px] lg:text-xs font-bold uppercase tracking-wide">
354
+ <span class="material-symbols-outlined text-sm lg:text-base">close</span>
355
+ <span>Remove File</span>
356
+ </button>
357
+ </div>
358
+ `;
359
+ document.getElementById('removeFileBtn').addEventListener('click', (e) => {
360
+ e.stopPropagation(); e.preventDefault(); resetUploadUI();
361
+ });
362
+ }
363
+ function resetUploadUI() {
364
+ selectedFile = null; fileInput.value = ""; dropZone.innerHTML = defaultDropZoneContent; initDropZone();
365
+ }
366
+ startBtn.addEventListener('click', async (e) => {
367
+ e.preventDefault();
368
+ if (!fileInput.files || fileInput.files.length === 0) {
369
+ alert("Please select or drop an image first."); return;
370
+ }
371
+ const originalBtnContent = startBtn.innerHTML;
372
+ startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3"><svg class="animate-spin h-4 w-4 lg:h-5 lg:w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span class="text-white text-base lg:text-lg font-bold tracking-wide">Synthesizing...</span></div>';
373
+ startBtn.disabled = true; startBtn.classList.remove('animate-pulse-slow', 'animate-glow-pulse');
374
+
375
+ try {
376
+ const formData = new FormData(); formData.append('file', fileInput.files[0]);
377
+ const response = await fetch('/generate-catalog', { method: 'POST', body: formData });
378
+ if (!response.ok) throw new Error("Server Error " + response.status);
379
+ const data = await response.json();
380
+ jsonOutput.textContent = JSON.stringify(data, null, 2);
381
+ isCatalogGenerated = true;
382
+ } catch (error) {
383
+ console.error("Agent Error:", error); alert("Pipeline failed: " + error.message);
384
+ } finally {
385
+ startBtn.innerHTML = originalBtnContent; startBtn.disabled = false; startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
386
+ }
387
+ });
388
+ copyBtn.addEventListener('click', () => {
389
+ navigator.clipboard.writeText(jsonOutput.innerText).then(() => {
390
+ const originalIcon = copyIcon.innerText; copyIcon.innerText = 'check'; copyIcon.classList.add('text-green-400');
391
+ setTimeout(() => { copyIcon.innerText = originalIcon; copyIcon.classList.remove('text-green-400'); }, 2000);
392
+ });
393
+ });
394
+ </script>
395
+ </body>
396
+ </html>
397
+
deploy_new_ui.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import subprocess
3
+ import sys
4
+
5
+ def main():
6
+ glassui_path = "glassui.html"
7
+ dashboard_path = "dashboard.html"
8
+
9
+ # Read the New Design
10
+ try:
11
+ with open(glassui_path, 'r', encoding='utf-8') as f:
12
+ glassui_content = f.read()
13
+ except FileNotFoundError:
14
+ print(f"Error: Could not find '{glassui_path}'.")
15
+ sys.exit(1)
16
+
17
+ # The Safety Scan (Crucial)
18
+ required_ids = [
19
+ "dropZone",
20
+ "fileInput",
21
+ "browseBtn",
22
+ "startBtn",
23
+ "deployBtn",
24
+ "jsonOutput",
25
+ "copyBtn",
26
+ "downloadBtn"
27
+ ]
28
+
29
+ for element_id in required_ids:
30
+ # Check if id="element_id" or id='element_id' exists
31
+ pattern = rf'id\s*=\s*[\'"]{element_id}[\'"]'
32
+ if not re.search(pattern, glassui_content):
33
+ print(f"⚠️ WARNING: Missing ID {element_id}")
34
+
35
+ # Overwrite
36
+ try:
37
+ with open(dashboard_path, 'w', encoding='utf-8') as f:
38
+ f.write(glassui_content)
39
+ print(f"Successfully copied '{glassui_path}' to '{dashboard_path}'.")
40
+ except Exception as e:
41
+ print(f"Error overwriting '{dashboard_path}': {e}")
42
+ sys.exit(1)
43
+
44
+ # Deploy
45
+ print("Deploying to Hugging Face...")
46
+ git_commands = [
47
+ ["git", "add", "dashboard.html"],
48
+ ["git", "commit", "-m", "UI Update: Apply responsive Glassmorphism design from Stitch"],
49
+ ["git", "push", "space", "clean_deploy:main"]
50
+ ]
51
+
52
+ for cmd in git_commands:
53
+ print(f"Running: {' '.join(cmd)}")
54
+ # Use encoding='utf-8' as strictly requested
55
+ result = subprocess.run(cmd, text=True, encoding='utf-8')
56
+ if result.returncode != 0:
57
+ print(f"Command failed: {' '.join(cmd)}")
58
+
59
+ if __name__ == "__main__":
60
+ main()
final_deploy_push.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import sys
3
+
4
+ # Force UTF-8 output for Windows terminals
5
+ sys.stdout.reconfigure(encoding='utf-8')
6
+
7
+ def deploy():
8
+ print("⚠️ Ensure you are inside the D:\\Projects\\StyleSync AI directory before running this!")
9
+
10
+ command = "git push --force space clean_deploy:main"
11
+ print(f"\nRunning: {command} ...")
12
+
13
+ try:
14
+ subprocess.run(command, check=True, shell=True)
15
+ print("\n✅ Successfully pushed to Space!")
16
+ except subprocess.CalledProcessError as e:
17
+ print(f"\n❌ Push failed: {e}")
18
+
19
+ if __name__ == "__main__":
20
+ deploy()
final_upload.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import sys
3
+
4
+ def run_command(command, check=True):
5
+ try:
6
+ subprocess.run(command, check=check, shell=True, text=True)
7
+ except subprocess.CalledProcessError as e:
8
+ print(f"Error executing currently: {command}")
9
+ # We don't exit here because some commands like 'remote remove' might fail meaningfully but we want to continue,
10
+ # or we handle them specifically in the main flow.
11
+ if check:
12
+ # Re-raise if we strictly wanted this to succeed
13
+ raise e
14
+
15
+ def main():
16
+ # Input: Ask the user for the GitHub URL
17
+ if len(sys.argv) > 1:
18
+ github_url = sys.argv[1].strip()
19
+ else:
20
+ github_url = input('Please paste your GitHub URL here: ').strip()
21
+
22
+ if not github_url:
23
+ print("Error: No URL provided.")
24
+ return
25
+
26
+ try:
27
+ # Git Commands sequence
28
+ print("Initializing git...")
29
+ run_command("git init")
30
+
31
+ print("Adding files...")
32
+ run_command("git add .")
33
+
34
+ print("Committing files...")
35
+ try:
36
+ # Use check=True so it raises exception on failure, which we catch
37
+ run_command('git commit -m "Initial commit - StyleSync AI"', check=True)
38
+ except subprocess.CalledProcessError:
39
+ print("Commit failed (likely nothing to commit). Continuing...")
40
+
41
+ print("Renaming branch to main...")
42
+ run_command("git branch -M main")
43
+
44
+ print("Removing existing origin (if any)...")
45
+ # Don't check=True here because it fails if origin doesn't exist
46
+ run_command("git remote remove origin", check=False)
47
+
48
+ print(f"Adding remote origin: {github_url}")
49
+ run_command(f"git remote add origin {github_url}")
50
+
51
+ print("Pushing to GitHub...")
52
+ run_command("git push -u origin main")
53
+
54
+ # Success message
55
+ print('✅ Code is live on GitHub!')
56
+
57
+ except Exception as e:
58
+ print(f"\n❌ An error occurred: {e}")
59
+
60
+ if __name__ == "__main__":
61
+ main()
fix_browse_button.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import subprocess
3
+ import re
4
+
5
+ def fix_html():
6
+ with open('dashboard.html', 'r', encoding='utf-8') as f:
7
+ content = f.read()
8
+
9
+ js_snippet = """
10
+ const browseBtn = document.getElementById('browseBtn');
11
+ browseBtn.addEventListener('click', (e) => {
12
+ e.preventDefault();
13
+ fileInput.click();
14
+ });
15
+ fileInput.addEventListener('change', () => {
16
+ if (fileInput.files.length > 0) {
17
+ // Provide a visual cue that a file was selected
18
+ const fileName = fileInput.files[0].name;
19
+ browseBtn.innerHTML = `<span class="material-symbols-outlined text-xl">check_circle</span> ${fileName}`;
20
+ }
21
+ });
22
+ """
23
+
24
+ if "browseBtn.addEventListener('click', (e) => {" in content and "Provide a visual cue that a file was selected" in content:
25
+ print("Snippet already exists. Skipping injection.")
26
+ else:
27
+ # Find where the DOM elements are defined
28
+ target = "const downloadBtn = document.getElementById('downloadBtn');"
29
+ if target in content:
30
+ new_content = content.replace(target, target + "\n" + js_snippet)
31
+ with open('dashboard.html', 'w', encoding='utf-8') as f:
32
+ f.write(new_content)
33
+ print("Injected JS successfully.")
34
+ else:
35
+ print("Could not find insertion point!")
36
+ return False
37
+
38
+ # Run git commands
39
+ subprocess.run(['git', 'add', 'dashboard.html'], check=True)
40
+ try:
41
+ subprocess.run(['git', 'commit', '-m', 'Bugfix: Wire up Browse Files button to hidden input'], check=True)
42
+ except subprocess.CalledProcessError:
43
+ print("Nothing to commit")
44
+
45
+ subprocess.run(['git', 'push', '--force', 'space', 'HEAD:main'], check=True)
46
+
47
+ # Push to origin main as well
48
+ try:
49
+ subprocess.run(['git', 'push', 'origin', 'HEAD:main'], check=True)
50
+ except subprocess.CalledProcessError:
51
+ print("Push to origin failed or not needed")
52
+
53
+ print("Deployment triggered successfully.")
54
+ return True
55
+
56
+ if __name__ == '__main__':
57
+ fix_html()
fix_dashboard_api.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import subprocess
3
+ import re
4
+
5
+ def patch_dashboard():
6
+ with open('dashboard.html', 'r', encoding='utf-8') as f:
7
+ content = f.read()
8
+
9
+ # Locate the setTimeout block inside startBtn.addEventListener and replace it with fetch logic
10
+ pattern = re.compile(r"setTimeout\(\(\) => \{[\s\S]*?\}, 1500\);", re.DOTALL)
11
+
12
+ new_block = """try {
13
+ const formData = new FormData();
14
+ formData.append('file', fileInput.files[0]);
15
+
16
+ const response = await fetch('/generate-catalog', {
17
+ method: 'POST',
18
+ body: formData
19
+ });
20
+
21
+ const data = await response.json();
22
+ jsonOutput.textContent = JSON.stringify(data, null, 2);
23
+ isCatalogGenerated = true;
24
+ } catch (error) {
25
+ console.error("Error generating catalog:", error);
26
+ } finally {
27
+ startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3 relative z-10"><span class="text-white text-base lg:text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span><span class="material-symbols-outlined text-white text-lg lg:text-xl group-hover:translate-x-1 transition-transform">arrow_forward</span></div>';
28
+ startBtn.disabled = false;
29
+ startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
30
+ }"""
31
+
32
+ if not pattern.search(content):
33
+ print("Error: Could not find the target setTimeout block in dashboard.html.")
34
+ return False
35
+
36
+ new_content = pattern.sub(new_block, content)
37
+
38
+ with open('dashboard.html', 'w', encoding='utf-8') as f:
39
+ f.write(new_content)
40
+
41
+ print("Successfully patched dashboard.html")
42
+ return True
43
+
44
+ def run_git_commands():
45
+ commands = [
46
+ ['git', 'add', 'dashboard.html'],
47
+ ['git', 'commit', '-m', 'Bugfix: Restore real API connection to Glassmorphism UI'],
48
+ ['git', 'push', '--force', 'space', 'HEAD:main']
49
+ ]
50
+
51
+ for cmd in commands:
52
+ print(f"Running: {' '.join(cmd)}")
53
+ result = subprocess.run(cmd, capture_output=True, text=True)
54
+ if result.returncode != 0:
55
+ print(f"Command failed with {result.returncode}: \\n{result.stderr}")
56
+ # Don't break here, let it try the other commands just in case, though push might fail if commit fails.
57
+ if cmd[1] == 'commit' and "nothing to commit" in result.stdout + result.stderr:
58
+ continue
59
+ if cmd[1] == 'push':
60
+ pass
61
+ else:
62
+ print(f"Success!\\n{result.stdout}")
63
+
64
+ if __name__ == '__main__':
65
+ if patch_dashboard():
66
+ run_git_commands()
fix_dashboard_routing.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+
4
+ def main():
5
+ # Define the content for main.py
6
+ main_py_content = """import os
7
+ from fastapi import FastAPI, UploadFile, File, HTTPException
8
+ from fastapi.responses import HTMLResponse, JSONResponse
9
+ from fastapi.staticfiles import StaticFiles
10
+ from agents.visual_analyst import VisualAnalyst
11
+ from dotenv import load_dotenv
12
+ # Load environment variables
13
+ load_dotenv()
14
+ app = FastAPI()
15
+ # Initialize Agent
16
+ visual_agent = VisualAnalyst()
17
+ # 1. READ THE DASHBOARD HTML FILE INTO MEMORY
18
+ try:
19
+ with open("dashboard.html", "r") as f:
20
+ dashboard_html = f.read()
21
+ except FileNotFoundError:
22
+ dashboard_html = "<h1>Error: dashboard.html not found. Please ensure the file exists.</h1>"
23
+ # 2. SERVE DASHBOARD AT ROOT (Home Page)
24
+ @app.get("/", response_class=HTMLResponse)
25
+ async def read_root():
26
+ return dashboard_html
27
+ # 3. KEEP /dashboard ROUTE AS BACKUP
28
+ @app.get("/dashboard", response_class=HTMLResponse)
29
+ async def read_dashboard():
30
+ return dashboard_html
31
+ @app.post("/analyze")
32
+ async def analyze_merch(file: UploadFile = File(...)):
33
+ try:
34
+ os.makedirs("uploads", exist_ok=True)
35
+ file_path = f"uploads/{file.filename}"
36
+ with open(file_path, "wb") as f:
37
+ f.write(await file.read())
38
+ result = await visual_agent.analyze_image(file_path)
39
+
40
+ if os.path.exists(file_path):
41
+ os.remove(file_path)
42
+
43
+ return JSONResponse(content=result)
44
+ except Exception as e:
45
+ return JSONResponse(content={"error": str(e)}, status_code=500)
46
+ if __name__ == "__main__":
47
+ import uvicorn
48
+ uvicorn.run(app, host="0.0.0.0", port=7860)
49
+ """
50
+
51
+ # Overwrite main.py
52
+ print("Overwriting main.py...")
53
+ try:
54
+ with open("main.py", "w", encoding="utf-8") as f:
55
+ f.write(main_py_content)
56
+ print("Successfully updated main.py")
57
+ except Exception as e:
58
+ print(f"Error writing main.py: {e}")
59
+ return
60
+
61
+ # Define git commands
62
+ git_commands = [
63
+ ["git", "add", "main.py"],
64
+ ["git", "commit", "-m", "Fix dashboard 404 by serving HTML at root"],
65
+ ["git", "push", "space", "clean_deploy:main"]
66
+ ]
67
+
68
+ # Run git commands
69
+ print("\nRunning git commands...")
70
+ for cmd in git_commands:
71
+ print(f"Executing: {' '.join(cmd)}")
72
+ try:
73
+ subprocess.run(cmd, check=True)
74
+ except subprocess.CalledProcessError as e:
75
+ print(f"Command failed: {e}")
76
+ # If commit fails (e.g. nothing to commit), we might want to continue or stop.
77
+ # But push should definitely happen if commit works.
78
+ # If commit fails because "nothing to commit, working tree clean", push might still be relevant if previous commit wasn't pushed?
79
+ # But the user logic implies we just made a change to main.py, so commit should succeed unless main.py was ALREADY this content.
80
+ # We will continue to try push even if commit fails, just in case.
81
+ # But wait, if commit fails, push might proceed.
82
+ pass
83
+
84
+ print("\nfix_dashboard_routing.py completed.")
85
+
86
+ if __name__ == "__main__":
87
+ main()
fix_google_key.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Force UTF-8 output for Windows terminals
5
+ sys.stdout.reconfigure(encoding='utf-8')
6
+
7
+ # 1. Update .env
8
+ env_path = ".env"
9
+ key = "GOOGLE_API_KEY"
10
+ value = "AIzaSyDgIkagGBciWNZDTn07OlfY9tVPvo6KJ1on"
11
+
12
+ print(f"Updating {key} in .env...")
13
+
14
+ lines = []
15
+ if os.path.exists(env_path):
16
+ with open(env_path, "r", encoding="utf-8") as f:
17
+ lines = f.readlines()
18
+
19
+ found = False
20
+ new_lines = []
21
+ for line in lines:
22
+ if line.startswith(f"{key}="):
23
+ new_lines.append(f"{key}={value}\n")
24
+ found = True
25
+ else:
26
+ new_lines.append(line)
27
+
28
+ if not found:
29
+ if new_lines and not new_lines[-1].endswith('\n'):
30
+ new_lines.append('\n')
31
+ new_lines.append(f"{key}={value}\n")
32
+
33
+ with open(env_path, "w", encoding="utf-8") as f:
34
+ f.writelines(new_lines)
35
+
36
+ print(f"✅ Updated {key} in .env")
37
+
38
+ # 2. Upload to Cloud
39
+ print("Syncing secrets to Hugging Face Space...")
40
+ try:
41
+ # Build path to ensure we can import upload_secrets
42
+ sys.path.append(os.getcwd())
43
+ from upload_secrets import upload_secrets
44
+
45
+ upload_secrets()
46
+ print("✅ Google Key saved locally and uploaded to Hugging Face!")
47
+ except Exception as e:
48
+ print(f"❌ Failed to sync: {e}")
fix_js_syntax.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import subprocess
3
+
4
+ def fix_html():
5
+ with open('dashboard.html', 'r', encoding='utf-8') as f:
6
+ content = f.read()
7
+
8
+ parts = content.split('</footer>')
9
+ if len(parts) < 2:
10
+ print("Could not find </footer> in dashboard.html")
11
+ return False
12
+
13
+ top_part = parts[0] + '</footer>\n'
14
+
15
+ new_script = """<script>
16
+ tailwind.config.theme.extend.animation = { shine: 'shine 1.5s infinite' }
17
+ tailwind.config.theme.extend.keyframes = {
18
+ shine: { '0%': { left: '-100%' }, '100%': { left: '200%' } }
19
+ }
20
+ const dropZone = document.getElementById('dropZone');
21
+ const fileInput = document.getElementById('fileInput');
22
+ const startBtn = document.getElementById('startBtn');
23
+ const jsonOutput = document.getElementById('jsonOutput');
24
+ const deployBtn = document.getElementById('deployBtn');
25
+ const copyBtn = document.getElementById('copyBtn');
26
+ const copyIcon = document.getElementById('copyIcon');
27
+ let selectedFile = null;
28
+ let isCatalogGenerated = false;
29
+ const defaultDropZoneContent = `
30
+ <div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
31
+ <div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
32
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
33
+ </div>
34
+ <div class="flex flex-col items-center gap-1 relative z-10">
35
+ <p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
36
+ <p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
37
+ </div>
38
+ <button id="browseBtn" class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide">
39
+ Browse Files
40
+ </button>
41
+ `;
42
+ function initDropZone() {
43
+ const currentBrowseBtn = document.getElementById('browseBtn');
44
+ if (currentBrowseBtn) {
45
+ currentBrowseBtn.addEventListener('click', (e) => {
46
+ e.preventDefault(); e.stopPropagation(); fileInput.click();
47
+ });
48
+ }
49
+ }
50
+ initDropZone();
51
+ fileInput.addEventListener('change', (e) => {
52
+ if (e.target.files.length > 0) handleFile(e.target.files[0]);
53
+ });
54
+ dropZone.addEventListener('dragover', (e) => {
55
+ e.preventDefault(); dropZone.classList.add('border-amber-500', 'bg-amber-500/5');
56
+ });
57
+ dropZone.addEventListener('dragleave', (e) => {
58
+ e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
59
+ });
60
+ dropZone.addEventListener('drop', (e) => {
61
+ e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
62
+ if (e.dataTransfer.files.length > 0) {
63
+ fileInput.files = e.dataTransfer.files;
64
+ handleFile(e.dataTransfer.files[0]);
65
+ }
66
+ });
67
+ function handleFile(file) {
68
+ selectedFile = file;
69
+ dropZone.innerHTML = `
70
+ <div class="flex flex-col items-center justify-center gap-4 z-10">
71
+ <div class="size-14 lg:size-16 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center">
72
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">check_circle</span>
73
+ </div>
74
+ <div class="flex flex-col items-center gap-1">
75
+ <p class="text-white text-base lg:text-lg font-bold text-center">${file.name}</p>
76
+ <p class="text-neutral-500 text-xs lg:text-sm text-center">${(file.size / 1024).toFixed(1)} KB</p>
77
+ </div>
78
+ <button id="removeFileBtn" class="mt-2 flex items-center justify-center gap-2 rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 transition-all text-[10px] lg:text-xs font-bold uppercase tracking-wide">
79
+ <span class="material-symbols-outlined text-sm lg:text-base">close</span>
80
+ <span>Remove File</span>
81
+ </button>
82
+ </div>
83
+ `;
84
+ document.getElementById('removeFileBtn').addEventListener('click', (e) => {
85
+ e.stopPropagation(); e.preventDefault(); resetUploadUI();
86
+ });
87
+ }
88
+ function resetUploadUI() {
89
+ selectedFile = null; fileInput.value = ""; dropZone.innerHTML = defaultDropZoneContent; initDropZone();
90
+ }
91
+ startBtn.addEventListener('click', async (e) => {
92
+ e.preventDefault();
93
+ if (!fileInput.files || fileInput.files.length === 0) {
94
+ alert("Please select or drop an image first."); return;
95
+ }
96
+ const originalBtnContent = startBtn.innerHTML;
97
+ startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3"><svg class="animate-spin h-4 w-4 lg:h-5 lg:w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span class="text-white text-base lg:text-lg font-bold tracking-wide">Synthesizing...</span></div>';
98
+ startBtn.disabled = true; startBtn.classList.remove('animate-pulse-slow', 'animate-glow-pulse');
99
+
100
+ try {
101
+ const formData = new FormData(); formData.append('file', fileInput.files[0]);
102
+ const response = await fetch('/generate-catalog', { method: 'POST', body: formData });
103
+ if (!response.ok) throw new Error("Server Error " + response.status);
104
+ const data = await response.json();
105
+ jsonOutput.textContent = JSON.stringify(data, null, 2);
106
+ isCatalogGenerated = true;
107
+ } catch (error) {
108
+ console.error("Agent Error:", error); alert("Pipeline failed: " + error.message);
109
+ } finally {
110
+ startBtn.innerHTML = originalBtnContent; startBtn.disabled = false; startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
111
+ }
112
+ });
113
+ copyBtn.addEventListener('click', () => {
114
+ navigator.clipboard.writeText(jsonOutput.innerText).then(() => {
115
+ const originalIcon = copyIcon.innerText; copyIcon.innerText = 'check'; copyIcon.classList.add('text-green-400');
116
+ setTimeout(() => { copyIcon.innerText = originalIcon; copyIcon.classList.remove('text-green-400'); }, 2000);
117
+ });
118
+ });
119
+ </script>
120
+ </body>
121
+ </html>
122
+ """
123
+
124
+ with open('dashboard.html', 'w', encoding='utf-8') as f:
125
+ f.write(top_part + new_script)
126
+
127
+ print("Replaced script successfully.")
128
+
129
+ # Run git commands
130
+ subprocess.run(['git', 'add', 'dashboard.html'], check=True)
131
+ try:
132
+ subprocess.run(['git', 'commit', '-m', 'Critical Bugfix: Resolve corrupted JS syntax and restore core agent loop'], check=True)
133
+ except subprocess.CalledProcessError:
134
+ print("Nothing to commit")
135
+
136
+ subprocess.run(['git', 'push', '--force', 'space', 'HEAD:main'], check=True)
137
+
138
+ # Push to origin main as well
139
+ try:
140
+ subprocess.run(['git', 'push', 'origin', 'HEAD:main'], check=True)
141
+ except subprocess.CalledProcessError:
142
+ print("Push to origin failed or not needed")
143
+
144
+ print("Deployment triggered successfully.")
145
+
146
+ if __name__ == '__main__':
147
+ fix_html()
fix_readme.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import subprocess
4
+
5
+ # Force UTF-8 output for Windows terminals
6
+ sys.stdout.reconfigure(encoding='utf-8')
7
+
8
+ readme_content = """---
9
+ title: StyleSync AI
10
+ emoji: 🚀
11
+ colorFrom: blue
12
+ colorTo: indigo
13
+ sdk: docker
14
+ pinned: false
15
+ ---
16
+ # StyleSync AI
17
+ An AI-powered catalog intelligence agent.
18
+ """
19
+
20
+ def run_command(command):
21
+ print(f"Running: {command}")
22
+ try:
23
+ subprocess.run(command, check=True, shell=True)
24
+ print("✅ Success")
25
+ except subprocess.CalledProcessError as e:
26
+ print(f"❌ Error: {e}")
27
+ # Don't exit, try to continue or let user see error
28
+
29
+ def fix_readme():
30
+ print("Writing README.md...")
31
+ with open("README.md", "w", encoding="utf-8") as f:
32
+ f.write(readme_content)
33
+ print("✅ Created README.md")
34
+
35
+ print("Deploying changes...")
36
+ run_command("git add README.md")
37
+ run_command('git commit -m "Add Hugging Face configuration"')
38
+ run_command("git push space clean_deploy:main")
39
+ print("✅ Configuration fixed and pushed!")
40
+
41
+ if __name__ == "__main__":
42
+ fix_readme()
fix_vision_core.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+
4
+ def fix_vision_core():
5
+ # Content for agents/visual_analyst.py
6
+ content = """import os
7
+ import json
8
+ import asyncio
9
+ import google.generativeai as genai
10
+ from PIL import Image
11
+ from dotenv import load_dotenv
12
+
13
+ load_dotenv()
14
+
15
+ class VisualAnalyst:
16
+ def __init__(self):
17
+ api_key = os.getenv("GEMINI_API_KEY")
18
+ if not api_key:
19
+ print("⚠️ GEMINI_API_KEY missing")
20
+
21
+ genai.configure(api_key=api_key)
22
+ # Use the modern, faster Flash model
23
+ self.model = genai.GenerativeModel('gemini-1.5-flash')
24
+
25
+ async def analyze_image(self, image_path: str):
26
+ print(f"👁️ Analyzing image: {image_path}")
27
+
28
+ try:
29
+ # 1. Load image properly with Pillow (Fixes format issues)
30
+ img = Image.open(image_path)
31
+
32
+ # 2. Define the prompt
33
+ prompt = \"\"\"
34
+ Analyze this product image for an e-commerce listing.
35
+ Return ONLY a raw JSON object (no markdown formatting) with this structure:
36
+ {
37
+ "main_color": "string",
38
+ "product_type": "string",
39
+ "design_style": "string (minimalist, streetwear, vintage, etc)",
40
+ "visual_features": ["list", "of", "visible", "features"],
41
+ "suggested_title": "creative product title",
42
+ "condition_guess": "new/used"
43
+ }
44
+ \"\"\"
45
+
46
+ # 3. Run in a thread to prevent blocking (Sync to Async wrapper)
47
+ response = await asyncio.to_thread(
48
+ self.model.generate_content,
49
+ [prompt, img]
50
+ )
51
+
52
+ # 4. Clean and Parse JSON
53
+ text_response = response.text.replace('```json', '').replace('```', '').strip()
54
+ return json.loads(text_response)
55
+ except Exception as e:
56
+ print(f"❌ Vision Error: {e}")
57
+ # Return a Safe Fallback (Simulation)
58
+ return {
59
+ "main_color": "Unknown",
60
+ "product_type": "Unidentified Item",
61
+ "design_style": "Standard",
62
+ "visual_features": ["Error analyzing image"],
63
+ "suggested_title": "Manual Review Needed",
64
+ "condition_guess": "New"
65
+ }
66
+ """
67
+ # Write the file
68
+ os.makedirs("agents", exist_ok=True)
69
+ with open("agents/visual_analyst.py", "w", encoding="utf-8") as f:
70
+ f.write(content)
71
+ print("✅ agents/visual_analyst.py updated.")
72
+
73
+ # Git operations
74
+ print("🚀 Pushing to HuggingFace...")
75
+ commands = [
76
+ ["git", "add", "agents/visual_analyst.py"],
77
+ ["git", "commit", "-m", "Fix vision core and error handling"],
78
+ ["git", "push", "space", "clean_deploy:main"]
79
+ ]
80
+
81
+ for cmd in commands:
82
+ try:
83
+ print(f"Running: {' '.join(cmd)}")
84
+ subprocess.run(cmd, check=True)
85
+ except subprocess.CalledProcessError as e:
86
+ print(f"⚠️ Command failed: {e}")
87
+ # Continue even if commit fails (e.g. prompt already applied)
88
+
89
+ if __name__ == "__main__":
90
+ fix_vision_core()
force_deploy.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ force_deploy.py
3
+ Force-deploys the current codebase to the Hugging Face Space.
4
+ Handles HF binary file restrictions by untracking offending files first.
5
+ """
6
+ import subprocess
7
+ import sys
8
+
9
+ def run(cmd, allow_fail=False):
10
+ print(f"\n>>> {cmd}")
11
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
12
+ if result.stdout:
13
+ print(result.stdout.strip())
14
+ if result.stderr:
15
+ print(result.stderr.strip())
16
+ if result.returncode != 0 and not allow_fail:
17
+ print(f"Command failed with exit code {result.returncode}")
18
+ sys.exit(1)
19
+ return result
20
+
21
+ if __name__ == "__main__":
22
+ print("=" * 60)
23
+ print(" StyleSync AI — Force Deploy to Hugging Face Space")
24
+ print("=" * 60)
25
+
26
+ # 0. Remove binary files from Git tracking (keep local copies)
27
+ binaries = [
28
+ "stitch_stylesync_ai_dashboard.zip",
29
+ "screen.jpg",
30
+ "test_image.jpg",
31
+ ]
32
+ for b in binaries:
33
+ run(f"git rm --cached {b}", allow_fail=True)
34
+
35
+ # Ensure .gitignore blocks them from being re-added
36
+ ignore_entries = binaries + ["*.zip", "*.jpg", "*.jpeg", "*.png"]
37
+ try:
38
+ existing = open(".gitignore", "r").read()
39
+ except FileNotFoundError:
40
+ existing = ""
41
+ with open(".gitignore", "a") as f:
42
+ for entry in ignore_entries:
43
+ if entry not in existing:
44
+ f.write(f"\n{entry}")
45
+ print("\nUpdated .gitignore with binary exclusions.")
46
+
47
+ # 1. Stage everything
48
+ run("git add .")
49
+
50
+ # 2. Commit (allow fail in case nothing changed)
51
+ run('git commit -m "Critical Deployment: Force update of License, Security Fixes, and Branding"', allow_fail=True)
52
+
53
+ # 3. Force push HEAD to space remote's main branch
54
+ run("git push --force space HEAD:main")
55
+
56
+ # 4. Print the latest commit for verification
57
+ print("\n" + "=" * 60)
58
+ print(" Deployed Commit:")
59
+ print("=" * 60)
60
+ run("git log -1")
61
+
62
+ print("\n✅ Force deploy complete.")
force_push.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+
3
+ def main():
4
+ print("Executing force push...")
5
+ command = "git push --force space HEAD:main"
6
+ result = subprocess.run(command, shell=True, capture_output=True, text=True)
7
+
8
+ print("STDOUT:")
9
+ print(result.stdout)
10
+ print("STDERR:")
11
+ print(result.stderr)
12
+
13
+ if __name__ == "__main__":
14
+ main()
glassui.html ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html class="dark" lang="en"><head>
3
+ <meta charset="utf-8"/>
4
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
5
+ <title>StyleSync AI Dashboard</title>
6
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
7
+ <link href="https://fonts.googleapis.com" rel="preconnect"/>
8
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "primary": "#f59e0b",
18
+ "primary-hover": "#d97706",
19
+ "glass-border": "rgba(255, 255, 255, 0.12)",
20
+ "glass-surface": "rgba(255, 255, 255, 0.03)",
21
+ "glass-surface-hover": "rgba(255, 255, 255, 0.07)",
22
+ "charcoal": "#0a0a0a",
23
+ "amber-accent": "#fbbf24"
24
+ },
25
+ fontFamily: {
26
+ "display": ["Plus Jakarta Sans", "sans-serif"],
27
+ "mono": ["monospace"]
28
+ },
29
+ borderRadius: { "DEFAULT": "1rem", "lg": "1.5rem", "xl": "2rem", "2xl": "3rem", "full": "9999px" },
30
+ backdropBlur: {
31
+ 'xs': '2px',
32
+ },
33
+ screens: {
34
+ 'xs': '480px',
35
+ },
36
+ animation: {
37
+ 'pulse-slow': 'pulse-slow 3s infinite',
38
+ 'glow-pulse': 'glow-pulse 3s infinite',
39
+ 'copy-success': 'copy-success 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
40
+ },
41
+ keyframes: {
42
+ 'pulse-slow': {
43
+ '0%, 100%': { transform: 'scale(1)' },
44
+ '50%': { transform: 'scale(1.02)' },
45
+ },
46
+ 'glow-pulse': {
47
+ '0%, 100%': { boxShadow: '0 0 0 0 rgba(245, 158, 11, 0.4)' },
48
+ '50%': { boxShadow: '0 0 20px 5px rgba(251, 191, 36, 0.5)' },
49
+ },
50
+ 'copy-success': {
51
+ '0%': { transform: 'scale(0.8)', opacity: '0' },
52
+ '100%': { transform: 'scale(1)', opacity: '1' },
53
+ }
54
+ }
55
+ },
56
+ },
57
+ }
58
+ </script>
59
+ <style type="text/tailwindcss">
60
+ .custom-scrollbar::-webkit-scrollbar {
61
+ width: 6px;
62
+ height: 6px;
63
+ }
64
+ .custom-scrollbar::-webkit-scrollbar-track {
65
+ background: transparent;
66
+ }
67
+ .custom-scrollbar::-webkit-scrollbar-thumb {
68
+ background: rgba(255, 255, 255, 0.1);
69
+ border-radius: 99px;
70
+ }
71
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
72
+ background: rgba(255, 255, 255, 0.2);
73
+ }
74
+ body {
75
+ background-color: #0a0a0a;
76
+ background-image:
77
+ radial-gradient(at 0% 0%, rgba(20, 20, 20, 1) 0, transparent 50%),
78
+ radial-gradient(at 50% -10%, rgba(217, 119, 6, 0.15) 0, transparent 60%),
79
+ radial-gradient(at 100% 0%, rgba(251, 191, 36, 0.1) 0, transparent 50%),
80
+ radial-gradient(at 80% 80%, rgba(217, 119, 6, 0.12) 0, transparent 50%);
81
+ background-attachment: fixed;
82
+ min-height: 100vh;
83
+ }
84
+ .glass-panel {
85
+ background: rgba(15, 15, 15, 0.75);
86
+ backdrop-filter: blur(20px);
87
+ -webkit-backdrop-filter: blur(20px);
88
+ border: 1px solid rgba(255, 255, 255, 0.08);
89
+ }
90
+ @media (max-width: 768px) {
91
+ .glass-panel {
92
+ backdrop-filter: blur(14px);
93
+ -webkit-backdrop-filter: blur(14px);
94
+ background: rgba(10, 10, 10, 0.85);
95
+ }
96
+ }
97
+ .glass-card {
98
+ background: rgba(255, 255, 255, 0.02);
99
+ border: 1px solid rgba(255, 255, 255, 0.05);
100
+ backdrop-filter: blur(12px);
101
+ transition: all 0.3s ease;
102
+ }
103
+ @media (hover: hover) {
104
+ .glass-card:hover {
105
+ border-color: rgba(251, 191, 36, 0.3);
106
+ box-shadow: 0 0 25px rgba(245, 158, 11, 0.15);
107
+ background: rgba(255, 255, 255, 0.04);
108
+ }
109
+ }
110
+ .neon-textarea {
111
+ background: rgba(255, 255, 255, 0.03);
112
+ border: 1px solid rgba(255, 255, 255, 0.08);
113
+ backdrop-filter: blur(16px);
114
+ -webkit-backdrop-filter: blur(16px);
115
+ transition: all 0.3s ease;
116
+ }
117
+ .neon-textarea:focus {
118
+ outline: none;
119
+ border-color: rgba(251, 191, 36, 0.5);
120
+ box-shadow: 0 0 15px rgba(245, 158, 11, 0.25), inset 0 0 5px rgba(245, 158, 11, 0.05);
121
+ background: rgba(255, 255, 255, 0.06);
122
+ }
123
+ .copy-active {
124
+ box-shadow: 0 0 15px rgba(251, 191, 36, 0.6) !important;
125
+ border-color: rgba(251, 191, 36, 0.4) !important;
126
+ background: rgba(251, 191, 36, 0.1) !important;
127
+ }
128
+ </style>
129
+ <style>
130
+ body {
131
+ min-height: max(884px, 100dvh);
132
+ }
133
+ </style>
134
+ </head>
135
+ <body class="font-display text-white antialiased overflow-x-hidden min-h-screen flex flex-col selection:bg-amber-500 selection:text-black">
136
+ <header class="flex-none z-30 px-4 py-3 lg:px-6 lg:py-4 border-b border-white/5 glass-panel sticky top-0">
137
+ <div class="max-w-[1600px] mx-auto flex items-center justify-between gap-4">
138
+ <div class="flex items-center gap-3 lg:gap-4 flex-1 min-w-0">
139
+ <div class="flex-none flex items-center justify-center size-9 lg:size-10 rounded-xl bg-gradient-to-br from-amber-400 to-amber-700 shadow-lg shadow-amber-900/20 text-white">
140
+ <span class="material-symbols-outlined text-xl lg:text-2xl">all_inclusive</span>
141
+ </div>
142
+ <div class="min-w-0">
143
+ <h2 class="text-white text-base lg:text-xl font-bold leading-tight tracking-tight truncate">StyleSync AI</h2>
144
+ <div class="flex items-center gap-2">
145
+ <span class="size-1.5 rounded-full bg-amber-500 animate-pulse flex-none"></span>
146
+ <span class="text-[10px] text-amber-200/60 font-bold uppercase tracking-wider truncate">Enterprise Edition</span>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ <button class="group relative flex-none flex cursor-pointer items-center justify-center overflow-hidden rounded-full h-9 lg:h-10 px-4 lg:px-6 bg-gradient-to-r from-amber-600 to-amber-800 hover:from-amber-500 hover:to-amber-700 border border-white/10 shadow-[0_0_15px_rgba(245,158,11,0.3)] transition-all" id="deployBtn">
151
+ <div class="absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity"></div>
152
+ <span class="material-symbols-outlined lg:mr-2 text-lg relative z-10">rocket_launch</span>
153
+ <span class="hidden lg:inline truncate text-sm font-bold tracking-wide relative z-10">Deploy</span>
154
+ </button>
155
+ </div>
156
+ </header>
157
+ <main class="flex-1 flex flex-col lg:flex-row relative z-10">
158
+ <div class="fixed top-1/4 -left-20 w-64 h-64 lg:w-96 lg:h-96 bg-amber-900/10 rounded-full blur-[120px] pointer-events-none z-0"></div>
159
+ <div class="fixed bottom-1/4 -right-20 w-64 h-64 lg:w-96 lg:h-96 bg-orange-900/5 rounded-full blur-[120px] pointer-events-none z-0"></div>
160
+ <div class="flex-none lg:flex-1 flex flex-col lg:border-r border-white/5 p-4 lg:p-8 bg-black/40 lg:overflow-y-auto lg:h-[calc(100vh-73px)] relative z-10">
161
+ <div class="max-w-xl w-full mx-auto flex flex-col gap-4 lg:gap-6 h-full">
162
+ <div class="flex items-center justify-between mb-2">
163
+ <h2 class="text-white text-xl lg:text-3xl font-bold tracking-tight">Input Data</h2>
164
+ <span class="bg-white/5 text-amber-200/80 px-2.5 py-1 rounded-full text-[10px] lg:text-xs font-semibold border border-white/10 backdrop-blur-sm">Step 1 of 2</span>
165
+ </div>
166
+ <div class="flex flex-col flex-none">
167
+ <input accept=".jpg,.jpeg,.png,.webp" class="hidden" id="fileInput" type="file"/>
168
+ <div class="group relative flex flex-col items-center justify-center gap-3 lg:gap-4 rounded-2xl lg:rounded-3xl border-2 border-dashed border-white/10 hover:border-amber-500/50 hover:bg-white/5 transition-all cursor-pointer min-h-[200px] lg:min-h-[260px] px-4 py-6 lg:px-6 lg:py-8 glass-card" id="dropZone">
169
+ <div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
170
+ <div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
171
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
172
+ </div>
173
+ <div class="flex flex-col items-center gap-1 relative z-10">
174
+ <p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
175
+ <p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
176
+ </div>
177
+ <button class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide" id="browseBtn">
178
+ Browse Files
179
+ </button>
180
+ </div>
181
+ </div>
182
+ <div class="flex flex-col gap-2 lg:gap-3 flex-1">
183
+ <label class="flex items-center justify-between px-1">
184
+ <span class="text-white text-sm lg:text-base font-semibold">Raw Product Specs</span>
185
+ <span class="text-[10px] lg:text-xs text-neutral-500 font-medium bg-black/40 px-2 py-0.5 rounded border border-white/5">JSON or Plain Text</span>
186
+ </label>
187
+ <div class="relative w-full">
188
+ <textarea class="form-input w-full min-h-[140px] lg:h-full lg:min-h-[180px] resize-y lg:resize-none rounded-xl lg:rounded-2xl text-neutral-100 placeholder:text-neutral-600 neon-textarea p-4 lg:p-5 text-sm leading-relaxed font-mono shadow-inner transition-all duration-300" id="productSpecs" placeholder="// Enter fabric details, dimensions, and SKU..."></textarea>
189
+ </div>
190
+ </div>
191
+ <div class="pt-2 pb-6 lg:pb-0">
192
+ <button class="group relative w-full cursor-pointer overflow-hidden rounded-xl lg:rounded-2xl h-12 lg:h-14 bg-gradient-to-r from-amber-600 via-amber-700 to-amber-800 hover:from-amber-500 hover:to-amber-700 transition-all border border-white/10 animate-pulse-slow animate-glow-pulse" id="startBtn">
193
+ <div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3 relative z-10">
194
+ <span class="text-white text-base lg:text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span>
195
+ <span class="material-symbols-outlined text-white text-lg lg:text-xl group-hover:translate-x-1 transition-transform">arrow_forward</span>
196
+ </div>
197
+ </button>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ <div class="flex-none lg:flex-1 flex flex-col p-4 lg:p-8 bg-black/60 lg:overflow-y-auto lg:h-[calc(100vh-73px)] relative">
202
+ <div class="absolute inset-0 opacity-[0.02] pointer-events-none" style="background-image: linear-gradient(to right, #ffffff 1px, transparent 1px), linear-gradient(to bottom, #ffffff 1px, transparent 1px); background-size: 40px 40px;"></div>
203
+ <div class="max-w-3xl w-full mx-auto flex flex-col gap-4 lg:gap-6 h-full relative z-10 pb-8 lg:pb-0">
204
+ <div class="flex items-center justify-between mb-2">
205
+ <h2 class="text-white text-xl lg:text-3xl font-bold tracking-tight">Generated Output</h2>
206
+ <div class="flex items-center gap-2">
207
+ <span class="hidden xs:inline-block bg-white/5 text-amber-200 px-2.5 py-1 rounded-full text-[10px] lg:text-xs font-semibold border border-white/10 backdrop-blur-sm">Step 2 of 2</span>
208
+ <div class="hidden xs:block h-6 w-px bg-white/10 mx-1"></div>
209
+ <button class="size-8 lg:size-9 flex items-center justify-center hover:bg-white/10 rounded-lg text-neutral-500 hover:text-amber-400 transition-all duration-300 border border-transparent" id="copyBtn" title="Copy JSON">
210
+ <span class="material-symbols-outlined text-lg lg:text-xl transition-all duration-300" id="copyIcon">content_copy</span>
211
+ </button>
212
+ <button class="size-8 lg:size-9 flex items-center justify-center hover:bg-white/10 rounded-lg text-neutral-500 hover:text-amber-400 transition-colors" id="downloadBtn" title="Download JSON">
213
+ <span class="material-symbols-outlined text-lg lg:text-xl">download</span>
214
+ </button>
215
+ </div>
216
+ </div>
217
+ <div class="grid grid-cols-1 xs:grid-cols-3 gap-3">
218
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
219
+ <div class="relative size-3 flex-none">
220
+ <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-500 opacity-75"></span>
221
+ <span class="relative inline-flex rounded-full size-3 bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.5)]"></span>
222
+ </div>
223
+ <div class="flex flex-col overflow-hidden min-w-0">
224
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">Vision Agent</span>
225
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Gemini Pro 1.5</span>
226
+ </div>
227
+ </div>
228
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
229
+ <div class="relative size-3 flex-none">
230
+ <span class="relative inline-flex rounded-full size-3 bg-amber-600 shadow-[0_0_10px_rgba(217,119,6,0.5)]"></span>
231
+ </div>
232
+ <div class="flex flex-col overflow-hidden min-w-0">
233
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">Reasoning Agent</span>
234
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Llama 3 70B</span>
235
+ </div>
236
+ </div>
237
+ <div class="flex items-center gap-3 p-3 lg:p-4 rounded-xl glass-card cursor-default group">
238
+ <div class="relative size-3 flex-none">
239
+ <span class="relative inline-flex rounded-full size-3 bg-neutral-600 shadow-[0_0_10px_rgba(82,82,82,0.5)]"></span>
240
+ </div>
241
+ <div class="flex flex-col overflow-hidden min-w-0">
242
+ <span class="text-[9px] lg:text-[10px] uppercase tracking-wider text-neutral-500 truncate group-hover:text-amber-300 transition-colors">SEO Context</span>
243
+ <span class="text-xs lg:text-sm font-bold text-white truncate">Pinecone DB</span>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ <div class="flex-none lg:flex-1 min-h-[400px] lg:min-h-0 rounded-xl lg:rounded-2xl bg-black/60 backdrop-blur-xl border border-white/5 flex flex-col overflow-hidden shadow-2xl relative glass-card group/output transition-all duration-500">
248
+ <div class="absolute top-0 right-0 w-64 h-64 bg-amber-500/5 rounded-full blur-[100px] pointer-events-none"></div>
249
+ <div class="flex items-center justify-between px-3 py-2.5 lg:px-4 lg:py-3 bg-white/5 border-b border-white/5">
250
+ <span class="text-[10px] lg:text-xs font-mono text-neutral-500 flex items-center gap-2">
251
+ <span class="material-symbols-outlined text-xs lg:text-sm">code</span>
252
+ output.json
253
+ </span>
254
+ <div class="flex gap-1.5">
255
+ <div class="size-2.5 lg:size-3 rounded-full bg-neutral-800 border border-white/10"></div>
256
+ <div class="size-2.5 lg:size-3 rounded-full bg-neutral-800 border border-white/10"></div>
257
+ <div class="size-2.5 lg:size-3 rounded-full bg-amber-500/20 border border-amber-500/30"></div>
258
+ </div>
259
+ </div>
260
+ <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
261
+ <pre><code class="language-json block" id="jsonOutput"><span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">01</span><span class="text-amber-400">{</span>
262
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">02</span> <span class="text-amber-600">"product_analysis"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">{</span>
263
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">03</span> <span class="text-amber-600">"title"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Noir Elite Series Artisan Timepiece"</span><span class="text-neutral-400">,</span>
264
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">04</span> <span class="text-amber-600">"category"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Luxury / Accessories"</span><span class="text-neutral-400">,</span>
265
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">05</span> <span class="text-amber-600">"features"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
266
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">06</span> <span class="text-amber-200">"Obsidian Finish"</span><span class="text-neutral-400">,</span>
267
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">07</span> <span class="text-amber-200">"Golden Accents"</span><span class="text-neutral-400">,</span>
268
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">08</span> <span class="text-amber-200">"Smart Haptic Interface"</span>
269
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">09</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
270
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">10</span> <span class="text-amber-600">"seo_tags"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
271
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">11</span> <span class="text-amber-200">"#luxurywear"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#amberstyle"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#premiumtech"</span>
272
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">12</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
273
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">13</span> <span class="text-amber-600">"sentiment_score"</span><span class="text-neutral-400">:</span> <span class="text-amber-500">0.99</span><span class="text-neutral-400">,</span>
274
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">14</span> <span class="text-amber-600">"market_fit"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Exceptional"</span>
275
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">15</span> <span class="text-amber-400">}</span><span class="text-neutral-400">,</span>
276
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">16</span> <span class="text-amber-600">"deployment_status"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Authorized"</span>
277
+ <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">17</span><span class="text-amber-400">}</span></code></pre>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </main>
283
+ <footer class="flex-none flex items-center justify-center py-4 bg-black/80 border-t border-white/5 backdrop-blur-md z-10">
284
+ <span class="text-[10px] lg:text-xs text-neutral-600 font-medium tracking-wide">© 2026 StyleSync AI — All Rights Reserved</span>
285
+ </footer>
286
+ <script>
287
+ tailwind.config.theme.extend.animation = {
288
+ shine: 'shine 1.5s infinite',
289
+ }
290
+ tailwind.config.theme.extend.keyframes = {
291
+ shine: {
292
+ '0%': { left: '-100%' },
293
+ '100%': { left: '200%' },
294
+ }
295
+ }
296
+ const dropZone = document.getElementById('dropZone');
297
+ const fileInput = document.getElementById('fileInput');
298
+ const startBtn = document.getElementById('startBtn');
299
+ const jsonOutput = document.getElementById('jsonOutput');
300
+ const deployBtn = document.getElementById('deployBtn');
301
+ const copyBtn = document.getElementById('copyBtn');
302
+ const copyIcon = document.getElementById('copyIcon');
303
+ const downloadBtn = document.getElementById('downloadBtn');
304
+ let selectedFile = null;
305
+ let isCatalogGenerated = false;
306
+ const defaultDropZoneContent = `
307
+ <div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
308
+ <div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
309
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
310
+ </div>
311
+ <div class="flex flex-col items-center gap-1 relative z-10">
312
+ <p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
313
+ <p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
314
+ </div>
315
+ <button id="browseBtn"
316
+ class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide">
317
+ Browse Files
318
+ </button>
319
+ `;
320
+ function initDropZone() {
321
+ const browseBtn = document.getElementById('browseBtn');
322
+ if (browseBtn) {
323
+ browseBtn.addEventListener('click', (e) => {
324
+ e.stopPropagation();
325
+ fileInput.click();
326
+ });
327
+ }
328
+ }
329
+ initDropZone();
330
+ fileInput.addEventListener('change', (e) => {
331
+ if (e.target.files.length > 0) {
332
+ handleFile(e.target.files[0]);
333
+ }
334
+ });
335
+ dropZone.addEventListener('dragover', (e) => {
336
+ e.preventDefault();
337
+ dropZone.classList.add('border-amber-500');
338
+ dropZone.classList.add('bg-amber-500/5');
339
+ });
340
+ dropZone.addEventListener('dragleave', (e) => {
341
+ e.preventDefault();
342
+ dropZone.classList.remove('border-amber-500');
343
+ dropZone.classList.remove('bg-amber-500/5');
344
+ });
345
+ dropZone.addEventListener('drop', (e) => {
346
+ e.preventDefault();
347
+ dropZone.classList.remove('border-amber-500');
348
+ dropZone.classList.remove('bg-amber-500/5');
349
+ if (e.dataTransfer.files.length > 0) {
350
+ handleFile(e.dataTransfer.files[0]);
351
+ }
352
+ });
353
+ function handleFile(file) {
354
+ selectedFile = file;
355
+ dropZone.innerHTML = `
356
+ <div class="flex flex-col items-center justify-center gap-4 z-10">
357
+ <div class="size-14 lg:size-16 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center">
358
+ <span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">check_circle</span>
359
+ </div>
360
+ <div class="flex flex-col items-center gap-1">
361
+ <p class="text-white text-base lg:text-lg font-bold text-center">${file.name}</p>
362
+ <p class="text-neutral-500 text-xs lg:text-sm text-center">${(file.size / 1024).toFixed(1)} KB</p>
363
+ </div>
364
+ <button id="removeFileBtn" class="mt-2 flex items-center justify-center gap-2 rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 transition-all text-[10px] lg:text-xs font-bold uppercase tracking-wide">
365
+ <span class="material-symbols-outlined text-sm lg:text-base">close</span>
366
+ <span>Remove File</span>
367
+ </button>
368
+ </div>
369
+ `;
370
+ document.getElementById('removeFileBtn').addEventListener('click', (e) => {
371
+ e.stopPropagation();
372
+ resetUploadUI();
373
+ });
374
+ }
375
+ function resetUploadUI() {
376
+ selectedFile = null;
377
+ fileInput.value = "";
378
+ dropZone.innerHTML = defaultDropZoneContent;
379
+ initDropZone();
380
+ }
381
+ deployBtn.addEventListener('click', () => {
382
+ if (!isCatalogGenerated) {
383
+ alert("Please generate a catalog first before deploying.");
384
+ return;
385
+ }
386
+ const originalContent = deployBtn.innerHTML;
387
+ deployBtn.disabled = true;
388
+ deployBtn.innerHTML = `
389
+ <svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
390
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
391
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
392
+ </svg>
393
+ `;
394
+ setTimeout(() => {
395
+ alert("Success: Catalog pushed to storefront!");
396
+ deployBtn.innerHTML = originalContent;
397
+ deployBtn.disabled = false;
398
+ }, 1500);
399
+ });
400
+ copyBtn.addEventListener('click', () => {
401
+ const textToCopy = jsonOutput.innerText;
402
+ navigator.clipboard.writeText(textToCopy).then(() => {
403
+ const originalIcon = copyIcon.innerText;
404
+ copyIcon.style.opacity = '0';
405
+ copyIcon.style.transform = 'scale(0.8)';
406
+ setTimeout(() => {
407
+ copyIcon.innerText = 'check';
408
+ copyIcon.classList.remove('text-neutral-500');
409
+ copyIcon.classList.add('text-amber-500');
410
+ copyIcon.classList.add('animate-copy-success');
411
+ copyIcon.style.opacity = '1';
412
+ copyIcon.style.transform = 'scale(1)';
413
+ copyBtn.classList.add('copy-active');
414
+ }, 150);
415
+ setTimeout(() => {
416
+ copyIcon.style.opacity = '0';
417
+ copyIcon.style.transform = 'scale(0.8)';
418
+ setTimeout(() => {
419
+ copyIcon.innerText = originalIcon;
420
+ copyIcon.classList.remove('text-amber-500', 'animate-copy-success');
421
+ copyIcon.classList.add('text-neutral-500');
422
+ copyIcon.style.opacity = '1';
423
+ copyIcon.style.transform = 'scale(1)';
424
+ copyBtn.classList.remove('copy-active');
425
+ }, 150);
426
+ }, 2000);
427
+ });
428
+ });
429
+ downloadBtn.addEventListener('click', () => {
430
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(jsonOutput.innerText);
431
+ const downloadAnchorNode = document.createElement('a');
432
+ downloadAnchorNode.setAttribute("href", dataStr);
433
+ downloadAnchorNode.setAttribute("download", "stylesync_output.json");
434
+ document.body.appendChild(downloadAnchorNode);
435
+ downloadAnchorNode.click();
436
+ downloadAnchorNode.remove();
437
+ });
438
+ startBtn.addEventListener('click', async () => {
439
+ if (!selectedFile) {
440
+ alert("Please select a file first.");
441
+ return;
442
+ }
443
+ startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3"><svg class="animate-spin h-4 w-4 lg:h-5 lg:w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span class="text-white text-base lg:text-lg font-bold tracking-wide">Synthesizing...</span></div>';
444
+ startBtn.disabled = true;
445
+ startBtn.classList.remove('animate-pulse-slow', 'animate-glow-pulse');
446
+ setTimeout(() => {
447
+ isCatalogGenerated = true;
448
+ startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3 relative z-10"><span class="text-white text-base lg:text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span><span class="material-symbols-outlined text-white text-lg lg:text-xl group-hover:translate-x-1 transition-transform">arrow_forward</span></div>';
449
+ startBtn.disabled = false;
450
+ startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
451
+ }, 1500);
452
+ });
453
+ </script>
454
+
455
+ </body></html>
install_gh.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import shutil
2
+ import subprocess
3
+ import sys
4
+
5
+ def main():
6
+ # Check Status
7
+ gh_path = shutil.which('gh')
8
+
9
+ if not gh_path:
10
+ # Install
11
+ print("GitHub CLI not found. Installing via winget...")
12
+ try:
13
+ subprocess.run(['winget', 'install', '--id', 'GitHub.cli', '-e'], check=True)
14
+ except subprocess.CalledProcessError as e:
15
+ print(f"Error installing GitHub CLI: {e}")
16
+ return
17
+ except FileNotFoundError:
18
+ print("Error: 'winget' command not found. Please ensure App Installer is installed.")
19
+ return
20
+
21
+ # Post-Install Instructions (Runs if installed or if installation succeeded)
22
+ print("\n" + "="*40)
23
+ try:
24
+ # Attempt to use ANSI codes for bold, may not work in all Windows terminals but works in VS Code / modern Windows Terminal
25
+ print("✅ \033[1mGitHub CLI is ready!\033[0m")
26
+ except:
27
+ print("✅ GitHub CLI is ready!")
28
+ print("="*40)
29
+ print("⚠️ IMPORTANT: You must now restart your terminal to reload your PATH.")
30
+ print("👉 After restarting, run this command to log in: gh auth login")
31
+
32
+ if __name__ == "__main__":
33
+ main()
landingpage.html ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>StyleSync AI | Autonomous E-Commerce Catalog Intelligence</title>
7
+ <!-- BEGIN: External Resources -->
8
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
9
+ <link href="https://fonts.googleapis.com" rel="preconnect"/>
10
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
11
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
12
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
13
+ <!-- END: External Resources -->
14
+ <!-- BEGIN: Tailwind Configuration -->
15
+ <script id="tailwind-config">
16
+ tailwind.config = {
17
+ darkMode: "class",
18
+ theme: {
19
+ extend: {
20
+ colors: {
21
+ "primary": "#fbbf24", // Amber-400
22
+ "primary-dark": "#d97706", // Amber-600
23
+ "charcoal": "#0a0a0a",
24
+ "glass-white": "rgba(255, 255, 255, 0.08)",
25
+ "glass-amber": "rgba(251, 191, 36, 0.05)",
26
+ },
27
+ fontFamily: {
28
+ "sans": ["Plus Jakarta Sans", "sans-serif"],
29
+ },
30
+ animation: {
31
+ 'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
32
+ 'shine': 'shine 2s infinite',
33
+ 'float': 'float 6s ease-in-out infinite',
34
+ },
35
+ keyframes: {
36
+ shine: {
37
+ '0%': { transform: 'translateX(-100%)' },
38
+ '100%': { transform: 'translateX(100%)' }
39
+ },
40
+ float: {
41
+ '0%, 100%': { transform: 'translateY(0)' },
42
+ '50%': { transform: 'translateY(-20px)' }
43
+ }
44
+ }
45
+ },
46
+ },
47
+ }
48
+ </script>
49
+ <!-- END: Tailwind Configuration -->
50
+ <!-- BEGIN: Custom Styles -->
51
+ <style data-purpose="layout-and-glassmorphism">
52
+ body {
53
+ background-color: #0a0a0a;
54
+ background-image:
55
+ radial-gradient(circle at 20% 30%, rgba(217, 119, 6, 0.08) 0%, transparent 40%),
56
+ radial-gradient(circle at 80% 70%, rgba(251, 191, 36, 0.05) 0%, transparent 40%);
57
+ }
58
+
59
+ .glass-panel {
60
+ background: rgba(255, 255, 255, 0.03);
61
+ backdrop-filter: blur(20px);
62
+ -webkit-backdrop-filter: blur(20px);
63
+ border: 1px solid rgba(255, 255, 255, 0.08);
64
+ }
65
+
66
+ .glass-card-hover:hover {
67
+ background: rgba(251, 191, 36, 0.05);
68
+ border-color: rgba(251, 191, 36, 0.3);
69
+ transform: translateY(-4px);
70
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
71
+ }
72
+
73
+ .text-glow {
74
+ text-shadow: 0 0 20px rgba(251, 191, 36, 0.3);
75
+ }
76
+
77
+ .custom-scrollbar::-webkit-scrollbar {
78
+ width: 6px;
79
+ }
80
+ .custom-scrollbar::-webkit-scrollbar-track {
81
+ background: transparent;
82
+ }
83
+ .custom-scrollbar::-webkit-scrollbar-thumb {
84
+ background: rgba(255, 255, 255, 0.1);
85
+ border-radius: 10px;
86
+ }
87
+
88
+ /* Scroll Reveal Animation Styles */
89
+ .reveal-card {
90
+ opacity: 0;
91
+ transform: translateY(30px);
92
+ transition: all 0.8s cubic-bezier(0.22, 1, 0.36, 1);
93
+ will-change: transform, opacity;
94
+ }
95
+
96
+ .reveal-card.is-visible {
97
+ opacity: 1;
98
+ transform: translateY(0);
99
+ }
100
+ </style>
101
+ <!-- END: Custom Styles -->
102
+ </head>
103
+ <body class="text-slate-200 antialiased font-sans">
104
+ <!-- BEGIN: NavHeader -->
105
+ <header class="fixed top-0 w-full z-50 px-6 py-4 flex items-center justify-between glass-panel border-t-0 border-x-0">
106
+ <div class="flex items-center gap-3">
107
+ <div class="flex items-center justify-center size-10 rounded-lg bg-gradient-to-br from-primary to-primary-dark text-charcoal">
108
+ <span class="material-symbols-outlined font-bold">all_inclusive</span>
109
+ </div>
110
+ <div>
111
+ <h1 class="text-white font-extrabold text-lg leading-none">StyleSync AI</h1>
112
+ <p class="text-[10px] text-primary uppercase tracking-[0.2em] font-semibold">Enterprise Edition</p>
113
+ </div>
114
+ </div>
115
+ <nav class="hidden md:flex items-center gap-8">
116
+ <a class="text-sm font-medium hover:text-primary transition-colors" href="#features">Features</a>
117
+ <a class="text-sm font-medium hover:text-primary transition-colors" href="#architecture">Architecture</a>
118
+ <a class="text-sm font-medium hover:text-primary transition-colors" href="#tech-stack">Tech Stack</a>
119
+ <a class="text-sm font-medium hover:text-primary transition-colors" href="#legal">Legal</a>
120
+ </nav>
121
+ <div class="flex items-center gap-4">
122
+ <button class="px-5 py-2.5 rounded-full text-sm font-bold bg-white/5 hover:bg-white/10 border border-white/10 transition-all">
123
+ Sign In
124
+ </button>
125
+ </div>
126
+ </header>
127
+ <!-- END: NavHeader -->
128
+ <main class="pt-24">
129
+ <!-- BEGIN: Hero Section -->
130
+ <section class="relative min-h-[90vh] flex items-center px-6 lg:px-24 overflow-hidden" id="hero">
131
+ <div class="grid lg:grid-cols-2 gap-12 items-center w-full">
132
+ <div class="z-10">
133
+ <span class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-widest mb-6">
134
+ <span class="relative flex h-2 w-2">
135
+ <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
136
+ <span class="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
137
+ </span>
138
+ Now in Private Beta
139
+ </span>
140
+ <h2 class="text-5xl lg:text-7xl font-extrabold text-white leading-[1.1] mb-6 tracking-tight">
141
+ Autonomous <br/>
142
+ <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary to-primary-dark">E-Commerce</span> <br/>
143
+ Intelligence
144
+ </h2>
145
+ <p class="text-lg text-slate-400 mb-10 max-w-xl leading-relaxed">
146
+ A multi-agent AI pipeline that converts raw product imagery into enterprise-grade, SEO-optimized e-commerce catalogs in seconds.
147
+ </p>
148
+ <div class="flex flex-wrap gap-4">
149
+ <a class="flex items-center gap-3 px-8 py-4 bg-primary hover:bg-primary-dark text-charcoal rounded-xl font-bold transition-all transform hover:scale-105 shadow-[0_0_30px_rgba(251,191,36,0.2)]" href="/dashboard">
150
+ <span class="material-symbols-outlined">rocket_launch</span>
151
+ Enter Dashboard
152
+ </a>
153
+ <button class="flex items-center gap-3 px-8 py-4 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl font-bold transition-all">
154
+ Watch Demo
155
+ </button>
156
+ </div>
157
+ </div>
158
+ <!-- Visual: Floating Glassmorphic Product Conversion -->
159
+ <div class="relative flex justify-center items-center">
160
+ <div class="absolute w-[120%] h-[120%] bg-primary/5 rounded-full blur-[100px] -z-10"></div>
161
+ <div class="relative animate-float">
162
+ <!-- Glass Mockup for Conversion -->
163
+ <div class="w-72 h-96 glass-panel rounded-[2rem] p-6 shadow-2xl relative">
164
+ <div class="w-full h-48 rounded-2xl bg-white/5 border border-white/10 overflow-hidden mb-4 flex items-center justify-center">
165
+ <span class="material-symbols-outlined text-6xl text-white/20">image</span>
166
+ </div>
167
+ <div class="space-y-3">
168
+ <div class="h-4 w-3/4 bg-white/10 rounded"></div>
169
+ <div class="h-4 w-1/2 bg-white/10 rounded"></div>
170
+ <div class="pt-4 grid grid-cols-3 gap-2">
171
+ <div class="h-8 rounded bg-primary/20 border border-primary/30"></div>
172
+ <div class="h-8 rounded bg-white/5 border border-white/10"></div>
173
+ <div class="h-8 rounded bg-white/5 border border-white/10"></div>
174
+ </div>
175
+ </div>
176
+ <!-- JSON Output Popover -->
177
+ <div class="absolute -right-12 -bottom-10 w-64 glass-panel rounded-2xl p-4 border-primary/30 shadow-2xl scale-90 lg:scale-100">
178
+ <div class="flex items-center gap-2 mb-2">
179
+ <div class="size-2 rounded-full bg-green-500"></div>
180
+ <span class="text-[10px] font-mono text-slate-400">output.json</span>
181
+ </div>
182
+ <pre class="text-[10px] font-mono text-primary/80"><code>{
183
+ "sku": "MF-9902",
184
+ "name": "AeroShell X",
185
+ "tags": ["SEO", "Hiker"]
186
+ }</code></pre>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </section>
193
+ <!-- END: Hero Section -->
194
+ <!-- BEGIN: Core Value Prop -->
195
+ <section class="py-24 px-6 lg:px-24" id="features">
196
+ <div class="max-w-4xl mx-auto text-center mb-16">
197
+ <h3 class="text-3xl lg:text-5xl font-bold text-white mb-6">Zero Manual Bottlenecks</h3>
198
+ <p class="text-slate-400 text-lg">Shift from human-in-the-loop to autonomous excellence. Our pipeline ensures your inventory is ready for global distribution instantly.</p>
199
+ </div>
200
+ <div class="grid md:grid-cols-3 gap-8">
201
+ <div class="p-8 rounded-3xl glass-panel glass-card-hover text-center">
202
+ <div class="size-14 mx-auto mb-6 rounded-2xl bg-primary/10 flex items-center justify-center">
203
+ <span class="material-symbols-outlined text-primary text-3xl">bolt</span>
204
+ </div>
205
+ <h4 class="text-xl font-bold text-white mb-3">Instant Delivery</h4>
206
+ <p class="text-slate-400 text-sm leading-relaxed">Go from product photo to live Shopify entry in under 15 seconds with zero latency agents.</p>
207
+ </div>
208
+ <div class="p-8 rounded-3xl glass-panel glass-card-hover text-center border-primary/20 bg-glass-amber">
209
+ <div class="size-14 mx-auto mb-6 rounded-2xl bg-primary flex items-center justify-center">
210
+ <span class="material-symbols-outlined text-charcoal text-3xl">verified</span>
211
+ </div>
212
+ <h4 class="text-xl font-bold text-white mb-3">0% Human Error</h4>
213
+ <p class="text-slate-400 text-sm leading-relaxed">Automated cross-referencing against technical specs ensures 100% attribute accuracy.</p>
214
+ </div>
215
+ <div class="p-8 rounded-3xl glass-panel glass-card-hover text-center">
216
+ <div class="size-14 mx-auto mb-6 rounded-2xl bg-primary/10 flex items-center justify-center">
217
+ <span class="material-symbols-outlined text-primary text-3xl">trending_up</span>
218
+ </div>
219
+ <h4 class="text-xl font-bold text-white mb-3">SEO Native</h4>
220
+ <p class="text-slate-400 text-sm leading-relaxed">Every description is engineered for search algorithms using real-time market sentiment data.</p>
221
+ </div>
222
+ </div>
223
+ </section>
224
+ <!-- END: Core Value Prop -->
225
+ <!-- BEGIN: Multi-Agent Pipeline -->
226
+ <section class="py-24 px-6 lg:px-24 bg-white/2" id="architecture">
227
+ <div class="flex flex-col lg:flex-row gap-16 items-start">
228
+ <div class="lg:w-1/3 sticky top-32">
229
+ <h3 class="text-4xl font-bold text-white mb-6">Multi-Agent <br/>Pipeline</h3>
230
+ <p class="text-slate-400 leading-relaxed mb-8">A sophisticated orchestration of specialized AI agents working in harmony to deliver high-fidelity catalog data.</p>
231
+ <div class="p-6 rounded-2xl border border-white/5 bg-white/2">
232
+ <div class="flex items-center gap-4 text-sm text-slate-300">
233
+ <span class="material-symbols-outlined text-green-500">memory</span>
234
+ Orchestration Layer: FastAPI + LangGraph
235
+ </div>
236
+ </div>
237
+ </div>
238
+ <div class="lg:w-2/3 grid gap-6">
239
+ <!-- Card 1 -->
240
+ <div class="reveal-card group p-8 rounded-3xl glass-panel flex gap-6 items-start hover:border-primary/40 transition-all">
241
+ <div class="size-16 rounded-2xl bg-indigo-500/20 flex-shrink-0 flex items-center justify-center">
242
+ <span class="material-symbols-outlined text-indigo-400 text-3xl">visibility</span>
243
+ </div>
244
+ <div>
245
+ <div class="flex items-center justify-between mb-2">
246
+ <h4 class="text-xl font-bold text-white">Visual Analyst</h4>
247
+ <span class="text-[10px] font-mono px-2 py-1 rounded bg-indigo-500/10 text-indigo-400 border border-indigo-500/20">GEMINI 2.5 FLASH</span>
248
+ </div>
249
+ <p class="text-slate-400 text-sm leading-relaxed">Performs deep pixel analysis to extract material textures, silhouettes, and hardware details directly from raw imagery.</p>
250
+ </div>
251
+ </div>
252
+ <!-- Card 2 -->
253
+ <div class="reveal-card group p-8 rounded-3xl glass-panel flex gap-6 items-start hover:border-primary/40 transition-all">
254
+ <div class="size-16 rounded-2xl bg-cyan-500/20 flex-shrink-0 flex items-center justify-center">
255
+ <span class="material-symbols-outlined text-cyan-400 text-3xl">database</span>
256
+ </div>
257
+ <div>
258
+ <div class="flex items-center justify-between mb-2">
259
+ <h4 class="text-xl font-bold text-white">Semantic Memory</h4>
260
+ <span class="text-[10px] font-mono px-2 py-1 rounded bg-cyan-500/10 text-cyan-400 border border-cyan-500/20">PINECONE RAG</span>
261
+ </div>
262
+ <p class="text-slate-400 text-sm leading-relaxed">Queries vector databases for historical product context, brand voice guidelines, and competitive SEO keywords.</p>
263
+ </div>
264
+ </div>
265
+ <!-- Card 3 -->
266
+ <div class="reveal-card group p-8 rounded-3xl glass-panel flex gap-6 items-start hover:border-primary/40 transition-all">
267
+ <div class="size-16 rounded-2xl bg-primary/20 flex-shrink-0 flex items-center justify-center">
268
+ <span class="material-symbols-outlined text-primary text-3xl">edit_note</span>
269
+ </div>
270
+ <div>
271
+ <div class="flex items-center justify-between mb-2">
272
+ <h4 class="text-xl font-bold text-white">Writer Agent</h4>
273
+ <span class="text-[10px] font-mono px-2 py-1 rounded bg-primary/10 text-primary border border-primary/20">LLAMA 3.3 70B (GROQ)</span>
274
+ </div>
275
+ <p class="text-slate-400 text-sm leading-relaxed">Synthesizes visual and semantic data into high-converting product descriptions, bullet points, and metadata.</p>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </section>
281
+ <!-- END: Multi-Agent Pipeline -->
282
+ <!-- BEGIN: Tech Stack Table -->
283
+ <section class="py-24 px-6 lg:px-24" id="tech-stack">
284
+ <div class="glass-panel rounded-3xl overflow-hidden border-white/5 shadow-2xl">
285
+ <div class="px-8 py-6 border-b border-white/5 bg-white/2 flex items-center justify-between">
286
+ <h3 class="text-xl font-bold text-white">Technical Infrastructure</h3>
287
+ <span class="text-xs text-slate-500 font-mono tracking-widest">SYSTEM_MANIFEST.V2</span>
288
+ </div>
289
+ <div class="overflow-x-auto">
290
+ <table class="w-full text-left">
291
+ <thead>
292
+ <tr class="bg-white/2 text-[10px] uppercase tracking-widest text-slate-500 border-b border-white/5">
293
+ <th class="px-8 py-4 font-bold">Component</th>
294
+ <th class="px-8 py-4 font-bold">Technology</th>
295
+ <th class="px-8 py-4 font-bold">Purpose</th>
296
+ <th class="px-8 py-4 font-bold">Status</th>
297
+ </tr>
298
+ </thead>
299
+ <tbody class="text-sm">
300
+ <tr class="border-b border-white/5 hover:bg-white/2 transition-colors">
301
+ <td class="px-8 py-6 font-bold text-white">Core Backend</td>
302
+ <td class="px-8 py-6 text-slate-400">Python 3.10+ / FastAPI</td>
303
+ <td class="px-8 py-6 text-slate-400">Asynchronous high-throughput processing</td>
304
+ <td class="px-8 py-6"><span class="px-2 py-1 rounded-full bg-green-500/10 text-green-500 text-[10px] font-bold">STABLE</span></td>
305
+ </tr>
306
+ <tr class="border-b border-white/5 hover:bg-white/2 transition-colors">
307
+ <td class="px-8 py-6 font-bold text-white">Agent Framework</td>
308
+ <td class="px-8 py-6 text-slate-400">Google GenAI / Groq SDK</td>
309
+ <td class="px-8 py-6 text-slate-400">Multi-LLM orchestration &amp; execution</td>
310
+ <td class="px-8 py-6"><span class="px-2 py-1 rounded-full bg-green-500/10 text-green-500 text-[10px] font-bold">STABLE</span></td>
311
+ </tr>
312
+ <tr class="border-b border-white/5 hover:bg-white/2 transition-colors">
313
+ <td class="px-8 py-6 font-bold text-white">Knowledge Base</td>
314
+ <td class="px-8 py-6 text-slate-400">Pinecone Vector DB</td>
315
+ <td class="px-8 py-6 text-slate-400">Retrieval Augmented Generation (RAG)</td>
316
+ <td class="px-8 py-6"><span class="px-2 py-1 rounded-full bg-green-500/10 text-green-500 text-[10px] font-bold">STABLE</span></td>
317
+ </tr>
318
+ <tr class="hover:bg-white/2 transition-colors">
319
+ <td class="px-8 py-6 font-bold text-white">Deployment</td>
320
+ <td class="px-8 py-6 text-slate-400">Docker / Kubernetes</td>
321
+ <td class="px-8 py-6 text-slate-400">Containerized auto-scaling architecture</td>
322
+ <td class="px-8 py-6"><span class="px-2 py-1 rounded-full bg-green-500/10 text-green-500 text-[10px] font-bold">STABLE</span></td>
323
+ </tr>
324
+ </tbody>
325
+ </table>
326
+ </div>
327
+ </div>
328
+ </section>
329
+ <!-- END: Tech Stack Table -->
330
+ <!-- BEGIN: Final CTA -->
331
+ <section class="py-24 px-6 text-center">
332
+ <div class="max-w-3xl mx-auto glass-panel p-12 lg:p-20 rounded-[3rem] border-primary/20 relative overflow-hidden">
333
+ <div class="absolute -top-24 -right-24 size-64 bg-primary/10 rounded-full blur-3xl"></div>
334
+ <h3 class="text-4xl lg:text-6xl font-extrabold text-white mb-8 tracking-tight">Scale Your Catalog To <br/>Infinite.</h3>
335
+ <p class="text-slate-400 text-lg mb-10 leading-relaxed">Join enterprise retail leaders who are automating their supply chain intelligence with StyleSync AI.</p>
336
+ <div class="flex flex-col sm:flex-row items-center justify-center gap-4">
337
+ <input class="w-full sm:w-auto min-w-[300px] bg-charcoal border border-white/10 rounded-xl px-6 py-4 focus:ring-2 focus:ring-primary/50 outline-none transition-all text-white" placeholder="Enter corporate email" type="email"/>
338
+ <button class="w-full sm:w-auto px-8 py-4 bg-primary hover:bg-primary-dark text-charcoal rounded-xl font-bold transition-all transform hover:scale-105">
339
+ Request Access
340
+ </button>
341
+ </div>
342
+ </div>
343
+ </section>
344
+ <!-- END: Final CTA -->
345
+ </main>
346
+ <!-- BEGIN: Footer -->
347
+ <footer class="mt-12 border-t border-white/5 bg-charcoal py-12 px-6 lg:px-24" id="legal">
348
+ <div class="flex flex-col md:flex-row justify-between items-start gap-12 mb-12">
349
+ <div class="max-w-xs">
350
+ <div class="flex items-center gap-3 mb-6">
351
+ <div class="flex items-center justify-center size-8 rounded-lg bg-primary text-charcoal">
352
+ <span class="material-symbols-outlined text-sm font-bold">all_inclusive</span>
353
+ </div>
354
+ <span class="text-white font-bold tracking-tight">StyleSync AI</span>
355
+ </div>
356
+ <p class="text-xs text-slate-500 leading-relaxed">
357
+ Designed and engineered for high-performance e-commerce logistics.
358
+ </p>
359
+ </div>
360
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-12">
361
+ <div>
362
+ <h5 class="text-white text-xs font-bold uppercase tracking-widest mb-6">Platform</h5>
363
+ <ul class="space-y-4 text-xs text-slate-400">
364
+ <li><a class="hover:text-primary" href="#">Agent Dashboard</a></li>
365
+ <li><a class="hover:text-primary" href="#">API Documentation</a></li>
366
+ <li><a class="hover:text-primary" href="#">Pricing Plans</a></li>
367
+ </ul>
368
+ </div>
369
+ <div>
370
+ <h5 class="text-white text-xs font-bold uppercase tracking-widest mb-6">Company</h5>
371
+ <ul class="space-y-4 text-xs text-slate-400">
372
+ <li><a class="hover:text-primary" href="#">About Us</a></li>
373
+ <li><a class="hover:text-primary" href="#">Careers</a></li>
374
+ <li><a class="hover:text-primary" href="#">Contact</a></li>
375
+ </ul>
376
+ </div>
377
+ <div>
378
+ <h5 class="text-white text-xs font-bold uppercase tracking-widest mb-6">Security</h5>
379
+ <ul class="space-y-4 text-xs text-slate-400">
380
+ <li><a class="hover:text-primary" href="#">Privacy Policy</a></li>
381
+ <li><a class="hover:text-primary" href="#">Terms of Service</a></li>
382
+ <li><a class="hover:text-primary" href="#">SLA</a></li>
383
+ </ul>
384
+ </div>
385
+ </div>
386
+ </div>
387
+ <div class="pt-8 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-4">
388
+ <p class="text-[10px] text-slate-600">
389
+ © 2026 StyleSync AI. All Rights Reserved.
390
+ </p>
391
+ <div class="flex gap-6">
392
+ <span class="material-symbols-outlined text-slate-600 hover:text-primary cursor-pointer">public</span>
393
+ <span class="material-symbols-outlined text-slate-600 hover:text-primary cursor-pointer">lock</span>
394
+ <span class="material-symbols-outlined text-slate-600 hover:text-primary cursor-pointer">hub</span>
395
+ </div>
396
+ </div>
397
+ </footer>
398
+ <!-- END: Footer -->
399
+ <!-- BEGIN: Animation Script -->
400
+ <script id="scroll-reveal-logic">
401
+ document.addEventListener('DOMContentLoaded', () => {
402
+ const observerOptions = {
403
+ root: null,
404
+ threshold: 0.15,
405
+ rootMargin: '0px 0px -50px 0px'
406
+ };
407
+
408
+ const observer = new IntersectionObserver((entries) => {
409
+ entries.forEach((entry, index) => {
410
+ if (entry.isIntersecting) {
411
+ // Stagger the animation of the cards
412
+ setTimeout(() => {
413
+ entry.target.classList.add('is-visible');
414
+ }, index * 100);
415
+ // Once visible, stop observing this specific element
416
+ observer.unobserve(entry.target);
417
+ }
418
+ });
419
+ }, observerOptions);
420
+
421
+ const revealCards = document.querySelectorAll('.reveal-card');
422
+ revealCards.forEach(card => observer.observe(card));
423
+ });
424
+ </script>
425
+ <!-- END: Animation Script -->
426
+ </body></html>
launcher.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import time
3
+ import webbrowser
4
+ import os
5
+ import urllib.request
6
+ import sys
7
+
8
+ def is_server_ready(url):
9
+ try:
10
+ with urllib.request.urlopen(url) as response:
11
+ return response.getcode() == 200
12
+ except Exception:
13
+ return False
14
+
15
+ def main():
16
+ print("🚀 Starting Engine...")
17
+
18
+ # Definition of the server command
19
+ # Using sys.executable to ensure we use the same python interpreter
20
+ server_command = [sys.executable, "-m", "uvicorn", "main:app", "--reload"]
21
+
22
+ # Start the server as a subprocess
23
+ process = subprocess.Popen(server_command, cwd=os.getcwd())
24
+
25
+ server_url = "http://localhost:8000"
26
+
27
+ # Poll for server availability
28
+ try:
29
+ while not is_server_ready(server_url):
30
+ time.sleep(1)
31
+
32
+ print("✅ Dashboard Launched")
33
+
34
+ # Open the dashboard in the default web browser
35
+ dashboard_path = os.path.abspath("dashboard.html")
36
+ webbrowser.open(f"file:///{dashboard_path}")
37
+
38
+ # Keep the script running to maintain the server process
39
+ process.wait()
40
+
41
+ except KeyboardInterrupt:
42
+ print("\n🛑 Shutting down...")
43
+ process.terminate()
44
+ process.wait()
45
+
46
+ if __name__ == "__main__":
47
+ main()
legacy/trend_spotter.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import google.generativeai as genai
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ class TrendSpotter:
9
+ def __init__(self):
10
+ self.api_key = os.getenv("GEMINI_API_KEY")
11
+ if self.api_key:
12
+ genai.configure(api_key=self.api_key)
13
+ self.model = genai.GenerativeModel('gemini-flash-latest')
14
+ self.has_key = True
15
+ else:
16
+ self.model = None
17
+ self.has_key = False
18
+
19
+ def get_trends(self, niche: str):
20
+ if not self.has_key:
21
+ print("⚠️ No API Key found, using mock data")
22
+ return ['Retro Cat Mom', 'Pixel Art Kitty', 'Cattitude']
23
+
24
+ try:
25
+ prompt = f"Generate 5 short, witty, and viral t-shirt text concepts for the niche: {niche}. Return strictly a JSON list of strings."
26
+ response = self.model.generate_content(prompt)
27
+
28
+ content = response.text
29
+ # Clean up markdown formatting if present
30
+ if "```json" in content:
31
+ content = content.replace("```json", "").replace("```", "")
32
+ elif "```" in content:
33
+ content = content.replace("```", "")
34
+
35
+ try:
36
+ trends = json.loads(content)
37
+ if isinstance(trends, list):
38
+ return trends
39
+ else:
40
+ return [content]
41
+ except json.JSONDecodeError:
42
+ return [content]
43
+
44
+ except Exception as e:
45
+ print(f"❌ Error calling Gemini: {e}")
46
+ return ['Retro Cat Mom', 'Pixel Art Kitty', 'Cattitude']