Spaces:
Sleeping
Sleeping
Richard Lai
commited on
Commit
·
4024eae
1
Parent(s):
9010fb4
first commit
Browse files- .dockerignore +78 -0
- .gitignore +118 -0
- Dockerfile +69 -0
- README.md +234 -9
- README_HF.md +31 -0
- app.py +39 -0
- backend/main.py +1173 -0
- frontend/.gitignore +24 -0
- frontend/.nvmrc +1 -0
- frontend/.pnpmfile.cjs +15 -0
- frontend/README.md +69 -0
- frontend/eslint.config.js +23 -0
- frontend/index.html +13 -0
- frontend/package.json +40 -0
- frontend/pnpm-lock.yaml +2780 -0
- frontend/postcss.config.js +6 -0
- frontend/public/logo.svg +68 -0
- frontend/public/vite.svg +1 -0
- frontend/src/App.css +66 -0
- frontend/src/App.tsx +594 -0
- frontend/src/assets/react.svg +1 -0
- frontend/src/components/EditorModal.tsx +68 -0
- frontend/src/components/FeedPicker.tsx +67 -0
- frontend/src/components/TweetCards.tsx +248 -0
- frontend/src/index.css +3 -0
- frontend/src/main.tsx +10 -0
- frontend/src/vite-env.d.ts +1 -0
- frontend/tailwind.config.js +11 -0
- frontend/tsconfig.app.json +27 -0
- frontend/tsconfig.json +7 -0
- frontend/tsconfig.node.json +25 -0
- frontend/vite.config.ts +7 -0
- pyproject.toml +20 -0
- requirements.txt +79 -0
- uv.lock +399 -0
.dockerignore
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Git
|
2 |
+
.git
|
3 |
+
.gitignore
|
4 |
+
|
5 |
+
# Documentation
|
6 |
+
README.md
|
7 |
+
*.md
|
8 |
+
|
9 |
+
# Environment files
|
10 |
+
.env
|
11 |
+
.env.local
|
12 |
+
.env.example
|
13 |
+
|
14 |
+
# Python
|
15 |
+
__pycache__/
|
16 |
+
*.py[cod]
|
17 |
+
*$py.class
|
18 |
+
*.so
|
19 |
+
.Python
|
20 |
+
env/
|
21 |
+
venv/
|
22 |
+
.venv/
|
23 |
+
.coverage
|
24 |
+
.pytest_cache/
|
25 |
+
|
26 |
+
# Node.js
|
27 |
+
node_modules/
|
28 |
+
npm-debug.log*
|
29 |
+
yarn-debug.log*
|
30 |
+
yarn-error.log*
|
31 |
+
.npm
|
32 |
+
.yarn/
|
33 |
+
.pnpm-store/
|
34 |
+
|
35 |
+
# Frontend build artifacts (will be copied from build stage)
|
36 |
+
frontend/dist/
|
37 |
+
frontend/.vite/
|
38 |
+
frontend/coverage/
|
39 |
+
|
40 |
+
# IDE and editor files
|
41 |
+
.vscode/
|
42 |
+
.idea/
|
43 |
+
*.swp
|
44 |
+
*.swo
|
45 |
+
*~
|
46 |
+
|
47 |
+
# OS generated files
|
48 |
+
.DS_Store
|
49 |
+
.DS_Store?
|
50 |
+
._*
|
51 |
+
.Spotlight-V100
|
52 |
+
.Trashes
|
53 |
+
ehthumbs.db
|
54 |
+
Thumbs.db
|
55 |
+
|
56 |
+
# Logs
|
57 |
+
logs/
|
58 |
+
*.log
|
59 |
+
|
60 |
+
# Temporary files
|
61 |
+
tmp/
|
62 |
+
temp/
|
63 |
+
|
64 |
+
# Old project files
|
65 |
+
AI-NewsLetter-old/
|
66 |
+
|
67 |
+
# Deployment files
|
68 |
+
.vercel/
|
69 |
+
.railway/
|
70 |
+
|
71 |
+
# Package manager lock files (except the ones we need)
|
72 |
+
package-lock.json
|
73 |
+
yarn.lock
|
74 |
+
|
75 |
+
# Test files
|
76 |
+
*.test.js
|
77 |
+
*.test.ts
|
78 |
+
*.test.tsx
|
.gitignore
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
2 |
+
|
3 |
+
# dependencies
|
4 |
+
/node_modules
|
5 |
+
/.pnp
|
6 |
+
.pnp.js
|
7 |
+
|
8 |
+
# testing
|
9 |
+
/coverage
|
10 |
+
|
11 |
+
# next.js
|
12 |
+
/.next/
|
13 |
+
/out/
|
14 |
+
|
15 |
+
# production
|
16 |
+
/build
|
17 |
+
|
18 |
+
# misc
|
19 |
+
.DS_Store
|
20 |
+
*.pem
|
21 |
+
|
22 |
+
# debug
|
23 |
+
npm-debug.log*
|
24 |
+
yarn-debug.log*
|
25 |
+
yarn-error.log*
|
26 |
+
|
27 |
+
# local env files
|
28 |
+
.env*.local
|
29 |
+
.env
|
30 |
+
|
31 |
+
# vercel
|
32 |
+
.vercel
|
33 |
+
|
34 |
+
# typescript
|
35 |
+
*.tsbuildinfo
|
36 |
+
next-env.d.ts
|
37 |
+
|
38 |
+
# Python
|
39 |
+
__pycache__/
|
40 |
+
*.py[cod]
|
41 |
+
*$py.class
|
42 |
+
*.pyo
|
43 |
+
*.pyd
|
44 |
+
*.pyc
|
45 |
+
|
46 |
+
# Distribution / packaging
|
47 |
+
.Python
|
48 |
+
build/
|
49 |
+
develop-eggs/
|
50 |
+
dist/
|
51 |
+
downloads/
|
52 |
+
eggs/
|
53 |
+
.eggs/
|
54 |
+
lib/
|
55 |
+
lib64/
|
56 |
+
parts/
|
57 |
+
sdist/
|
58 |
+
var/
|
59 |
+
wheels/
|
60 |
+
pip-wheel-metadata/
|
61 |
+
share/python-wheels/
|
62 |
+
*.egg-info/
|
63 |
+
.installed.cfg
|
64 |
+
*.egg
|
65 |
+
MANIFEST
|
66 |
+
|
67 |
+
# PyInstaller
|
68 |
+
# Usually these files are written by a python script from a template
|
69 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
70 |
+
*.manifest
|
71 |
+
*.spec
|
72 |
+
|
73 |
+
# Installer logs
|
74 |
+
pip-log.txt
|
75 |
+
pip-delete-this-directory.txt
|
76 |
+
|
77 |
+
# Unit test / coverage reports
|
78 |
+
htmlcov/
|
79 |
+
.tox/
|
80 |
+
.nox/
|
81 |
+
.coverage
|
82 |
+
.coverage.*
|
83 |
+
.cache
|
84 |
+
nosetests.xml
|
85 |
+
coverage.xml
|
86 |
+
*.cover
|
87 |
+
.hypothesis/
|
88 |
+
.pytest_cache/
|
89 |
+
|
90 |
+
# Jupyter Notebook
|
91 |
+
.ipynb_checkpoints
|
92 |
+
|
93 |
+
# pyenv
|
94 |
+
.python-version
|
95 |
+
|
96 |
+
# mypy
|
97 |
+
.mypy_cache/
|
98 |
+
.dmypy.json
|
99 |
+
dmypy.json
|
100 |
+
|
101 |
+
# Pyre type checker
|
102 |
+
.pyre/
|
103 |
+
|
104 |
+
# pytype
|
105 |
+
.pytype/
|
106 |
+
|
107 |
+
# Cython debug symbols
|
108 |
+
cython_debug/
|
109 |
+
|
110 |
+
# Virtual environment
|
111 |
+
.venv/*
|
112 |
+
|
113 |
+
# vscode
|
114 |
+
.vscode/*
|
115 |
+
|
116 |
+
# cursor
|
117 |
+
.cursor/*
|
118 |
+
|
Dockerfile
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Multi-stage build for AI Newsletter Generator
|
2 |
+
# Optimized for Hugging Face Spaces deployment
|
3 |
+
|
4 |
+
# Stage 1: Build frontend
|
5 |
+
FROM node:20-slim as frontend-builder
|
6 |
+
|
7 |
+
# Install pnpm
|
8 |
+
RUN npm install -g pnpm
|
9 |
+
|
10 |
+
# Set working directory
|
11 |
+
WORKDIR /app/frontend
|
12 |
+
|
13 |
+
# Copy frontend package files
|
14 |
+
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
15 |
+
|
16 |
+
# Install frontend dependencies
|
17 |
+
RUN pnpm install --frozen-lockfile
|
18 |
+
|
19 |
+
# Copy frontend source
|
20 |
+
COPY frontend/ .
|
21 |
+
|
22 |
+
# Build frontend for production
|
23 |
+
RUN pnpm build
|
24 |
+
|
25 |
+
# Stage 2: Python backend with built frontend
|
26 |
+
FROM python:3.12-slim
|
27 |
+
|
28 |
+
# Set environment variables
|
29 |
+
ENV PYTHONUNBUFFERED=1
|
30 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
31 |
+
ENV PORT=7860
|
32 |
+
|
33 |
+
# Install system dependencies
|
34 |
+
RUN apt-get update && apt-get install -y \
|
35 |
+
curl \
|
36 |
+
&& rm -rf /var/lib/apt/lists/*
|
37 |
+
|
38 |
+
# Install uv for fast Python package management
|
39 |
+
RUN pip install uv
|
40 |
+
|
41 |
+
# Set working directory
|
42 |
+
WORKDIR /app
|
43 |
+
|
44 |
+
# Copy Python configuration
|
45 |
+
COPY pyproject.toml uv.lock ./
|
46 |
+
|
47 |
+
# Install Python dependencies
|
48 |
+
RUN uv sync --no-dev
|
49 |
+
|
50 |
+
# Copy backend source
|
51 |
+
COPY backend/ ./backend/
|
52 |
+
|
53 |
+
# Copy built frontend from previous stage
|
54 |
+
COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
|
55 |
+
|
56 |
+
# Create non-root user for security
|
57 |
+
RUN useradd --create-home --shell /bin/bash app
|
58 |
+
RUN chown -R app:app /app
|
59 |
+
USER app
|
60 |
+
|
61 |
+
# Expose port (Hugging Face Spaces uses 7860)
|
62 |
+
EXPOSE 7860
|
63 |
+
|
64 |
+
# Health check
|
65 |
+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
66 |
+
CMD curl -f http://localhost:7860/api/health || exit 1
|
67 |
+
|
68 |
+
# Start command for Hugging Face Spaces
|
69 |
+
CMD ["uv", "run", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
@@ -1,11 +1,236 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
---
|
10 |
|
11 |
-
|
|
|
1 |
+
# AI Newsletter Generator
|
2 |
+
|
3 |
+
A full-stack AI-powered newsletter generator that creates engaging newsletters from RSS feeds with intelligent article summarization, tweet generation, and AI-assisted editing.
|
4 |
+
|
5 |
+
## ✨ Features
|
6 |
+
|
7 |
+
- 🤖 **AI-Enhanced Article Summaries**: LLM-generated engaging abstracts for better readability
|
8 |
+
- 📰 **RSS Feed Aggregation**: Curate content from multiple AI/tech news sources
|
9 |
+
- 🔍 **Smart Article Selection**: Interactive interface to choose articles for processing
|
10 |
+
- 📝 **Deep Article Summarization**: AI-powered detailed summaries of selected articles
|
11 |
+
- 🐦 **Social Media Content**: Generate Twitter/X posts with AI editing capabilities
|
12 |
+
- 📧 **Professional Newsletters**: Create polished HTML newsletters
|
13 |
+
- ✨ **Interactive AI Editing**: Real-time AI assistance for content refinement
|
14 |
+
- 🎨 **Modern UI**: Beautiful React interface with gradient backgrounds and smooth interactions
|
15 |
+
- ⚡ **Fast Performance**: Vite-powered frontend with hot reload
|
16 |
+
- 🔒 **Environment Security**: Secure API key management
|
17 |
+
|
18 |
+
## 🏗️ Architecture
|
19 |
+
|
20 |
+
### Project Structure
|
21 |
+
```
|
22 |
+
AI-NewsLetter/
|
23 |
+
├── backend/ # FastAPI backend
|
24 |
+
│ └── main.py # API endpoints + static file serving
|
25 |
+
├── frontend/ # React + Vite + Tailwind frontend
|
26 |
+
│ ├── src/
|
27 |
+
│ │ ├── components/ # React components
|
28 |
+
│ │ │ ├── FeedPicker.tsx
|
29 |
+
│ │ │ ├── TweetCards.tsx
|
30 |
+
│ │ │ └── EditorModal.tsx
|
31 |
+
│ │ ├── App.tsx # Main application
|
32 |
+
│ │ └── index.css # Tailwind styles
|
33 |
+
│ └── dist/ # Built frontend (served by backend)
|
34 |
+
├── pyproject.toml # Python dependencies (managed by uv)
|
35 |
+
├── .env # Environment variables
|
36 |
+
└── README.md
|
37 |
+
```
|
38 |
+
|
39 |
+
### Technology Stack
|
40 |
+
|
41 |
+
**Backend:**
|
42 |
+
- **FastAPI** - Modern Python web framework with automatic API docs
|
43 |
+
- **OpenAI API** - GPT-4o-mini for content generation and enhancement
|
44 |
+
- **httpx** - Async HTTP client for web scraping
|
45 |
+
- **feedparser** - RSS/Atom feed parsing
|
46 |
+
- **uvicorn** - High-performance ASGI server
|
47 |
+
|
48 |
+
**Frontend:**
|
49 |
+
- **React 19** - Latest React with modern hooks
|
50 |
+
- **TypeScript** - Type safety and better developer experience
|
51 |
+
- **Vite** - Lightning-fast build tool and dev server
|
52 |
+
- **Tailwind CSS v3** - Utility-first styling with custom components
|
53 |
+
- **pnpm** - Fast, disk-efficient package manager
|
54 |
+
|
55 |
+
**Development Tools:**
|
56 |
+
- **uv** - Ultra-fast Python package manager
|
57 |
+
- **ESLint** - Code linting and formatting
|
58 |
+
- **PostCSS** - CSS processing with Tailwind
|
59 |
+
|
60 |
+
## 🚀 Quick Start
|
61 |
+
|
62 |
+
### Prerequisites
|
63 |
+
|
64 |
+
- **Python 3.12+** with [uv](https://docs.astral.sh/uv/) installed
|
65 |
+
- **Node.js 18+**
|
66 |
+
- **pnpm** (recommended) or npm
|
67 |
+
- **OpenAI API Key** - Get one from [OpenAI Platform](https://platform.openai.com/account/api-keys)
|
68 |
+
|
69 |
+
### 1. Environment Setup
|
70 |
+
|
71 |
+
Create a `.env` file in the project root:
|
72 |
+
```bash
|
73 |
+
OPENAI_API_KEY=sk-your-actual-openai-api-key-here
|
74 |
+
```
|
75 |
+
|
76 |
+
### 2. Backend Setup
|
77 |
+
|
78 |
+
```bash
|
79 |
+
# Install Python dependencies
|
80 |
+
uv sync
|
81 |
+
|
82 |
+
# Start the FastAPI server (serves both API and frontend)
|
83 |
+
uv run uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
|
84 |
+
```
|
85 |
+
|
86 |
+
### 3. Frontend Setup
|
87 |
+
|
88 |
+
```bash
|
89 |
+
cd frontend
|
90 |
+
|
91 |
+
# Install dependencies
|
92 |
+
pnpm install
|
93 |
+
|
94 |
+
# Build for production
|
95 |
+
pnpm build
|
96 |
+
```
|
97 |
+
|
98 |
+
### 4. Access the Application
|
99 |
+
|
100 |
+
Open your browser to **http://127.0.0.1:8000**
|
101 |
+
|
102 |
+
The backend serves both the API endpoints and the built React frontend from a single port.
|
103 |
+
|
104 |
+
## 📖 User Guide
|
105 |
+
|
106 |
+
### Workflow
|
107 |
+
|
108 |
+
1. **Select Sources**: Choose from curated AI/tech RSS feeds
|
109 |
+
2. **Get Highlights**: Fetch articles and generate initial AI summary
|
110 |
+
3. **Select Articles**: Review articles with AI-enhanced abstracts
|
111 |
+
4. **Get Summaries**: Generate detailed summaries for selected articles (max 5)
|
112 |
+
5. **Generate Tweets**: Create social media content with AI editing
|
113 |
+
6. **Create Newsletter**: Build professional HTML newsletter
|
114 |
+
7. **Download**: Export your newsletter
|
115 |
+
|
116 |
+
### Key Features Explained
|
117 |
+
|
118 |
+
**AI-Enhanced Abstracts**: When you click "Get Highlights", the system not only fetches articles but uses GPT to create engaging 2-3 sentence summaries for each article, making them much more readable and compelling than raw RSS descriptions.
|
119 |
+
|
120 |
+
**Smart Article Selection**: The interface shows checkboxes for each article with enhanced summaries, publication dates, and sources. You can easily select which articles to dive deeper into.
|
121 |
+
|
122 |
+
**Detailed Summarization**: The "Get Summaries" feature scrapes full article content and creates comprehensive summaries using AI, perfect for busy readers who want key insights.
|
123 |
+
|
124 |
+
**Interactive AI Editing**: Both tweets and newsletter content can be edited with AI assistance through natural language commands.
|
125 |
+
|
126 |
+
## 🔧 Development
|
127 |
+
|
128 |
+
### Full-Stack Development (Recommended)
|
129 |
+
```bash
|
130 |
+
# Terminal 1: Start backend
|
131 |
+
uv run uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
|
132 |
+
|
133 |
+
# Terminal 2: Build frontend after changes
|
134 |
+
cd frontend && pnpm build
|
135 |
+
```
|
136 |
+
|
137 |
+
### Frontend-Only Development
|
138 |
+
For rapid UI development with hot reload:
|
139 |
+
```bash
|
140 |
+
# Terminal 1: Backend
|
141 |
+
uv run uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
|
142 |
+
|
143 |
+
# Terminal 2: Frontend dev server
|
144 |
+
cd frontend && pnpm dev --port 3002
|
145 |
+
```
|
146 |
+
|
147 |
+
Then open http://127.0.0.1:3002 for development or http://127.0.0.1:8000 for production.
|
148 |
+
|
149 |
+
### Available Scripts
|
150 |
+
|
151 |
+
**Backend:**
|
152 |
+
```bash
|
153 |
+
uv sync # Install dependencies
|
154 |
+
uv run uvicorn backend.main:app --reload # Start server
|
155 |
+
```
|
156 |
+
|
157 |
+
**Frontend:**
|
158 |
+
```bash
|
159 |
+
pnpm install # Install dependencies
|
160 |
+
pnpm build # Build for production
|
161 |
+
pnpm dev # Development server
|
162 |
+
pnpm type-check # TypeScript checking
|
163 |
+
pnpm clean # Clean build artifacts
|
164 |
+
```
|
165 |
+
|
166 |
+
## 🌐 API Reference
|
167 |
+
|
168 |
+
### Core Endpoints
|
169 |
+
|
170 |
+
- `GET /` - Serves React frontend application
|
171 |
+
- `GET /api/health` - API health check
|
172 |
+
- `GET /api/defaults` - Get default RSS feed sources
|
173 |
+
|
174 |
+
### Content Generation
|
175 |
+
|
176 |
+
- `POST /api/aggregate` - Fetch articles from RSS feeds with AI-enhanced summaries
|
177 |
+
- `POST /api/highlights` - Generate weekly highlights summary
|
178 |
+
- `POST /api/summaries_selected` - Create detailed summaries for selected articles
|
179 |
+
- `POST /api/tweets` - Generate social media posts from summaries
|
180 |
+
- `POST /api/newsletter` - Create HTML newsletter
|
181 |
+
- `POST /api/edit_tweet` - AI-powered tweet editing
|
182 |
+
|
183 |
+
### Example API Usage
|
184 |
+
|
185 |
+
```bash
|
186 |
+
# Get enhanced articles with AI summaries
|
187 |
+
curl -X POST "http://127.0.0.1:8000/api/aggregate" \
|
188 |
+
-H "Content-Type: application/json" \
|
189 |
+
-d '{"sources": ["https://huggingface.co/blog/feed.xml"]}'
|
190 |
+
|
191 |
+
# Generate detailed summaries
|
192 |
+
curl -X POST "http://127.0.0.1:8000/api/summaries_selected" \
|
193 |
+
-H "Content-Type: application/json" \
|
194 |
+
-d '{"articles": [...]}'
|
195 |
+
```
|
196 |
+
|
197 |
+
## 🐳 Deployment
|
198 |
+
|
199 |
+
### Hugging Face Spaces
|
200 |
+
|
201 |
+
This project includes a Dockerfile optimized for Hugging Face Spaces deployment:
|
202 |
+
|
203 |
+
1. Push your code to a Hugging Face repository
|
204 |
+
2. Set your `OPENAI_API_KEY` in the Space settings
|
205 |
+
3. The Dockerfile will handle the rest!
|
206 |
+
|
207 |
+
### Other Platforms
|
208 |
+
|
209 |
+
The application can be deployed on any platform that supports Docker containers:
|
210 |
+
- Railway
|
211 |
+
- Render
|
212 |
+
- DigitalOcean App Platform
|
213 |
+
- AWS ECS
|
214 |
+
- Google Cloud Run
|
215 |
+
|
216 |
+
## 🤝 Contributing
|
217 |
+
|
218 |
+
Contributions are welcome! This project uses:
|
219 |
+
- **Python**: Black formatting, type hints encouraged
|
220 |
+
- **TypeScript**: Strict mode, ESLint configuration
|
221 |
+
- **Git**: Conventional commit messages preferred
|
222 |
+
|
223 |
+
## 📄 License
|
224 |
+
|
225 |
+
This project is open source and available under the MIT License.
|
226 |
+
|
227 |
+
## 🙋♂️ Support
|
228 |
+
|
229 |
+
Having issues?
|
230 |
+
1. Check that your OpenAI API key is correctly set in `.env`
|
231 |
+
2. Ensure all dependencies are installed (`uv sync` and `pnpm install`)
|
232 |
+
3. Verify the frontend is built (`pnpm build`) before accessing the full-stack app
|
233 |
+
|
234 |
---
|
235 |
|
236 |
+
Built with ❤️ using modern web technologies and AI.
|
README_HF.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# AI Newsletter Generator - Hugging Face Space
|
2 |
+
|
3 |
+
This Space runs an AI-powered newsletter generator that creates engaging newsletters from RSS feeds.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
|
7 |
+
- 🤖 AI-enhanced article summaries using GPT-4o-mini
|
8 |
+
- 📰 RSS feed aggregation from top AI/tech sources
|
9 |
+
- 🐦 Social media content generation
|
10 |
+
- 📧 Professional HTML newsletter creation
|
11 |
+
- ✨ Interactive AI-powered editing
|
12 |
+
|
13 |
+
## Usage
|
14 |
+
|
15 |
+
1. **Select Sources**: Choose from curated RSS feeds
|
16 |
+
2. **Get Highlights**: Fetch articles with AI-enhanced summaries
|
17 |
+
3. **Select Articles**: Pick articles for detailed processing
|
18 |
+
4. **Generate Content**: Create summaries, tweets, and newsletters
|
19 |
+
5. **Download**: Export your newsletter
|
20 |
+
|
21 |
+
## Requirements
|
22 |
+
|
23 |
+
This Space requires an OpenAI API key to function. Set your `OPENAI_API_KEY` in the Space settings.
|
24 |
+
|
25 |
+
## Tech Stack
|
26 |
+
|
27 |
+
- **Backend**: FastAPI + OpenAI API
|
28 |
+
- **Frontend**: React + Vite + Tailwind CSS
|
29 |
+
- **Deployment**: Docker multi-stage build
|
30 |
+
|
31 |
+
Built with modern web technologies and AI for content creators and tech enthusiasts.
|
app.py
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
AI Newsletter Generator - Hugging Face Spaces Entry Point
|
4 |
+
|
5 |
+
This file serves as an alternative entry point for Hugging Face Spaces.
|
6 |
+
The main application is in backend/main.py and runs via uvicorn.
|
7 |
+
"""
|
8 |
+
|
9 |
+
import subprocess
|
10 |
+
import sys
|
11 |
+
import os
|
12 |
+
|
13 |
+
def main():
|
14 |
+
"""Start the FastAPI application for Hugging Face Spaces"""
|
15 |
+
|
16 |
+
# Set default port for Hugging Face Spaces
|
17 |
+
port = os.getenv("PORT", "7860")
|
18 |
+
|
19 |
+
# Command to start the FastAPI app
|
20 |
+
cmd = [
|
21 |
+
sys.executable, "-m", "uvicorn",
|
22 |
+
"backend.main:app",
|
23 |
+
"--host", "0.0.0.0",
|
24 |
+
"--port", port
|
25 |
+
]
|
26 |
+
|
27 |
+
print(f"Starting AI Newsletter Generator on port {port}...")
|
28 |
+
print(f"Command: {' '.join(cmd)}")
|
29 |
+
|
30 |
+
try:
|
31 |
+
subprocess.run(cmd, check=True)
|
32 |
+
except KeyboardInterrupt:
|
33 |
+
print("\nShutting down...")
|
34 |
+
except subprocess.CalledProcessError as e:
|
35 |
+
print(f"Error starting application: {e}")
|
36 |
+
sys.exit(1)
|
37 |
+
|
38 |
+
if __name__ == "__main__":
|
39 |
+
main()
|
backend/main.py
ADDED
@@ -0,0 +1,1173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import io
|
3 |
+
import json
|
4 |
+
from datetime import datetime, timedelta, timezone
|
5 |
+
import re
|
6 |
+
from html import unescape
|
7 |
+
import httpx
|
8 |
+
from typing import List, Optional, Dict, Any
|
9 |
+
|
10 |
+
import feedparser
|
11 |
+
from fastapi import FastAPI, HTTPException, Response, Request
|
12 |
+
from fastapi.middleware.cors import CORSMiddleware
|
13 |
+
from fastapi.staticfiles import StaticFiles
|
14 |
+
from fastapi.responses import FileResponse
|
15 |
+
from pydantic import BaseModel, Field, AnyHttpUrl
|
16 |
+
from dateutil import parser as dateparser
|
17 |
+
from openai import OpenAI
|
18 |
+
from dotenv import load_dotenv
|
19 |
+
|
20 |
+
# Load environment variables from .env file (if it exists)
|
21 |
+
try:
|
22 |
+
load_dotenv()
|
23 |
+
except Exception:
|
24 |
+
pass # Ignore if .env file doesn't exist (like in Railway)
|
25 |
+
|
26 |
+
|
27 |
+
# ASGI app for Vercel Python function: export `app`
|
28 |
+
app = FastAPI(title="AI Newsletter Generator API", version="1.0.0")
|
29 |
+
|
30 |
+
# CORS (same-origin on Vercel, but allow localhost for dev)
|
31 |
+
allowed_origins = [
|
32 |
+
os.getenv("ALLOWED_ORIGIN", "*"),
|
33 |
+
"http://localhost:3000",
|
34 |
+
"https://localhost:3000",
|
35 |
+
"http://localhost:3001",
|
36 |
+
"https://localhost:3001",
|
37 |
+
"http://localhost:3010",
|
38 |
+
"https://localhost:3010",
|
39 |
+
"http://localhost:3002",
|
40 |
+
"https://localhost:3002",
|
41 |
+
]
|
42 |
+
app.add_middleware(
|
43 |
+
CORSMiddleware,
|
44 |
+
allow_origins=allowed_origins,
|
45 |
+
allow_credentials=True,
|
46 |
+
allow_methods=["*"]
|
47 |
+
,
|
48 |
+
allow_headers=["*"]
|
49 |
+
)
|
50 |
+
|
51 |
+
|
52 |
+
# ----- Memory Store (ephemeral in serverless) -----
|
53 |
+
class ConversationTurn(BaseModel):
|
54 |
+
role: str
|
55 |
+
content: str
|
56 |
+
|
57 |
+
|
58 |
+
class SessionMemory(BaseModel):
|
59 |
+
session_id: str
|
60 |
+
history: List[ConversationTurn] = Field(default_factory=list)
|
61 |
+
last_newsletter_html: Optional[str] = None
|
62 |
+
last_summary: Optional[str] = None
|
63 |
+
last_tweets: Optional[List[str]] = None
|
64 |
+
|
65 |
+
|
66 |
+
memory_store: Dict[str, SessionMemory] = {}
|
67 |
+
|
68 |
+
|
69 |
+
def get_memory(session_id: str) -> SessionMemory:
|
70 |
+
if session_id not in memory_store:
|
71 |
+
memory_store[session_id] = SessionMemory(session_id=session_id)
|
72 |
+
return memory_store[session_id]
|
73 |
+
|
74 |
+
|
75 |
+
# ----- Default RSS Feeds (AI-focused) -----
|
76 |
+
DEFAULT_FEEDS: Dict[str, str] = {
|
77 |
+
# Working RSS feeds verified as of 2025
|
78 |
+
"Hugging Face Blog": "https://huggingface.co/blog/feed.xml",
|
79 |
+
"The Gradient": "https://thegradient.pub/rss/",
|
80 |
+
"MIT Technology Review AI": "https://www.technologyreview.com/tag/artificial-intelligence/feed/",
|
81 |
+
"VentureBeat AI": "https://venturebeat.com/ai/feed/",
|
82 |
+
"AI News": "https://artificialintelligence-news.com/feed/",
|
83 |
+
}
|
84 |
+
|
85 |
+
|
86 |
+
# ----- Models -----
|
87 |
+
class AggregateRequest(BaseModel):
|
88 |
+
sources: Optional[List[AnyHttpUrl]] = None
|
89 |
+
since_days: int = Field(default=7, ge=1, le=31)
|
90 |
+
|
91 |
+
|
92 |
+
class Article(BaseModel):
|
93 |
+
title: str
|
94 |
+
link: AnyHttpUrl
|
95 |
+
summary: Optional[str] = None
|
96 |
+
published: Optional[str] = None
|
97 |
+
source: Optional[str] = None
|
98 |
+
|
99 |
+
|
100 |
+
class AggregateResponse(BaseModel):
|
101 |
+
articles: List[Article]
|
102 |
+
|
103 |
+
|
104 |
+
class SummarizeRequest(BaseModel):
|
105 |
+
session_id: str
|
106 |
+
articles: List[Article]
|
107 |
+
instructions: Optional[str] = Field(
|
108 |
+
default=(
|
109 |
+
"Summarize the week's most important AI developments for a technical but busy audience. "
|
110 |
+
"Be concise, structured with headings and bullet points, and include source attributions."
|
111 |
+
)
|
112 |
+
)
|
113 |
+
prior_history: Optional[List[ConversationTurn]] = None
|
114 |
+
|
115 |
+
|
116 |
+
class SummarizeResponse(BaseModel):
|
117 |
+
summary_markdown: str
|
118 |
+
|
119 |
+
|
120 |
+
# ----- Per-Article Summaries (Highlights) -----
|
121 |
+
class HighlightItem(BaseModel):
|
122 |
+
title: str
|
123 |
+
link: AnyHttpUrl
|
124 |
+
source: Optional[str] = None
|
125 |
+
summary: str
|
126 |
+
|
127 |
+
|
128 |
+
class TweetsRequest(BaseModel):
|
129 |
+
session_id: str
|
130 |
+
summaries: List[HighlightItem] # Changed to use individual summaries
|
131 |
+
prior_history: Optional[List[ConversationTurn]] = None
|
132 |
+
|
133 |
+
|
134 |
+
class Tweet(BaseModel):
|
135 |
+
id: str
|
136 |
+
content: str
|
137 |
+
summary_title: str
|
138 |
+
summary_link: str
|
139 |
+
summary_source: str
|
140 |
+
|
141 |
+
|
142 |
+
class TweetsResponse(BaseModel):
|
143 |
+
tweets: List[Tweet]
|
144 |
+
|
145 |
+
|
146 |
+
class NewsletterRequest(BaseModel):
|
147 |
+
session_id: str
|
148 |
+
summary_markdown: str
|
149 |
+
articles: List[Article]
|
150 |
+
prior_history: Optional[List[ConversationTurn]] = None
|
151 |
+
|
152 |
+
|
153 |
+
class NewsletterResponse(BaseModel):
|
154 |
+
html: str
|
155 |
+
|
156 |
+
|
157 |
+
class EditRequest(BaseModel):
|
158 |
+
session_id: str
|
159 |
+
text: str
|
160 |
+
instruction: str
|
161 |
+
prior_history: Optional[List[ConversationTurn]] = None
|
162 |
+
|
163 |
+
|
164 |
+
class SummariesSelectedRequest(BaseModel):
|
165 |
+
articles: List[Article]
|
166 |
+
|
167 |
+
|
168 |
+
class EditResponse(BaseModel):
|
169 |
+
edited_text: str
|
170 |
+
history: List[ConversationTurn]
|
171 |
+
|
172 |
+
|
173 |
+
class TweetEditRequest(BaseModel):
|
174 |
+
session_id: str
|
175 |
+
tweet_id: str
|
176 |
+
current_tweet: str
|
177 |
+
original_summary: str
|
178 |
+
user_message: str
|
179 |
+
conversation_history: Optional[List[ConversationTurn]] = None
|
180 |
+
|
181 |
+
|
182 |
+
class TweetEditResponse(BaseModel):
|
183 |
+
new_tweet: str
|
184 |
+
ai_response: str
|
185 |
+
conversation_history: List[ConversationTurn]
|
186 |
+
|
187 |
+
|
188 |
+
# Initialize OpenAI client with error handling
|
189 |
+
try:
|
190 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
191 |
+
if not api_key:
|
192 |
+
print("WARNING: OPENAI_API_KEY not found in environment variables")
|
193 |
+
openai_client = None
|
194 |
+
else:
|
195 |
+
openai_client = OpenAI(api_key=api_key)
|
196 |
+
MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
|
197 |
+
except Exception as e:
|
198 |
+
print(f"ERROR initializing OpenAI client: {e}")
|
199 |
+
openai_client = None
|
200 |
+
|
201 |
+
|
202 |
+
def _parse_date(dt_str: Optional[str]) -> Optional[datetime]:
|
203 |
+
if not dt_str:
|
204 |
+
return None
|
205 |
+
try:
|
206 |
+
return dateparser.parse(dt_str)
|
207 |
+
except Exception:
|
208 |
+
return None
|
209 |
+
|
210 |
+
|
211 |
+
# Mount static files for serving React build
|
212 |
+
static_dir = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
|
213 |
+
if os.path.exists(static_dir):
|
214 |
+
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
215 |
+
|
216 |
+
@app.get("/api/health")
|
217 |
+
def api_health():
|
218 |
+
"""API health check endpoint"""
|
219 |
+
return {"status": "ok", "message": "AI Newsletter API is running"}
|
220 |
+
|
221 |
+
@app.get("/")
|
222 |
+
def serve_frontend():
|
223 |
+
"""Serve React frontend from dist folder"""
|
224 |
+
static_dir = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
|
225 |
+
index_file = os.path.join(static_dir, "index.html")
|
226 |
+
|
227 |
+
# If frontend build exists, serve it
|
228 |
+
if os.path.exists(index_file):
|
229 |
+
return FileResponse(index_file)
|
230 |
+
|
231 |
+
# Fallback to API health check if frontend not built
|
232 |
+
return {"status": "ok", "message": "AI Newsletter API is running", "note": "Frontend not built yet"}
|
233 |
+
|
234 |
+
|
235 |
+
|
236 |
+
|
237 |
+
@app.get("/api/defaults", response_model=Dict[str, str])
|
238 |
+
def get_defaults() -> Dict[str, str]:
|
239 |
+
"""Get default RSS feed sources"""
|
240 |
+
try:
|
241 |
+
return DEFAULT_FEEDS
|
242 |
+
except Exception as e:
|
243 |
+
print(f"Error in get_defaults: {e}")
|
244 |
+
raise HTTPException(status_code=500, detail=f"Server error: {str(e)}")
|
245 |
+
|
246 |
+
|
247 |
+
def _generate_engaging_summaries(articles: List[Article]) -> List[Article]:
|
248 |
+
"""Generate engaging, short summaries for articles using LLM"""
|
249 |
+
if not openai_client:
|
250 |
+
return articles # Return unchanged if no OpenAI client
|
251 |
+
|
252 |
+
enhanced_articles = []
|
253 |
+
|
254 |
+
for article in articles:
|
255 |
+
try:
|
256 |
+
# Create a prompt to generate an engaging summary
|
257 |
+
if article.summary:
|
258 |
+
# Improve existing summary
|
259 |
+
prompt = f"""
|
260 |
+
Create an engaging, concise summary (2-3 sentences, ~50-80 words) for this article:
|
261 |
+
|
262 |
+
Title: {article.title}
|
263 |
+
Source: {article.source}
|
264 |
+
Current Summary: {article.summary}
|
265 |
+
|
266 |
+
Make it more engaging and accessible while keeping the key information. Focus on why readers should care.
|
267 |
+
"""
|
268 |
+
else:
|
269 |
+
# Generate summary from title only
|
270 |
+
prompt = f"""
|
271 |
+
Create an engaging, concise summary (2-3 sentences, ~50-80 words) for this article based on its title:
|
272 |
+
|
273 |
+
Title: {article.title}
|
274 |
+
Source: {article.source}
|
275 |
+
|
276 |
+
Make it intriguing and accessible while staying true to what the title suggests. Focus on why readers should care about this topic.
|
277 |
+
"""
|
278 |
+
|
279 |
+
enhanced_summary = _chat([
|
280 |
+
{"role": "system", "content": "You are an expert content writer who creates engaging, accessible summaries for busy readers interested in AI and technology."},
|
281 |
+
{"role": "user", "content": prompt}
|
282 |
+
], temperature=0.7)
|
283 |
+
|
284 |
+
# Create new article with enhanced summary
|
285 |
+
enhanced_articles.append(Article(
|
286 |
+
title=article.title,
|
287 |
+
link=article.link,
|
288 |
+
summary=enhanced_summary.strip(),
|
289 |
+
published=article.published,
|
290 |
+
source=article.source
|
291 |
+
))
|
292 |
+
|
293 |
+
except Exception as e:
|
294 |
+
# If LLM fails, keep original article
|
295 |
+
print(f"Failed to enhance summary for {article.title}: {e}")
|
296 |
+
enhanced_articles.append(article)
|
297 |
+
|
298 |
+
return enhanced_articles
|
299 |
+
|
300 |
+
|
301 |
+
@app.post("/api/aggregate", response_model=AggregateResponse)
|
302 |
+
def aggregate(req: AggregateRequest) -> AggregateResponse:
|
303 |
+
# Only retrieve from explicitly selected sources. If none provided, return empty.
|
304 |
+
sources = req.sources or []
|
305 |
+
cutoff = datetime.now(timezone.utc) - timedelta(days=req.since_days)
|
306 |
+
|
307 |
+
collected: List[Article] = []
|
308 |
+
for src in sources:
|
309 |
+
feed = feedparser.parse(str(src))
|
310 |
+
source_title = getattr(feed.feed, "title", None) or "Unknown Source"
|
311 |
+
for entry in feed.entries[:50]:
|
312 |
+
published = None
|
313 |
+
published_dt: Optional[datetime] = None
|
314 |
+
if hasattr(entry, "published"):
|
315 |
+
published = entry.published
|
316 |
+
published_dt = _parse_date(published)
|
317 |
+
elif hasattr(entry, "updated"):
|
318 |
+
published = entry.updated
|
319 |
+
published_dt = _parse_date(published)
|
320 |
+
|
321 |
+
# Filter by recency if date available
|
322 |
+
if published_dt and published_dt.tzinfo is None:
|
323 |
+
published_dt = published_dt.replace(tzinfo=timezone.utc)
|
324 |
+
if published_dt and published_dt < cutoff:
|
325 |
+
continue
|
326 |
+
|
327 |
+
summary = getattr(entry, "summary", None)
|
328 |
+
if summary:
|
329 |
+
# Decode HTML entities and clean up
|
330 |
+
summary = unescape(summary.strip())
|
331 |
+
link = getattr(entry, "link", None)
|
332 |
+
title = getattr(entry, "title", None)
|
333 |
+
if not (title and link):
|
334 |
+
continue
|
335 |
+
|
336 |
+
collected.append(
|
337 |
+
Article(
|
338 |
+
title=title,
|
339 |
+
link=link,
|
340 |
+
summary=summary,
|
341 |
+
published=published,
|
342 |
+
source=source_title,
|
343 |
+
)
|
344 |
+
)
|
345 |
+
|
346 |
+
# Generate engaging summaries for articles that don't have them or improve existing ones
|
347 |
+
if openai_client and collected:
|
348 |
+
enhanced_articles = _generate_engaging_summaries(collected[:10]) # Limit to first 10 for performance
|
349 |
+
# Update the collected articles with enhanced summaries
|
350 |
+
for i, enhanced in enumerate(enhanced_articles):
|
351 |
+
if i < len(collected):
|
352 |
+
collected[i] = enhanced
|
353 |
+
|
354 |
+
return AggregateResponse(articles=collected)
|
355 |
+
|
356 |
+
|
357 |
+
# ----- Simple Web Scraper (no external heavy deps) -----
|
358 |
+
class ScrapeRequest(BaseModel):
|
359 |
+
url: AnyHttpUrl
|
360 |
+
|
361 |
+
|
362 |
+
class ScrapeResponse(BaseModel):
|
363 |
+
content_text: str
|
364 |
+
|
365 |
+
|
366 |
+
def _extract_main_text(html: str) -> str:
|
367 |
+
# Try to focus on <article> or <main> blocks first
|
368 |
+
try:
|
369 |
+
article_match = re.search(r"<article[\s\S]*?</article>", html, flags=re.IGNORECASE)
|
370 |
+
main_match = re.search(r"<main[\s\S]*?</main>", html, flags=re.IGNORECASE)
|
371 |
+
snippet = None
|
372 |
+
if article_match:
|
373 |
+
snippet = article_match.group(0)
|
374 |
+
elif main_match:
|
375 |
+
snippet = main_match.group(0)
|
376 |
+
else:
|
377 |
+
snippet = html
|
378 |
+
# Remove scripts/styles
|
379 |
+
snippet = re.sub(r"<script[\s\S]*?</script>", " ", snippet, flags=re.IGNORECASE)
|
380 |
+
snippet = re.sub(r"<style[\s\S]*?</style>", " ", snippet, flags=re.IGNORECASE)
|
381 |
+
# Strip tags
|
382 |
+
text = re.sub(r"<[^>]+>", " ", snippet)
|
383 |
+
text = unescape(text)
|
384 |
+
# Collapse whitespace
|
385 |
+
text = re.sub(r"\s+", " ", text).strip()
|
386 |
+
return text
|
387 |
+
except Exception:
|
388 |
+
return ""
|
389 |
+
|
390 |
+
|
391 |
+
@app.post("/api/scrape", response_model=ScrapeResponse)
|
392 |
+
def scrape(req: ScrapeRequest) -> ScrapeResponse:
|
393 |
+
try:
|
394 |
+
with httpx.Client(timeout=10.0, follow_redirects=True, headers={"User-Agent": "Mozilla/5.0 (compatible; AI-Newsletter/1.0)"}) as client:
|
395 |
+
resp = client.get(str(req.url))
|
396 |
+
resp.raise_for_status()
|
397 |
+
text = _extract_main_text(resp.text)
|
398 |
+
# Limit to a safe size for LLM context
|
399 |
+
if len(text) > 8000:
|
400 |
+
text = text[:8000]
|
401 |
+
return ScrapeResponse(content_text=text)
|
402 |
+
except Exception:
|
403 |
+
return ScrapeResponse(content_text="")
|
404 |
+
|
405 |
+
|
406 |
+
class HighlightsRequest(BaseModel):
|
407 |
+
sources: List[AnyHttpUrl]
|
408 |
+
since_days: int = Field(default=7, ge=1, le=31)
|
409 |
+
max_articles: int = Field(default=8, ge=1, le=20)
|
410 |
+
|
411 |
+
|
412 |
+
class HighlightsResponse(BaseModel):
|
413 |
+
items: List[HighlightItem]
|
414 |
+
|
415 |
+
|
416 |
+
@app.post("/api/summaries", response_model=HighlightsResponse)
|
417 |
+
def summaries(req: HighlightsRequest) -> HighlightsResponse:
|
418 |
+
# Enforce selection: if no sources, return empty list
|
419 |
+
if not req.sources:
|
420 |
+
return HighlightsResponse(items=[])
|
421 |
+
|
422 |
+
articles_resp = aggregate(AggregateRequest(sources=req.sources, since_days=req.since_days))
|
423 |
+
items: List[HighlightItem] = []
|
424 |
+
|
425 |
+
# Use configurable limit (default 8, max 20)
|
426 |
+
limited_articles = articles_resp.articles[:req.max_articles]
|
427 |
+
|
428 |
+
for a in limited_articles:
|
429 |
+
# Scrape content with shorter timeout
|
430 |
+
content_text = ""
|
431 |
+
try:
|
432 |
+
with httpx.Client(timeout=5.0, follow_redirects=True, headers={"User-Agent": "Mozilla/5.0 (compatible; AI-Newsletter/1.0)"}) as client:
|
433 |
+
resp = client.get(str(a.link))
|
434 |
+
resp.raise_for_status()
|
435 |
+
raw_html = resp.text
|
436 |
+
content_text = _extract_main_text(raw_html)
|
437 |
+
except Exception:
|
438 |
+
# Fallback to RSS summary if scraping fails
|
439 |
+
content_text = a.summary or ""
|
440 |
+
|
441 |
+
if len(content_text) > 4000: # Reduced from 8000 for faster processing
|
442 |
+
content_text = content_text[:4000]
|
443 |
+
|
444 |
+
# If no content available, use title and RSS summary
|
445 |
+
if not content_text.strip():
|
446 |
+
content_text = f"Title: {a.title}\nRSS Summary: {a.summary or 'No summary available'}"
|
447 |
+
|
448 |
+
# Summarize the single article's content
|
449 |
+
system = (
|
450 |
+
"You are an expert AI news editor. Summarize the article content for a busy technical audience. "
|
451 |
+
"Be concise (3-5 bullet points), capture key findings. If content is limited, work with what's available."
|
452 |
+
)
|
453 |
+
user = (
|
454 |
+
f"Title: {a.title}\nSource: {a.source or ''}\nURL: {a.link}\n\n"
|
455 |
+
f"Content:\n{content_text}\n\n"
|
456 |
+
"Write a clear, concise summary."
|
457 |
+
)
|
458 |
+
|
459 |
+
try:
|
460 |
+
summary_text = _chat([
|
461 |
+
{"role": "system", "content": system},
|
462 |
+
{"role": "user", "content": user},
|
463 |
+
], temperature=0.3)
|
464 |
+
except Exception:
|
465 |
+
# Fallback if OpenAI fails
|
466 |
+
summary_text = a.summary or f"Unable to generate summary for: {a.title}"
|
467 |
+
|
468 |
+
items.append(HighlightItem(title=a.title, link=a.link, source=a.source, summary=summary_text.strip()))
|
469 |
+
|
470 |
+
return HighlightsResponse(items=items)
|
471 |
+
|
472 |
+
|
473 |
+
@app.post("/api/summaries_selected", response_model=HighlightsResponse)
|
474 |
+
def summaries_selected(req: SummariesSelectedRequest) -> HighlightsResponse:
|
475 |
+
"""Process summaries for only selected articles (no RSS aggregation needed)"""
|
476 |
+
items: List[HighlightItem] = []
|
477 |
+
|
478 |
+
for a in req.articles[:5]: # Limit to 5 articles max for performance
|
479 |
+
# Scrape content with shorter timeout
|
480 |
+
content_text = ""
|
481 |
+
try:
|
482 |
+
with httpx.Client(timeout=5.0, follow_redirects=True, headers={"User-Agent": "Mozilla/5.0 (compatible; AI-Newsletter/1.0)"}) as client:
|
483 |
+
resp = client.get(str(a.link))
|
484 |
+
resp.raise_for_status()
|
485 |
+
raw_html = resp.text
|
486 |
+
content_text = _extract_main_text(raw_html)
|
487 |
+
except Exception:
|
488 |
+
# Fallback to RSS summary if scraping fails
|
489 |
+
content_text = a.summary or ""
|
490 |
+
|
491 |
+
if len(content_text) > 4000: # Reduced for faster processing
|
492 |
+
content_text = content_text[:4000]
|
493 |
+
|
494 |
+
# If no content available, use title and RSS summary
|
495 |
+
if not content_text.strip():
|
496 |
+
content_text = f"Title: {a.title}\nRSS Summary: {a.summary or 'No summary available'}"
|
497 |
+
|
498 |
+
# Summarize the single article's content
|
499 |
+
system = (
|
500 |
+
"You are an expert AI news editor. Summarize the article content for a busy technical audience. "
|
501 |
+
"Be concise (3-5 bullet points), capture key findings. If content is limited, work with what's available."
|
502 |
+
)
|
503 |
+
user = (
|
504 |
+
f"Title: {a.title}\nSource: {a.source or ''}\nURL: {a.link}\n\n"
|
505 |
+
f"Content:\n{content_text}\n\n"
|
506 |
+
"Write a clear, concise summary."
|
507 |
+
)
|
508 |
+
|
509 |
+
try:
|
510 |
+
summary_text = _chat([
|
511 |
+
{"role": "system", "content": system},
|
512 |
+
{"role": "user", "content": user},
|
513 |
+
], temperature=0.3)
|
514 |
+
except Exception:
|
515 |
+
# Fallback if OpenAI fails
|
516 |
+
summary_text = a.summary or f"Unable to generate summary for: {a.title}"
|
517 |
+
|
518 |
+
items.append(HighlightItem(title=a.title, link=a.link, source=a.source, summary=summary_text.strip()))
|
519 |
+
|
520 |
+
return HighlightsResponse(items=items)
|
521 |
+
|
522 |
+
|
523 |
+
def _chat(messages: List[Dict[str, str]], temperature: float = 0.4) -> str:
|
524 |
+
if not openai_client:
|
525 |
+
raise Exception("OpenAI client not initialized - API key missing")
|
526 |
+
|
527 |
+
completion = openai_client.chat.completions.create(
|
528 |
+
model=MODEL,
|
529 |
+
messages=messages,
|
530 |
+
temperature=temperature,
|
531 |
+
)
|
532 |
+
return completion.choices[0].message.content or ""
|
533 |
+
|
534 |
+
|
535 |
+
@app.post("/api/highlights", response_model=SummarizeResponse)
|
536 |
+
def highlights_endpoint(req: SummarizeRequest) -> SummarizeResponse:
|
537 |
+
if not os.getenv("OPENAI_API_KEY"):
|
538 |
+
raise HTTPException(status_code=500, detail="OPENAI_API_KEY not set")
|
539 |
+
|
540 |
+
memory = get_memory(req.session_id)
|
541 |
+
if req.prior_history:
|
542 |
+
memory.history.extend(req.prior_history[-8:])
|
543 |
+
# Build context from articles
|
544 |
+
articles_text = "\n".join(
|
545 |
+
[
|
546 |
+
f"- {a.title} ({a.source}) — {a.link}\n{a.summary or ''}"
|
547 |
+
for a in req.articles[:20]
|
548 |
+
]
|
549 |
+
)
|
550 |
+
|
551 |
+
# Anchor summary to the current week to avoid stale dates from the model
|
552 |
+
now_local = datetime.now()
|
553 |
+
week_start = now_local - timedelta(days=now_local.weekday()) # Monday
|
554 |
+
week_of = week_start.strftime("%b %d, %Y")
|
555 |
+
|
556 |
+
system = (
|
557 |
+
"You are an expert AI news editor. Create a crisp weekly summary for a technical audience. "
|
558 |
+
"Use clear section headings, bullet points, and callouts. Include hyperlinks when relevant. "
|
559 |
+
f"Always label the summary with a top heading 'Week of {week_of}'."
|
560 |
+
)
|
561 |
+
user = (
|
562 |
+
f"Write a weekly highlights summary based on these items:\n\n{articles_text}\n\n"
|
563 |
+
f"Instructions: {req.instructions}"
|
564 |
+
)
|
565 |
+
|
566 |
+
messages: List[Dict[str, str]] = (
|
567 |
+
[
|
568 |
+
{"role": "system", "content": system},
|
569 |
+
]
|
570 |
+
+ [{"role": t.role, "content": t.content} for t in memory.history[-6:]]
|
571 |
+
+ [
|
572 |
+
{"role": "user", "content": user},
|
573 |
+
]
|
574 |
+
)
|
575 |
+
|
576 |
+
content = _chat(messages, temperature=0.3)
|
577 |
+
# Ensure the summary includes the correct 'Week of' label without duplication
|
578 |
+
content_clean = content.strip()
|
579 |
+
if not content_clean.lower().startswith(("week of", "# week of", "## week of")):
|
580 |
+
content = f"## Week of {week_of}\n\n" + content_clean
|
581 |
+
else:
|
582 |
+
content = content_clean
|
583 |
+
memory.last_summary = content
|
584 |
+
memory.history.append(ConversationTurn(role="user", content=user))
|
585 |
+
memory.history.append(ConversationTurn(role="assistant", content=content))
|
586 |
+
return SummarizeResponse(summary_markdown=content)
|
587 |
+
|
588 |
+
|
589 |
+
@app.post("/api/tweets", response_model=TweetsResponse)
|
590 |
+
def generate_tweets(req: TweetsRequest) -> TweetsResponse:
|
591 |
+
memory = get_memory(req.session_id)
|
592 |
+
if req.prior_history:
|
593 |
+
memory.history.extend(req.prior_history[-8:])
|
594 |
+
|
595 |
+
tweets: List[Tweet] = []
|
596 |
+
|
597 |
+
for i, summary in enumerate(req.summaries):
|
598 |
+
system = (
|
599 |
+
"You write engaging, factual, and concise Twitter posts (X). "
|
600 |
+
"Create ONE tweet about this specific AI news article."
|
601 |
+
)
|
602 |
+
user = (
|
603 |
+
f"Create a single engaging tweet about this AI news article:\n\n"
|
604 |
+
f"Title: {summary.title}\n"
|
605 |
+
f"Source: {summary.source}\n"
|
606 |
+
f"Summary: {summary.summary}\n\n"
|
607 |
+
"Include 1-2 relevant emojis and 1-2 hashtags. Keep under 280 characters. "
|
608 |
+
"Return only the tweet text, no JSON formatting."
|
609 |
+
)
|
610 |
+
|
611 |
+
messages = (
|
612 |
+
[{"role": "system", "content": system}]
|
613 |
+
+ [{"role": t.role, "content": t.content} for t in memory.history[-4:]]
|
614 |
+
+ [{"role": "user", "content": user}]
|
615 |
+
)
|
616 |
+
|
617 |
+
try:
|
618 |
+
tweet_content = _chat(messages, temperature=0.7)
|
619 |
+
# Clean up the response
|
620 |
+
tweet_content = tweet_content.strip().strip('"').strip("'")
|
621 |
+
|
622 |
+
tweet = Tweet(
|
623 |
+
id=f"tweet_{i}_{summary.title[:20].replace(' ', '_')}",
|
624 |
+
content=tweet_content,
|
625 |
+
summary_title=summary.title,
|
626 |
+
summary_link=str(summary.link),
|
627 |
+
summary_source=summary.source or "Unknown"
|
628 |
+
)
|
629 |
+
tweets.append(tweet)
|
630 |
+
|
631 |
+
except Exception:
|
632 |
+
# Fallback tweet if AI generation fails
|
633 |
+
fallback_content = f"🤖 {summary.title[:200]}... #AI #Tech"
|
634 |
+
tweet = Tweet(
|
635 |
+
id=f"tweet_{i}_{summary.title[:20].replace(' ', '_')}",
|
636 |
+
content=fallback_content,
|
637 |
+
summary_title=summary.title,
|
638 |
+
summary_link=str(summary.link),
|
639 |
+
summary_source=summary.source or "Unknown"
|
640 |
+
)
|
641 |
+
tweets.append(tweet)
|
642 |
+
|
643 |
+
# Store conversation context
|
644 |
+
turn_user = ConversationTurn(role="user", content=f"Generated {len(tweets)} tweets from summaries")
|
645 |
+
turn_assistant = ConversationTurn(role="assistant", content="Tweets generated successfully")
|
646 |
+
memory.history.append(turn_user)
|
647 |
+
memory.history.append(turn_assistant)
|
648 |
+
|
649 |
+
memory.last_tweets = [t.content for t in tweets] # Store for backward compatibility
|
650 |
+
return TweetsResponse(tweets=tweets)
|
651 |
+
|
652 |
+
|
653 |
+
def _build_newsletter_html(summary_md: str, articles: List[Article]) -> str:
|
654 |
+
# Select featured article (first article with good content)
|
655 |
+
featured_article = None
|
656 |
+
remaining_articles = []
|
657 |
+
|
658 |
+
for article in articles[:8]: # Use first 8 articles
|
659 |
+
if not featured_article and article.summary and len(article.summary) > 100:
|
660 |
+
featured_article = article
|
661 |
+
else:
|
662 |
+
remaining_articles.append(article)
|
663 |
+
|
664 |
+
# If no good featured article found, use the first one
|
665 |
+
if not featured_article and articles:
|
666 |
+
featured_article = articles[0]
|
667 |
+
remaining_articles = articles[1:8]
|
668 |
+
|
669 |
+
# Build news grid items (max 6 items, 2x3 grid)
|
670 |
+
news_items = ""
|
671 |
+
for i, article in enumerate(remaining_articles[:6]):
|
672 |
+
news_items += f"""
|
673 |
+
<div class="news-item">
|
674 |
+
<h4>{article.title}</h4>
|
675 |
+
<p>{(article.summary or 'Click to read more about this story.')[:150]}{'...' if len(article.summary or '') > 150 else ''}</p>
|
676 |
+
<a href="{article.link}" class="read-more">Read more →</a>
|
677 |
+
</div>
|
678 |
+
"""
|
679 |
+
|
680 |
+
now = datetime.now().strftime("%B %d, %Y")
|
681 |
+
|
682 |
+
# Format featured article
|
683 |
+
featured_title = featured_article.title if featured_article else "AI Weekly Highlights"
|
684 |
+
featured_summary = (featured_article.summary or "This week brings exciting developments in AI and technology.")[:200] + "..." if featured_article and len(featured_article.summary or "") > 200 else (featured_article.summary if featured_article else "This week brings exciting developments in AI and technology.")
|
685 |
+
featured_link = featured_article.link if featured_article else "#"
|
686 |
+
|
687 |
+
return f"""<!DOCTYPE html>
|
688 |
+
<html lang="en">
|
689 |
+
<head>
|
690 |
+
<meta charset="UTF-8">
|
691 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
692 |
+
<title>AI Weekly - Newsletter</title>
|
693 |
+
<style>
|
694 |
+
* {{
|
695 |
+
margin: 0;
|
696 |
+
padding: 0;
|
697 |
+
box-sizing: border-box;
|
698 |
+
}}
|
699 |
+
|
700 |
+
body {{
|
701 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
702 |
+
line-height: 1.6;
|
703 |
+
background-color: #f4f4f4;
|
704 |
+
color: #333;
|
705 |
+
}}
|
706 |
+
|
707 |
+
.container {{
|
708 |
+
max-width: 600px;
|
709 |
+
margin: 20px auto;
|
710 |
+
background-color: white;
|
711 |
+
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
712 |
+
}}
|
713 |
+
|
714 |
+
.header {{
|
715 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
716 |
+
color: white;
|
717 |
+
padding: 30px 20px;
|
718 |
+
text-align: center;
|
719 |
+
}}
|
720 |
+
|
721 |
+
.logo {{
|
722 |
+
font-size: 28px;
|
723 |
+
font-weight: bold;
|
724 |
+
margin-bottom: 10px;
|
725 |
+
}}
|
726 |
+
|
727 |
+
.tagline {{
|
728 |
+
font-size: 14px;
|
729 |
+
opacity: 0.9;
|
730 |
+
}}
|
731 |
+
|
732 |
+
.content {{
|
733 |
+
padding: 30px 20px;
|
734 |
+
}}
|
735 |
+
|
736 |
+
.section {{
|
737 |
+
margin-bottom: 30px;
|
738 |
+
border-bottom: 1px solid #eee;
|
739 |
+
padding-bottom: 30px;
|
740 |
+
}}
|
741 |
+
|
742 |
+
.section:last-child {{
|
743 |
+
border-bottom: none;
|
744 |
+
margin-bottom: 0;
|
745 |
+
padding-bottom: 0;
|
746 |
+
}}
|
747 |
+
|
748 |
+
.section h2 {{
|
749 |
+
color: #667eea;
|
750 |
+
font-size: 22px;
|
751 |
+
margin-bottom: 15px;
|
752 |
+
border-left: 4px solid #667eea;
|
753 |
+
padding-left: 15px;
|
754 |
+
}}
|
755 |
+
|
756 |
+
.featured-article {{
|
757 |
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
758 |
+
color: white;
|
759 |
+
padding: 25px;
|
760 |
+
border-radius: 10px;
|
761 |
+
margin-bottom: 20px;
|
762 |
+
}}
|
763 |
+
|
764 |
+
.featured-article h3 {{
|
765 |
+
font-size: 20px;
|
766 |
+
margin-bottom: 10px;
|
767 |
+
}}
|
768 |
+
|
769 |
+
.featured-article p {{
|
770 |
+
margin-bottom: 15px;
|
771 |
+
opacity: 0.95;
|
772 |
+
}}
|
773 |
+
|
774 |
+
.btn {{
|
775 |
+
display: inline-block;
|
776 |
+
background-color: white;
|
777 |
+
color: #f5576c;
|
778 |
+
padding: 12px 25px;
|
779 |
+
text-decoration: none;
|
780 |
+
border-radius: 25px;
|
781 |
+
font-weight: bold;
|
782 |
+
transition: transform 0.3s ease;
|
783 |
+
}}
|
784 |
+
|
785 |
+
.btn:hover {{
|
786 |
+
transform: translateY(-2px);
|
787 |
+
}}
|
788 |
+
|
789 |
+
.news-grid {{
|
790 |
+
display: grid;
|
791 |
+
grid-template-columns: 1fr 1fr;
|
792 |
+
gap: 20px;
|
793 |
+
margin-top: 20px;
|
794 |
+
}}
|
795 |
+
|
796 |
+
.news-item {{
|
797 |
+
border: 1px solid #eee;
|
798 |
+
border-radius: 8px;
|
799 |
+
padding: 20px;
|
800 |
+
transition: box-shadow 0.3s ease;
|
801 |
+
}}
|
802 |
+
|
803 |
+
.news-item:hover {{
|
804 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
805 |
+
}}
|
806 |
+
|
807 |
+
.news-item h4 {{
|
808 |
+
color: #333;
|
809 |
+
margin-bottom: 10px;
|
810 |
+
font-size: 16px;
|
811 |
+
}}
|
812 |
+
|
813 |
+
.news-item p {{
|
814 |
+
font-size: 14px;
|
815 |
+
color: #666;
|
816 |
+
margin-bottom: 10px;
|
817 |
+
}}
|
818 |
+
|
819 |
+
.read-more {{
|
820 |
+
color: #667eea;
|
821 |
+
text-decoration: none;
|
822 |
+
font-size: 14px;
|
823 |
+
font-weight: bold;
|
824 |
+
}}
|
825 |
+
|
826 |
+
.cta-section {{
|
827 |
+
background-color: #f8f9fa;
|
828 |
+
padding: 30px;
|
829 |
+
text-align: center;
|
830 |
+
border-radius: 10px;
|
831 |
+
}}
|
832 |
+
|
833 |
+
.cta-section h3 {{
|
834 |
+
color: #333;
|
835 |
+
margin-bottom: 15px;
|
836 |
+
}}
|
837 |
+
|
838 |
+
.cta-btn {{
|
839 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
840 |
+
color: white;
|
841 |
+
padding: 15px 30px;
|
842 |
+
text-decoration: none;
|
843 |
+
border-radius: 30px;
|
844 |
+
font-weight: bold;
|
845 |
+
display: inline-block;
|
846 |
+
margin-top: 10px;
|
847 |
+
}}
|
848 |
+
|
849 |
+
.social-links {{
|
850 |
+
text-align: center;
|
851 |
+
margin-top: 30px;
|
852 |
+
}}
|
853 |
+
|
854 |
+
.social-links a {{
|
855 |
+
display: inline-block;
|
856 |
+
margin: 0 10px;
|
857 |
+
width: 40px;
|
858 |
+
height: 40px;
|
859 |
+
background-color: #667eea;
|
860 |
+
color: white;
|
861 |
+
text-decoration: none;
|
862 |
+
border-radius: 50%;
|
863 |
+
line-height: 40px;
|
864 |
+
transition: background-color 0.3s ease;
|
865 |
+
}}
|
866 |
+
|
867 |
+
.social-links a:hover {{
|
868 |
+
background-color: #764ba2;
|
869 |
+
}}
|
870 |
+
|
871 |
+
.footer {{
|
872 |
+
background-color: #333;
|
873 |
+
color: white;
|
874 |
+
padding: 30px 20px;
|
875 |
+
text-align: center;
|
876 |
+
}}
|
877 |
+
|
878 |
+
.footer p {{
|
879 |
+
margin-bottom: 10px;
|
880 |
+
font-size: 14px;
|
881 |
+
}}
|
882 |
+
|
883 |
+
.footer a {{
|
884 |
+
color: #667eea;
|
885 |
+
text-decoration: none;
|
886 |
+
}}
|
887 |
+
|
888 |
+
@media (max-width: 600px) {{
|
889 |
+
.news-grid {{
|
890 |
+
grid-template-columns: 1fr;
|
891 |
+
}}
|
892 |
+
|
893 |
+
.container {{
|
894 |
+
margin: 10px;
|
895 |
+
}}
|
896 |
+
|
897 |
+
.content {{
|
898 |
+
padding: 20px 15px;
|
899 |
+
}}
|
900 |
+
}}
|
901 |
+
</style>
|
902 |
+
</head>
|
903 |
+
<body>
|
904 |
+
<div class="container">
|
905 |
+
<!-- Header -->
|
906 |
+
<div class="header">
|
907 |
+
<div class="logo" style="display:flex; align-items:center; justify-content:center; gap:10px;">
|
908 |
+
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibG9nb0dyYWQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjNjY3ZWVhIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjNzY0YmEyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2YwOTNmYiIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgCiAgPCEtLSBNYWluIGNvbnRhaW5lciBjaXJjbGUgLS0+CiAgPGNpcmNsZSBjeD0iMzIiIGN5PSIzMiIgcj0iMjgiIGZpbGw9InVybCgjbG9nb0dyYWQpIi8+CiAgCiAgPCEtLSBOZXVyYWwgbmV0d29yayBub2RlcyAtLT4KICA8ZyBmaWxsPSIjZmZmZmZmIiBvcGFjaXR5PSIwLjkiPgogICAgPCEtLSBJbnB1dCBsYXllciAtLT4KICAgIDxjaXJjbGUgY3g9IjE4IiBjeT0iMjQiIHI9IjIuNSIvPgogICAgPGNpcmNsZSBjeD0iMTgiIGN5PSIzMiIgcj0iMi41Ii8+CiAgICA8Y2lyY2xlIGN4PSIxOCIgY3k9IjQwIiByPSIyLjUiLz4KICAgIAogICAgPCEtLSBIaWRkZW4gbGF5ZXIgLS0+CiAgICA8Y2lyY2xlIGN4PSIzMiIgY3k9IjIwIiByPSIyIi8+CiAgICA8Y2lyY2xlIGN4PSIzMiIgY3k9IjI4IiByPSIyIi8+CiAgICA8Y2lyY2xlIGN4PSIzMiIgY3k9IjM2IiByPSIyIi8+CiAgICA8Y2lyY2xlIGN4PSIzMiIgY3k9IjQ0IiByPSIyIi8+CiAgICAKICAgIDwhLS0gT3V0cHV0IGxheWVyIC0tPgogICAgPGNpcmNsZSBjeD0iNDYiIGN5PSIyOCIgcj0iMi41Ii8+CiAgICA8Y2lyY2xlIGN4PSI0NiIgY3k9IjM2IiByPSIyLjUiLz4KICA8L2c+CiAgCiAgPCEtLSBOZXVyYWwgbmV0d29yayBjb25uZWN0aW9ucyAtLT4KICA8ZyBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMSIgb3BhY2l0eT0iMC42IiBmaWxsPSJub25lIj4KICAgIDwhLS0gSW5wdXQgdG8gaGlkZGVuIGNvbm5lY3Rpb25zIC0tPgogICAgPGxpbmUgeDE9IjIwLjUiIHkxPSIyNCIgeDI9IjMwIiB5Mj0iMjAiLz4KICAgIDxsaW5lIHgxPSIyMC41IiB5MT0iMjQiIHgyPSIzMCIgeTI9IjI4Ii8+CiAgICA8bGluZSB4MT0iMjAuNSIgeTE9IjMyIiB4Mj0iMzAiIHkyPSIyOCIvPgogICAgPGxpbmUgeDE9IjIwLjUiIHkxPSIzMiIgeDI9IjMwIiB5Mj0iMzYiLz4KICAgIDxsaW5lIHgxPSIyMC41IiB5MT0iNDAiIHgyPSIzMCIgeTI9IjM2Ii8+CiAgICA8bGluZSB4MT0iMjAuNSIgeTE9IjQwIiB4Mj0iMzAiIHkyPSI0NCIvPgogICAgCiAgICA8IS0tIEhpZGRlbiB0byBvdXRwdXQgY29ubmVjdGlvbnMgLS0+CiAgICA8bGluZSB4MT0iMzQiIHkxPSIyMCIgeDI9IjQzLjUiIHkyPSIyOCIvPgogICAgPGxpbmUgeDE9IjM0IiB5MT0iMjgiIHgyPSI0My41IiB5Mj0iMjgiLz4KICAgIDxsaW5lIHgxPSIzNCIgeTE9IjM2IiB4Mj0iNDMuNSIgeTI9IjM2Ii8+CiAgICA8bGluZSB4MT0iMzQiIHkxPSI0NCIgeDI9IjQzLjUiIHkyPSIzNiIvPgogIDwvZz4KICAKICA8IS0tIEFJIHN5bWJvbCBpbiBjZW50ZXIgLS0+CiAgPGcgZmlsbD0iI2ZmZmZmZiIgb3BhY2l0eT0iMC44Ij4KICAgIDx0ZXh0IHg9IjMyIiB5PSIxNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IkFyaWFsLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjgiIGZvbnQtd2VpZ2h0PSJib2xkIj5BSTwvdGV4dD4KICA8L2c+CiAgCiAgPCEtLSBOZXdzbGV0dGVyL2RvY3VtZW50IGljb24gLS0+CiAgPGcgZmlsbD0iI2ZmZmZmZiIgb3BhY2l0eT0iMC43Ij4KICAgIDxyZWN0IHg9IjI2IiB5PSI0OCIgd2lkdGg9IjEyIiBoZWlnaHQ9IjgiIHJ4PSIxIi8+CiAgICA8bGluZSB4MT0iMjgiIHkxPSI1MCIgeDI9IjM2IiB5Mj0iNTAiIHN0cm9rZT0iIzY2N2VlYSIgc3Ryb2tlLXdpZHRoPSIwLjgiLz4KICAgIDxsaW5lIHgxPSIyOCIgeTE9IjUyIiB4Mj0iMzQiIHkyPSI1MiIgc3Ryb2tlPSIjNjY3ZWVhIiBzdHJva2Utd2lkdGg9IjAuOCIvPgogICAgPGxpbmUgeDE9IjI4IiB5MT0iNTQiIHgyPSIzNiIgeTI9IjU0IiBzdHJva2U9IiM2NjdlZWEiIHN0cm9rZS13aWR0aD0iMC44Ii8+CiAgPC9nPgo8L3N2Zz4K" alt="AI Weekly" width="28" height="28" style="display:inline-block; vertical-align:middle;" />
|
909 |
+
<span>AI Weekly</span>
|
910 |
+
</div>
|
911 |
+
<div class="tagline">Your weekly dose of AI insights • {now}</div>
|
912 |
+
</div>
|
913 |
+
|
914 |
+
<!-- Main Content -->
|
915 |
+
<div class="content">
|
916 |
+
<!-- Welcome Section -->
|
917 |
+
<div class="section">
|
918 |
+
<h2>📧 This Week's Highlights</h2>
|
919 |
+
<p>Hello AI Tech Enthusiasts! Welcome to another edition of AI Weekly. This week, we're diving deep into the latest AI developments, breakthrough innovations, and emerging technologies that are shaping our digital future.</p>
|
920 |
+
</div>
|
921 |
+
|
922 |
+
<!-- Featured Article -->
|
923 |
+
<div class="section">
|
924 |
+
<h2>🌟 Featured Story</h2>
|
925 |
+
<div class="featured-article">
|
926 |
+
<h3>{featured_title}</h3>
|
927 |
+
<p>{featured_summary}</p>
|
928 |
+
<a href="{featured_link}" class="btn">Read Full Article</a>
|
929 |
+
</div>
|
930 |
+
</div>
|
931 |
+
|
932 |
+
<!-- News Section -->
|
933 |
+
<div class="section">
|
934 |
+
<h2>📰 Latest AI News</h2>
|
935 |
+
<div class="news-grid">
|
936 |
+
{news_items}
|
937 |
+
</div>
|
938 |
+
</div>
|
939 |
+
|
940 |
+
<!-- CTA Section -->
|
941 |
+
<div class="section">
|
942 |
+
<div class="cta-section">
|
943 |
+
<h3>🤖 Stay Connected</h3>
|
944 |
+
<p>Join thousands of AI enthusiasts getting the latest insights delivered weekly.</p>
|
945 |
+
<a href="#" class="cta-btn">Subscribe for Updates</a>
|
946 |
+
</div>
|
947 |
+
</div>
|
948 |
+
|
949 |
+
<!-- Social Links -->
|
950 |
+
<div class="social-links">
|
951 |
+
<a href="#">🐦</a>
|
952 |
+
<a href="#">📘</a>
|
953 |
+
<a href="#">💼</a>
|
954 |
+
<a href="#">📧</a>
|
955 |
+
</div>
|
956 |
+
</div>
|
957 |
+
|
958 |
+
<!-- Footer -->
|
959 |
+
<div class="footer">
|
960 |
+
<p><strong>AI Weekly</strong></p>
|
961 |
+
<p>Curated with ❤️ by AI Newsletter</p>
|
962 |
+
<p>© 2025 AI Weekly. All rights reserved.</p>
|
963 |
+
<p style="margin-top: 20px;">
|
964 |
+
<a href="#">Unsubscribe</a> |
|
965 |
+
<a href="#">Update Preferences</a> |
|
966 |
+
<a href="#">Privacy Policy</a>
|
967 |
+
</p>
|
968 |
+
</div>
|
969 |
+
</div>
|
970 |
+
</body>
|
971 |
+
</html>"""
|
972 |
+
|
973 |
+
|
974 |
+
@app.post("/api/newsletter", response_model=NewsletterResponse)
|
975 |
+
def newsletter(req: NewsletterRequest) -> NewsletterResponse:
|
976 |
+
memory = get_memory(req.session_id)
|
977 |
+
if req.prior_history:
|
978 |
+
memory.history.extend(req.prior_history[-8:])
|
979 |
+
html = _build_newsletter_html(req.summary_markdown, req.articles)
|
980 |
+
memory.last_newsletter_html = html
|
981 |
+
return NewsletterResponse(html=html)
|
982 |
+
|
983 |
+
|
984 |
+
@app.post("/api/edit", response_model=EditResponse)
|
985 |
+
def edit(req: EditRequest) -> EditResponse:
|
986 |
+
memory = get_memory(req.session_id)
|
987 |
+
if req.prior_history:
|
988 |
+
# Allow client to supply recent context from local storage when serverless memory resets
|
989 |
+
memory.history.extend(req.prior_history[-8:])
|
990 |
+
|
991 |
+
system = (
|
992 |
+
"You are a helpful writing assistant. Edit the provided text according to the instruction, "
|
993 |
+
"preserving facts and links. Return only the edited text."
|
994 |
+
)
|
995 |
+
user = f"Instruction: {req.instruction}\n\nText to edit:\n{req.text}"
|
996 |
+
messages = (
|
997 |
+
[{"role": "system", "content": system}]
|
998 |
+
+ [{"role": t.role, "content": t.content} for t in memory.history[-8:]]
|
999 |
+
+ [{"role": "user", "content": user}]
|
1000 |
+
)
|
1001 |
+
content = _chat(messages, temperature=0.4)
|
1002 |
+
turn_user = ConversationTurn(role="user", content=user)
|
1003 |
+
turn_assistant = ConversationTurn(role="assistant", content=content)
|
1004 |
+
memory.history.append(turn_user)
|
1005 |
+
memory.history.append(turn_assistant)
|
1006 |
+
return EditResponse(edited_text=content, history=memory.history[-10:])
|
1007 |
+
|
1008 |
+
|
1009 |
+
@app.post("/api/edit_tweet", response_model=TweetEditResponse)
|
1010 |
+
def edit_tweet(req: TweetEditRequest) -> TweetEditResponse:
|
1011 |
+
# Get or create conversation history for this specific tweet
|
1012 |
+
conversation_key = f"{req.session_id}_tweet_{req.tweet_id}"
|
1013 |
+
if conversation_key not in memory_store:
|
1014 |
+
memory_store[conversation_key] = SessionMemory(session_id=conversation_key)
|
1015 |
+
|
1016 |
+
tweet_memory = memory_store[conversation_key]
|
1017 |
+
|
1018 |
+
# Add any provided conversation history
|
1019 |
+
if req.conversation_history:
|
1020 |
+
tweet_memory.history.extend(req.conversation_history)
|
1021 |
+
|
1022 |
+
system = (
|
1023 |
+
"You are an AI assistant helping to edit and improve Twitter/X posts. "
|
1024 |
+
"You have context about the original article summary and the current tweet. "
|
1025 |
+
"Help the user modify the tweet based on their requests while keeping it STRICTLY under 280 characters. "
|
1026 |
+
"CRITICAL: Count characters carefully - if adding hashtags would exceed 280 chars, shorten the main text to make room. "
|
1027 |
+
"IMPORTANT: Always structure your response as follows:\n"
|
1028 |
+
"1. A brief conversational response to the user\n"
|
1029 |
+
"2. Then on a new line, write 'UPDATED TWEET:' followed by the new tweet content\n"
|
1030 |
+
"Example format:\n"
|
1031 |
+
"Sure! I'll add more hashtags and shorten the text to fit.\n\n"
|
1032 |
+
"UPDATED TWEET: Your concise tweet content with #hashtags #AI #Tech"
|
1033 |
+
)
|
1034 |
+
|
1035 |
+
context = (
|
1036 |
+
f"Original Article Summary: {req.original_summary}\n"
|
1037 |
+
f"Current Tweet: {req.current_tweet}\n"
|
1038 |
+
f"User Request: {req.user_message}"
|
1039 |
+
)
|
1040 |
+
|
1041 |
+
messages = (
|
1042 |
+
[{"role": "system", "content": system}]
|
1043 |
+
+ [{"role": t.role, "content": t.content} for t in tweet_memory.history[-6:]]
|
1044 |
+
+ [{"role": "user", "content": context}]
|
1045 |
+
)
|
1046 |
+
|
1047 |
+
ai_response = _chat(messages, temperature=0.7)
|
1048 |
+
|
1049 |
+
# Extract the new tweet and AI message using the structured format
|
1050 |
+
new_tweet = req.current_tweet # Fallback to current tweet
|
1051 |
+
ai_message = ai_response
|
1052 |
+
|
1053 |
+
# Look for "UPDATED TWEET:" pattern
|
1054 |
+
if "UPDATED TWEET:" in ai_response:
|
1055 |
+
parts = ai_response.split("UPDATED TWEET:", 1)
|
1056 |
+
if len(parts) == 2:
|
1057 |
+
ai_message = parts[0].strip()
|
1058 |
+
new_tweet = parts[1].strip()
|
1059 |
+
|
1060 |
+
# Clean up the new tweet (remove any quotes or extra formatting)
|
1061 |
+
new_tweet = new_tweet.strip('"').strip("'").strip()
|
1062 |
+
|
1063 |
+
# Validate tweet length and truncate smartly
|
1064 |
+
if len(new_tweet) > 280:
|
1065 |
+
# Try to truncate at word boundaries to avoid cutting hashtags
|
1066 |
+
words = new_tweet.split(' ')
|
1067 |
+
truncated = ""
|
1068 |
+
for word in words:
|
1069 |
+
if len(truncated + " " + word) <= 280:
|
1070 |
+
if truncated:
|
1071 |
+
truncated += " " + word
|
1072 |
+
else:
|
1073 |
+
truncated = word
|
1074 |
+
else:
|
1075 |
+
break
|
1076 |
+
new_tweet = truncated if truncated else new_tweet[:280]
|
1077 |
+
|
1078 |
+
if not ai_message:
|
1079 |
+
ai_message = "I've updated your tweet based on your request!"
|
1080 |
+
else:
|
1081 |
+
# Fallback: if the structured format wasn't followed, try to extract tweet-like content
|
1082 |
+
lines = ai_response.split('\n')
|
1083 |
+
for line in lines:
|
1084 |
+
line = line.strip()
|
1085 |
+
if len(line) > 20 and len(line) <= 280 and ('#' in line or '@' in line or any(emoji in line for emoji in ['🔥', '🚀', '💡', '🤖', '⚡'])):
|
1086 |
+
new_tweet = line
|
1087 |
+
ai_message = ai_response.replace(new_tweet, "").strip()
|
1088 |
+
if not ai_message:
|
1089 |
+
ai_message = "I've updated your tweet based on your request!"
|
1090 |
+
break
|
1091 |
+
|
1092 |
+
# Store conversation
|
1093 |
+
turn_user = ConversationTurn(role="user", content=req.user_message)
|
1094 |
+
turn_assistant = ConversationTurn(role="assistant", content=ai_response)
|
1095 |
+
tweet_memory.history.append(turn_user)
|
1096 |
+
tweet_memory.history.append(turn_assistant)
|
1097 |
+
|
1098 |
+
return TweetEditResponse(
|
1099 |
+
new_tweet=new_tweet,
|
1100 |
+
ai_response=ai_message,
|
1101 |
+
conversation_history=tweet_memory.history[-10:]
|
1102 |
+
)
|
1103 |
+
|
1104 |
+
|
1105 |
+
# Provide a synchronous alternative endpoint with explicit model
|
1106 |
+
class DownloadRequest(BaseModel):
|
1107 |
+
session_id: Optional[str] = None
|
1108 |
+
html: Optional[str] = None
|
1109 |
+
|
1110 |
+
|
1111 |
+
@app.post("/api/download_html")
|
1112 |
+
def download_html(req: DownloadRequest):
|
1113 |
+
html = req.html
|
1114 |
+
if not html and req.session_id:
|
1115 |
+
mem = get_memory(req.session_id)
|
1116 |
+
html = mem.last_newsletter_html
|
1117 |
+
if not html:
|
1118 |
+
raise HTTPException(status_code=400, detail="No HTML provided or found for session")
|
1119 |
+
buffer = io.BytesIO(html.encode("utf-8"))
|
1120 |
+
headers = {
|
1121 |
+
"Content-Disposition": "attachment; filename=ai_weekly.html"
|
1122 |
+
}
|
1123 |
+
return Response(content=buffer.getvalue(), headers=headers, media_type="text/html")
|
1124 |
+
|
1125 |
+
|
1126 |
+
# Catch-all route for SPA routing - MUST be at the very end
|
1127 |
+
@app.get("/{path:path}")
|
1128 |
+
def catch_all(path: str):
|
1129 |
+
"""Catch-all route to serve React app for client-side routing"""
|
1130 |
+
# Don't intercept API routes
|
1131 |
+
if path.startswith("api/"):
|
1132 |
+
raise HTTPException(status_code=404, detail="API endpoint not found")
|
1133 |
+
|
1134 |
+
static_dir = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
|
1135 |
+
index_file = os.path.join(static_dir, "index.html")
|
1136 |
+
|
1137 |
+
# Serve static files if they exist
|
1138 |
+
file_path = os.path.join(static_dir, path)
|
1139 |
+
if os.path.isfile(file_path):
|
1140 |
+
return FileResponse(file_path)
|
1141 |
+
|
1142 |
+
# Otherwise serve index.html for SPA routing
|
1143 |
+
if os.path.exists(index_file):
|
1144 |
+
return FileResponse(index_file)
|
1145 |
+
|
1146 |
+
# Fallback if no frontend built
|
1147 |
+
return {"status": "error", "message": "Frontend not available"}
|
1148 |
+
|
1149 |
+
|
1150 |
+
# Lambda handler for AWS
|
1151 |
+
def handler(event, context):
|
1152 |
+
"""AWS Lambda handler for FastAPI - Version 2.0"""
|
1153 |
+
print(f"[DEBUG v2.0] Lambda handler called with event: {event.get('httpMethod', 'unknown')}")
|
1154 |
+
print(f"[DEBUG v2.0] Event keys: {list(event.keys())}")
|
1155 |
+
try:
|
1156 |
+
from mangum import Mangum
|
1157 |
+
print("[DEBUG v2.0] Mangum imported successfully")
|
1158 |
+
asgi_handler = Mangum(app)
|
1159 |
+
print("[DEBUG v2.0] Mangum handler created")
|
1160 |
+
result = asgi_handler(event, context)
|
1161 |
+
print(f"[DEBUG v2.0] Handler result type: {type(result)}")
|
1162 |
+
return result
|
1163 |
+
except Exception as e:
|
1164 |
+
print(f"[ERROR v2.0] Handler failed: {str(e)}")
|
1165 |
+
import traceback
|
1166 |
+
traceback.print_exc()
|
1167 |
+
raise
|
1168 |
+
|
1169 |
+
|
1170 |
+
# Export for Vercel - app is automatically detected
|
1171 |
+
if __name__ == "__main__":
|
1172 |
+
import uvicorn
|
1173 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
frontend/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
pnpm-debug.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
|
10 |
+
node_modules
|
11 |
+
dist
|
12 |
+
dist-ssr
|
13 |
+
*.local
|
14 |
+
|
15 |
+
# Editor directories and files
|
16 |
+
.vscode/*
|
17 |
+
!.vscode/extensions.json
|
18 |
+
.idea
|
19 |
+
.DS_Store
|
20 |
+
*.suo
|
21 |
+
*.ntvs*
|
22 |
+
*.njsproj
|
23 |
+
*.sln
|
24 |
+
*.sw?
|
frontend/.nvmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
20.11.1
|
frontend/.pnpmfile.cjs
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// pnpm configuration for the AI Newsletter frontend
|
2 |
+
module.exports = {
|
3 |
+
hooks: {
|
4 |
+
readPackage(pkg) {
|
5 |
+
// Ensure React types compatibility
|
6 |
+
if (pkg.name === '@types/react') {
|
7 |
+
pkg.peerDependencies = {
|
8 |
+
...pkg.peerDependencies,
|
9 |
+
'react': '*'
|
10 |
+
}
|
11 |
+
}
|
12 |
+
return pkg
|
13 |
+
}
|
14 |
+
}
|
15 |
+
}
|
frontend/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# React + TypeScript + Vite
|
2 |
+
|
3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
4 |
+
|
5 |
+
Currently, two official plugins are available:
|
6 |
+
|
7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
9 |
+
|
10 |
+
## Expanding the ESLint configuration
|
11 |
+
|
12 |
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
13 |
+
|
14 |
+
```js
|
15 |
+
export default tseslint.config([
|
16 |
+
globalIgnores(['dist']),
|
17 |
+
{
|
18 |
+
files: ['**/*.{ts,tsx}'],
|
19 |
+
extends: [
|
20 |
+
// Other configs...
|
21 |
+
|
22 |
+
// Remove tseslint.configs.recommended and replace with this
|
23 |
+
...tseslint.configs.recommendedTypeChecked,
|
24 |
+
// Alternatively, use this for stricter rules
|
25 |
+
...tseslint.configs.strictTypeChecked,
|
26 |
+
// Optionally, add this for stylistic rules
|
27 |
+
...tseslint.configs.stylisticTypeChecked,
|
28 |
+
|
29 |
+
// Other configs...
|
30 |
+
],
|
31 |
+
languageOptions: {
|
32 |
+
parserOptions: {
|
33 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
34 |
+
tsconfigRootDir: import.meta.dirname,
|
35 |
+
},
|
36 |
+
// other options...
|
37 |
+
},
|
38 |
+
},
|
39 |
+
])
|
40 |
+
```
|
41 |
+
|
42 |
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
43 |
+
|
44 |
+
```js
|
45 |
+
// eslint.config.js
|
46 |
+
import reactX from 'eslint-plugin-react-x'
|
47 |
+
import reactDom from 'eslint-plugin-react-dom'
|
48 |
+
|
49 |
+
export default tseslint.config([
|
50 |
+
globalIgnores(['dist']),
|
51 |
+
{
|
52 |
+
files: ['**/*.{ts,tsx}'],
|
53 |
+
extends: [
|
54 |
+
// Other configs...
|
55 |
+
// Enable lint rules for React
|
56 |
+
reactX.configs['recommended-typescript'],
|
57 |
+
// Enable lint rules for React DOM
|
58 |
+
reactDom.configs.recommended,
|
59 |
+
],
|
60 |
+
languageOptions: {
|
61 |
+
parserOptions: {
|
62 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
63 |
+
tsconfigRootDir: import.meta.dirname,
|
64 |
+
},
|
65 |
+
// other options...
|
66 |
+
},
|
67 |
+
},
|
68 |
+
])
|
69 |
+
```
|
frontend/eslint.config.js
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import js from '@eslint/js'
|
2 |
+
import globals from 'globals'
|
3 |
+
import reactHooks from 'eslint-plugin-react-hooks'
|
4 |
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
5 |
+
import tseslint from 'typescript-eslint'
|
6 |
+
import { globalIgnores } from 'eslint/config'
|
7 |
+
|
8 |
+
export default tseslint.config([
|
9 |
+
globalIgnores(['dist']),
|
10 |
+
{
|
11 |
+
files: ['**/*.{ts,tsx}'],
|
12 |
+
extends: [
|
13 |
+
js.configs.recommended,
|
14 |
+
tseslint.configs.recommended,
|
15 |
+
reactHooks.configs['recommended-latest'],
|
16 |
+
reactRefresh.configs.vite,
|
17 |
+
],
|
18 |
+
languageOptions: {
|
19 |
+
ecmaVersion: 2020,
|
20 |
+
globals: globals.browser,
|
21 |
+
},
|
22 |
+
},
|
23 |
+
])
|
frontend/index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Vite + React + TS</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="root"></div>
|
11 |
+
<script type="module" src="/src/main.tsx"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
frontend/package.json
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "frontend",
|
3 |
+
"private": true,
|
4 |
+
"version": "0.0.0",
|
5 |
+
"type": "module",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "vite --port 3002",
|
8 |
+
"build": "tsc -b && vite build",
|
9 |
+
"lint": "eslint .",
|
10 |
+
"preview": "vite preview",
|
11 |
+
"clean": "rm -rf dist node_modules/.vite",
|
12 |
+
"type-check": "tsc --noEmit"
|
13 |
+
},
|
14 |
+
"dependencies": {
|
15 |
+
"react": "^19.1.1",
|
16 |
+
"react-dom": "^19.1.1"
|
17 |
+
},
|
18 |
+
"devDependencies": {
|
19 |
+
"@eslint/js": "^9.32.0",
|
20 |
+
"@types/react": "^19.1.9",
|
21 |
+
"@types/react-dom": "^19.1.7",
|
22 |
+
"@vitejs/plugin-react": "^4.7.0",
|
23 |
+
"autoprefixer": "^10.4.21",
|
24 |
+
"classnames": "^2.5.1",
|
25 |
+
"eslint": "^9.32.0",
|
26 |
+
"eslint-plugin-react-hooks": "^5.2.0",
|
27 |
+
"eslint-plugin-react-refresh": "^0.4.20",
|
28 |
+
"globals": "^16.3.0",
|
29 |
+
"postcss": "^8.5.6",
|
30 |
+
"tailwindcss": "^3.4.17",
|
31 |
+
"typescript": "~5.8.3",
|
32 |
+
"typescript-eslint": "^8.39.0",
|
33 |
+
"vite": "^7.1.0"
|
34 |
+
},
|
35 |
+
"packageManager": "[email protected]",
|
36 |
+
"engines": {
|
37 |
+
"node": ">=18.0.0",
|
38 |
+
"pnpm": ">=8.0.0"
|
39 |
+
}
|
40 |
+
}
|
frontend/pnpm-lock.yaml
ADDED
@@ -0,0 +1,2780 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
lockfileVersion: '9.0'
|
2 |
+
|
3 |
+
settings:
|
4 |
+
autoInstallPeers: true
|
5 |
+
excludeLinksFromLockfile: false
|
6 |
+
|
7 |
+
pnpmfileChecksum: sha256-gvDTDqUZJL4m3Sr1Vuxqtbv0pSJ33/voxpVpvmgK1ic=
|
8 |
+
|
9 |
+
importers:
|
10 |
+
|
11 |
+
.:
|
12 |
+
dependencies:
|
13 |
+
react:
|
14 |
+
specifier: ^19.1.1
|
15 |
+
version: 19.1.1
|
16 |
+
react-dom:
|
17 |
+
specifier: ^19.1.1
|
18 |
+
version: 19.1.1([email protected])
|
19 |
+
devDependencies:
|
20 |
+
'@eslint/js':
|
21 |
+
specifier: ^9.32.0
|
22 |
+
version: 9.33.0
|
23 |
+
'@types/react':
|
24 |
+
specifier: ^19.1.9
|
25 |
+
version: 19.1.9([email protected])
|
26 |
+
'@types/react-dom':
|
27 |
+
specifier: ^19.1.7
|
28 |
+
version: 19.1.7(@types/[email protected]([email protected]))
|
29 |
+
'@vitejs/plugin-react':
|
30 |
+
specifier: ^4.7.0
|
31 |
+
version: 4.7.0([email protected]([email protected])([email protected])([email protected]))
|
32 |
+
autoprefixer:
|
33 |
+
specifier: ^10.4.21
|
34 |
+
version: 10.4.21([email protected])
|
35 |
+
classnames:
|
36 |
+
specifier: ^2.5.1
|
37 |
+
version: 2.5.1
|
38 |
+
eslint:
|
39 |
+
specifier: ^9.32.0
|
40 |
+
version: 9.33.0([email protected])
|
41 |
+
eslint-plugin-react-hooks:
|
42 |
+
specifier: ^5.2.0
|
43 |
+
version: 5.2.0([email protected]([email protected]))
|
44 |
+
eslint-plugin-react-refresh:
|
45 |
+
specifier: ^0.4.20
|
46 |
+
version: 0.4.20([email protected]([email protected]))
|
47 |
+
globals:
|
48 |
+
specifier: ^16.3.0
|
49 |
+
version: 16.3.0
|
50 |
+
postcss:
|
51 |
+
specifier: ^8.5.6
|
52 |
+
version: 8.5.6
|
53 |
+
tailwindcss:
|
54 |
+
specifier: ^3.4.17
|
55 |
+
version: 3.4.17
|
56 |
+
typescript:
|
57 |
+
specifier: ~5.8.3
|
58 |
+
version: 5.8.3
|
59 |
+
typescript-eslint:
|
60 |
+
specifier: ^8.39.0
|
61 |
+
version: 8.39.0([email protected]([email protected]))([email protected])
|
62 |
+
vite:
|
63 |
+
specifier: ^7.1.0
|
64 |
+
version: 7.1.1([email protected])([email protected])([email protected])
|
65 |
+
|
66 |
+
packages:
|
67 |
+
|
68 |
+
'@alloc/[email protected]':
|
69 |
+
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
70 |
+
engines: {node: '>=10'}
|
71 |
+
|
72 |
+
'@ampproject/[email protected]':
|
73 |
+
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
74 |
+
engines: {node: '>=6.0.0'}
|
75 |
+
|
76 |
+
'@babel/[email protected]':
|
77 |
+
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
78 |
+
engines: {node: '>=6.9.0'}
|
79 |
+
|
80 |
+
'@babel/[email protected]':
|
81 |
+
resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
|
82 |
+
engines: {node: '>=6.9.0'}
|
83 |
+
|
84 |
+
'@babel/[email protected]':
|
85 |
+
resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==}
|
86 |
+
engines: {node: '>=6.9.0'}
|
87 |
+
|
88 |
+
'@babel/[email protected]':
|
89 |
+
resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
|
90 |
+
engines: {node: '>=6.9.0'}
|
91 |
+
|
92 |
+
'@babel/[email protected]':
|
93 |
+
resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
|
94 |
+
engines: {node: '>=6.9.0'}
|
95 |
+
|
96 |
+
'@babel/[email protected]':
|
97 |
+
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
|
98 |
+
engines: {node: '>=6.9.0'}
|
99 |
+
|
100 |
+
'@babel/[email protected]':
|
101 |
+
resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
|
102 |
+
engines: {node: '>=6.9.0'}
|
103 |
+
|
104 |
+
'@babel/[email protected]':
|
105 |
+
resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
|
106 |
+
engines: {node: '>=6.9.0'}
|
107 |
+
peerDependencies:
|
108 |
+
'@babel/core': ^7.0.0
|
109 |
+
|
110 |
+
'@babel/[email protected]':
|
111 |
+
resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
|
112 |
+
engines: {node: '>=6.9.0'}
|
113 |
+
|
114 |
+
'@babel/[email protected]':
|
115 |
+
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
116 |
+
engines: {node: '>=6.9.0'}
|
117 |
+
|
118 |
+
'@babel/[email protected]':
|
119 |
+
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
120 |
+
engines: {node: '>=6.9.0'}
|
121 |
+
|
122 |
+
'@babel/[email protected]':
|
123 |
+
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
|
124 |
+
engines: {node: '>=6.9.0'}
|
125 |
+
|
126 |
+
'@babel/[email protected]':
|
127 |
+
resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==}
|
128 |
+
engines: {node: '>=6.9.0'}
|
129 |
+
|
130 |
+
'@babel/[email protected]':
|
131 |
+
resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
|
132 |
+
engines: {node: '>=6.0.0'}
|
133 |
+
hasBin: true
|
134 |
+
|
135 |
+
'@babel/[email protected]':
|
136 |
+
resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
|
137 |
+
engines: {node: '>=6.9.0'}
|
138 |
+
peerDependencies:
|
139 |
+
'@babel/core': ^7.0.0-0
|
140 |
+
|
141 |
+
'@babel/[email protected]':
|
142 |
+
resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
|
143 |
+
engines: {node: '>=6.9.0'}
|
144 |
+
peerDependencies:
|
145 |
+
'@babel/core': ^7.0.0-0
|
146 |
+
|
147 |
+
'@babel/[email protected]':
|
148 |
+
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
|
149 |
+
engines: {node: '>=6.9.0'}
|
150 |
+
|
151 |
+
'@babel/[email protected]':
|
152 |
+
resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==}
|
153 |
+
engines: {node: '>=6.9.0'}
|
154 |
+
|
155 |
+
'@babel/[email protected]':
|
156 |
+
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
|
157 |
+
engines: {node: '>=6.9.0'}
|
158 |
+
|
159 |
+
'@esbuild/[email protected]':
|
160 |
+
resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
|
161 |
+
engines: {node: '>=18'}
|
162 |
+
cpu: [ppc64]
|
163 |
+
os: [aix]
|
164 |
+
|
165 |
+
'@esbuild/[email protected]':
|
166 |
+
resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
|
167 |
+
engines: {node: '>=18'}
|
168 |
+
cpu: [arm64]
|
169 |
+
os: [android]
|
170 |
+
|
171 |
+
'@esbuild/[email protected]':
|
172 |
+
resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
|
173 |
+
engines: {node: '>=18'}
|
174 |
+
cpu: [arm]
|
175 |
+
os: [android]
|
176 |
+
|
177 |
+
'@esbuild/[email protected]':
|
178 |
+
resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
|
179 |
+
engines: {node: '>=18'}
|
180 |
+
cpu: [x64]
|
181 |
+
os: [android]
|
182 |
+
|
183 |
+
'@esbuild/[email protected]':
|
184 |
+
resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
|
185 |
+
engines: {node: '>=18'}
|
186 |
+
cpu: [arm64]
|
187 |
+
os: [darwin]
|
188 |
+
|
189 |
+
'@esbuild/[email protected]':
|
190 |
+
resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
|
191 |
+
engines: {node: '>=18'}
|
192 |
+
cpu: [x64]
|
193 |
+
os: [darwin]
|
194 |
+
|
195 |
+
'@esbuild/[email protected]':
|
196 |
+
resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
|
197 |
+
engines: {node: '>=18'}
|
198 |
+
cpu: [arm64]
|
199 |
+
os: [freebsd]
|
200 |
+
|
201 |
+
'@esbuild/[email protected]':
|
202 |
+
resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
|
203 |
+
engines: {node: '>=18'}
|
204 |
+
cpu: [x64]
|
205 |
+
os: [freebsd]
|
206 |
+
|
207 |
+
'@esbuild/[email protected]':
|
208 |
+
resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
|
209 |
+
engines: {node: '>=18'}
|
210 |
+
cpu: [arm64]
|
211 |
+
os: [linux]
|
212 |
+
|
213 |
+
'@esbuild/[email protected]':
|
214 |
+
resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
|
215 |
+
engines: {node: '>=18'}
|
216 |
+
cpu: [arm]
|
217 |
+
os: [linux]
|
218 |
+
|
219 |
+
'@esbuild/[email protected]':
|
220 |
+
resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
|
221 |
+
engines: {node: '>=18'}
|
222 |
+
cpu: [ia32]
|
223 |
+
os: [linux]
|
224 |
+
|
225 |
+
'@esbuild/[email protected]':
|
226 |
+
resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
|
227 |
+
engines: {node: '>=18'}
|
228 |
+
cpu: [loong64]
|
229 |
+
os: [linux]
|
230 |
+
|
231 |
+
'@esbuild/[email protected]':
|
232 |
+
resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
|
233 |
+
engines: {node: '>=18'}
|
234 |
+
cpu: [mips64el]
|
235 |
+
os: [linux]
|
236 |
+
|
237 |
+
'@esbuild/[email protected]':
|
238 |
+
resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
|
239 |
+
engines: {node: '>=18'}
|
240 |
+
cpu: [ppc64]
|
241 |
+
os: [linux]
|
242 |
+
|
243 |
+
'@esbuild/[email protected]':
|
244 |
+
resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
|
245 |
+
engines: {node: '>=18'}
|
246 |
+
cpu: [riscv64]
|
247 |
+
os: [linux]
|
248 |
+
|
249 |
+
'@esbuild/[email protected]':
|
250 |
+
resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
|
251 |
+
engines: {node: '>=18'}
|
252 |
+
cpu: [s390x]
|
253 |
+
os: [linux]
|
254 |
+
|
255 |
+
'@esbuild/[email protected]':
|
256 |
+
resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
|
257 |
+
engines: {node: '>=18'}
|
258 |
+
cpu: [x64]
|
259 |
+
os: [linux]
|
260 |
+
|
261 |
+
'@esbuild/[email protected]':
|
262 |
+
resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
|
263 |
+
engines: {node: '>=18'}
|
264 |
+
cpu: [arm64]
|
265 |
+
os: [netbsd]
|
266 |
+
|
267 |
+
'@esbuild/[email protected]':
|
268 |
+
resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
|
269 |
+
engines: {node: '>=18'}
|
270 |
+
cpu: [x64]
|
271 |
+
os: [netbsd]
|
272 |
+
|
273 |
+
'@esbuild/[email protected]':
|
274 |
+
resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
|
275 |
+
engines: {node: '>=18'}
|
276 |
+
cpu: [arm64]
|
277 |
+
os: [openbsd]
|
278 |
+
|
279 |
+
'@esbuild/[email protected]':
|
280 |
+
resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
|
281 |
+
engines: {node: '>=18'}
|
282 |
+
cpu: [x64]
|
283 |
+
os: [openbsd]
|
284 |
+
|
285 |
+
'@esbuild/[email protected]':
|
286 |
+
resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
|
287 |
+
engines: {node: '>=18'}
|
288 |
+
cpu: [arm64]
|
289 |
+
os: [openharmony]
|
290 |
+
|
291 |
+
'@esbuild/[email protected]':
|
292 |
+
resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
|
293 |
+
engines: {node: '>=18'}
|
294 |
+
cpu: [x64]
|
295 |
+
os: [sunos]
|
296 |
+
|
297 |
+
'@esbuild/[email protected]':
|
298 |
+
resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
|
299 |
+
engines: {node: '>=18'}
|
300 |
+
cpu: [arm64]
|
301 |
+
os: [win32]
|
302 |
+
|
303 |
+
'@esbuild/[email protected]':
|
304 |
+
resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
|
305 |
+
engines: {node: '>=18'}
|
306 |
+
cpu: [ia32]
|
307 |
+
os: [win32]
|
308 |
+
|
309 |
+
'@esbuild/[email protected]':
|
310 |
+
resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
|
311 |
+
engines: {node: '>=18'}
|
312 |
+
cpu: [x64]
|
313 |
+
os: [win32]
|
314 |
+
|
315 |
+
'@eslint-community/[email protected]':
|
316 |
+
resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
|
317 |
+
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
318 |
+
peerDependencies:
|
319 |
+
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
320 |
+
|
321 |
+
'@eslint-community/[email protected]':
|
322 |
+
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
|
323 |
+
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
324 |
+
|
325 |
+
'@eslint/[email protected]':
|
326 |
+
resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
|
327 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
328 |
+
|
329 |
+
'@eslint/[email protected]':
|
330 |
+
resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
|
331 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
332 |
+
|
333 |
+
'@eslint/[email protected]':
|
334 |
+
resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
|
335 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
336 |
+
|
337 |
+
'@eslint/[email protected]':
|
338 |
+
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
|
339 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
340 |
+
|
341 |
+
'@eslint/[email protected]':
|
342 |
+
resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==}
|
343 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
344 |
+
|
345 |
+
'@eslint/[email protected]':
|
346 |
+
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
|
347 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
348 |
+
|
349 |
+
'@eslint/[email protected]':
|
350 |
+
resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
|
351 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
352 |
+
|
353 |
+
'@humanfs/[email protected]':
|
354 |
+
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
355 |
+
engines: {node: '>=18.18.0'}
|
356 |
+
|
357 |
+
'@humanfs/[email protected]':
|
358 |
+
resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
|
359 |
+
engines: {node: '>=18.18.0'}
|
360 |
+
|
361 |
+
'@humanwhocodes/[email protected]':
|
362 |
+
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
363 |
+
engines: {node: '>=12.22'}
|
364 |
+
|
365 |
+
'@humanwhocodes/[email protected]':
|
366 |
+
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
|
367 |
+
engines: {node: '>=18.18'}
|
368 |
+
|
369 |
+
'@humanwhocodes/[email protected]':
|
370 |
+
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
371 |
+
engines: {node: '>=18.18'}
|
372 |
+
|
373 |
+
'@isaacs/[email protected]':
|
374 |
+
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
375 |
+
engines: {node: '>=12'}
|
376 |
+
|
377 |
+
'@jridgewell/[email protected]':
|
378 |
+
resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
|
379 |
+
|
380 |
+
'@jridgewell/[email protected]':
|
381 |
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
382 |
+
engines: {node: '>=6.0.0'}
|
383 |
+
|
384 |
+
'@jridgewell/[email protected]':
|
385 |
+
resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
|
386 |
+
|
387 |
+
'@jridgewell/[email protected]':
|
388 |
+
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
|
389 |
+
|
390 |
+
'@nodelib/[email protected]':
|
391 |
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
392 |
+
engines: {node: '>= 8'}
|
393 |
+
|
394 |
+
'@nodelib/[email protected]':
|
395 |
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
|
396 |
+
engines: {node: '>= 8'}
|
397 |
+
|
398 |
+
'@nodelib/[email protected]':
|
399 |
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
400 |
+
engines: {node: '>= 8'}
|
401 |
+
|
402 |
+
'@pkgjs/[email protected]':
|
403 |
+
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
404 |
+
engines: {node: '>=14'}
|
405 |
+
|
406 |
+
'@rolldown/[email protected]':
|
407 |
+
resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
|
408 |
+
|
409 |
+
'@rollup/[email protected]':
|
410 |
+
resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
|
411 |
+
cpu: [arm]
|
412 |
+
os: [android]
|
413 |
+
|
414 |
+
'@rollup/[email protected]':
|
415 |
+
resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
|
416 |
+
cpu: [arm64]
|
417 |
+
os: [android]
|
418 |
+
|
419 |
+
'@rollup/[email protected]':
|
420 |
+
resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
|
421 |
+
cpu: [arm64]
|
422 |
+
os: [darwin]
|
423 |
+
|
424 |
+
'@rollup/[email protected]':
|
425 |
+
resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
|
426 |
+
cpu: [x64]
|
427 |
+
os: [darwin]
|
428 |
+
|
429 |
+
'@rollup/[email protected]':
|
430 |
+
resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
|
431 |
+
cpu: [arm64]
|
432 |
+
os: [freebsd]
|
433 |
+
|
434 |
+
'@rollup/[email protected]':
|
435 |
+
resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
|
436 |
+
cpu: [x64]
|
437 |
+
os: [freebsd]
|
438 |
+
|
439 |
+
'@rollup/[email protected]':
|
440 |
+
resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
|
441 |
+
cpu: [arm]
|
442 |
+
os: [linux]
|
443 |
+
|
444 |
+
'@rollup/[email protected]':
|
445 |
+
resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
|
446 |
+
cpu: [arm]
|
447 |
+
os: [linux]
|
448 |
+
|
449 |
+
'@rollup/[email protected]':
|
450 |
+
resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
|
451 |
+
cpu: [arm64]
|
452 |
+
os: [linux]
|
453 |
+
|
454 |
+
'@rollup/[email protected]':
|
455 |
+
resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
|
456 |
+
cpu: [arm64]
|
457 |
+
os: [linux]
|
458 |
+
|
459 |
+
'@rollup/[email protected]':
|
460 |
+
resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
|
461 |
+
cpu: [loong64]
|
462 |
+
os: [linux]
|
463 |
+
|
464 |
+
'@rollup/[email protected]':
|
465 |
+
resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
|
466 |
+
cpu: [ppc64]
|
467 |
+
os: [linux]
|
468 |
+
|
469 |
+
'@rollup/[email protected]':
|
470 |
+
resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
|
471 |
+
cpu: [riscv64]
|
472 |
+
os: [linux]
|
473 |
+
|
474 |
+
'@rollup/[email protected]':
|
475 |
+
resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
|
476 |
+
cpu: [riscv64]
|
477 |
+
os: [linux]
|
478 |
+
|
479 |
+
'@rollup/[email protected]':
|
480 |
+
resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
|
481 |
+
cpu: [s390x]
|
482 |
+
os: [linux]
|
483 |
+
|
484 |
+
'@rollup/[email protected]':
|
485 |
+
resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
|
486 |
+
cpu: [x64]
|
487 |
+
os: [linux]
|
488 |
+
|
489 |
+
'@rollup/[email protected]':
|
490 |
+
resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
|
491 |
+
cpu: [x64]
|
492 |
+
os: [linux]
|
493 |
+
|
494 |
+
'@rollup/[email protected]':
|
495 |
+
resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
|
496 |
+
cpu: [arm64]
|
497 |
+
os: [win32]
|
498 |
+
|
499 |
+
'@rollup/[email protected]':
|
500 |
+
resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
|
501 |
+
cpu: [ia32]
|
502 |
+
os: [win32]
|
503 |
+
|
504 |
+
'@rollup/[email protected]':
|
505 |
+
resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
|
506 |
+
cpu: [x64]
|
507 |
+
os: [win32]
|
508 |
+
|
509 |
+
'@types/[email protected]':
|
510 |
+
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
511 |
+
|
512 |
+
'@types/[email protected]':
|
513 |
+
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
|
514 |
+
|
515 |
+
'@types/[email protected]':
|
516 |
+
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
|
517 |
+
|
518 |
+
'@types/[email protected]':
|
519 |
+
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
520 |
+
|
521 |
+
'@types/[email protected]':
|
522 |
+
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
523 |
+
|
524 |
+
'@types/[email protected]':
|
525 |
+
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
526 |
+
|
527 |
+
'@types/[email protected]':
|
528 |
+
resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==}
|
529 |
+
peerDependencies:
|
530 |
+
'@types/react': ^19.0.0
|
531 |
+
|
532 |
+
'@types/[email protected]':
|
533 |
+
resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==}
|
534 |
+
peerDependencies:
|
535 |
+
react: '*'
|
536 |
+
|
537 |
+
'@typescript-eslint/[email protected]':
|
538 |
+
resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
|
539 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
540 |
+
peerDependencies:
|
541 |
+
'@typescript-eslint/parser': ^8.39.0
|
542 |
+
eslint: ^8.57.0 || ^9.0.0
|
543 |
+
typescript: '>=4.8.4 <6.0.0'
|
544 |
+
|
545 |
+
'@typescript-eslint/[email protected]':
|
546 |
+
resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
|
547 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
548 |
+
peerDependencies:
|
549 |
+
eslint: ^8.57.0 || ^9.0.0
|
550 |
+
typescript: '>=4.8.4 <6.0.0'
|
551 |
+
|
552 |
+
'@typescript-eslint/[email protected]':
|
553 |
+
resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
|
554 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
555 |
+
peerDependencies:
|
556 |
+
typescript: '>=4.8.4 <6.0.0'
|
557 |
+
|
558 |
+
'@typescript-eslint/[email protected]':
|
559 |
+
resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
|
560 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
561 |
+
|
562 |
+
'@typescript-eslint/[email protected]':
|
563 |
+
resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
|
564 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
565 |
+
peerDependencies:
|
566 |
+
typescript: '>=4.8.4 <6.0.0'
|
567 |
+
|
568 |
+
'@typescript-eslint/[email protected]':
|
569 |
+
resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==}
|
570 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
571 |
+
peerDependencies:
|
572 |
+
eslint: ^8.57.0 || ^9.0.0
|
573 |
+
typescript: '>=4.8.4 <6.0.0'
|
574 |
+
|
575 |
+
'@typescript-eslint/[email protected]':
|
576 |
+
resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
|
577 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
578 |
+
|
579 |
+
'@typescript-eslint/[email protected]':
|
580 |
+
resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
|
581 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
582 |
+
peerDependencies:
|
583 |
+
typescript: '>=4.8.4 <6.0.0'
|
584 |
+
|
585 |
+
'@typescript-eslint/[email protected]':
|
586 |
+
resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
|
587 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
588 |
+
peerDependencies:
|
589 |
+
eslint: ^8.57.0 || ^9.0.0
|
590 |
+
typescript: '>=4.8.4 <6.0.0'
|
591 |
+
|
592 |
+
'@typescript-eslint/[email protected]':
|
593 |
+
resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
|
594 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
595 |
+
|
596 |
+
'@vitejs/[email protected]':
|
597 |
+
resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
|
598 |
+
engines: {node: ^14.18.0 || >=16.0.0}
|
599 |
+
peerDependencies:
|
600 |
+
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
601 |
+
|
602 | |
603 |
+
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
604 |
+
peerDependencies:
|
605 |
+
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
606 |
+
|
607 | |
608 |
+
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
609 |
+
engines: {node: '>=0.4.0'}
|
610 |
+
hasBin: true
|
611 |
+
|
612 | |
613 |
+
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
614 |
+
|
615 | |
616 |
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
617 |
+
engines: {node: '>=8'}
|
618 |
+
|
619 | |
620 |
+
resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
|
621 |
+
engines: {node: '>=12'}
|
622 |
+
|
623 | |
624 |
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
625 |
+
engines: {node: '>=8'}
|
626 |
+
|
627 | |
628 |
+
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
|
629 |
+
engines: {node: '>=12'}
|
630 |
+
|
631 | |
632 |
+
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
633 |
+
|
634 | |
635 |
+
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
636 |
+
engines: {node: '>= 8'}
|
637 |
+
|
638 | |
639 |
+
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
640 |
+
|
641 | |
642 |
+
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
643 |
+
|
644 | |
645 |
+
resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
|
646 |
+
engines: {node: ^10 || ^12 || >=14}
|
647 |
+
hasBin: true
|
648 |
+
peerDependencies:
|
649 |
+
postcss: ^8.1.0
|
650 |
+
|
651 | |
652 |
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
653 |
+
|
654 | |
655 |
+
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
656 |
+
engines: {node: '>=8'}
|
657 |
+
|
658 | |
659 |
+
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
660 |
+
|
661 | |
662 |
+
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
663 |
+
|
664 | |
665 |
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
666 |
+
engines: {node: '>=8'}
|
667 |
+
|
668 | |
669 |
+
resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
|
670 |
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
671 |
+
hasBin: true
|
672 |
+
|
673 | |
674 |
+
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
675 |
+
engines: {node: '>=6'}
|
676 |
+
|
677 | |
678 |
+
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
679 |
+
engines: {node: '>= 6'}
|
680 |
+
|
681 | |
682 |
+
resolution: {integrity: sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==}
|
683 |
+
|
684 | |
685 |
+
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
686 |
+
engines: {node: '>=10'}
|
687 |
+
|
688 | |
689 |
+
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
690 |
+
engines: {node: '>= 8.10.0'}
|
691 |
+
|
692 | |
693 |
+
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
694 |
+
|
695 | |
696 |
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
697 |
+
engines: {node: '>=7.0.0'}
|
698 |
+
|
699 | |
700 |
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
701 |
+
|
702 | |
703 |
+
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
704 |
+
engines: {node: '>= 6'}
|
705 |
+
|
706 | |
707 |
+
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
708 |
+
|
709 | |
710 |
+
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
711 |
+
|
712 | |
713 |
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
714 |
+
engines: {node: '>= 8'}
|
715 |
+
|
716 | |
717 |
+
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
718 |
+
engines: {node: '>=4'}
|
719 |
+
hasBin: true
|
720 |
+
|
721 | |
722 |
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
723 |
+
|
724 | |
725 |
+
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
726 |
+
engines: {node: '>=6.0'}
|
727 |
+
peerDependencies:
|
728 |
+
supports-color: '*'
|
729 |
+
peerDependenciesMeta:
|
730 |
+
supports-color:
|
731 |
+
optional: true
|
732 |
+
|
733 | |
734 |
+
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
735 |
+
|
736 | |
737 |
+
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
738 |
+
engines: {node: '>=8'}
|
739 |
+
|
740 | |
741 |
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
742 |
+
|
743 | |
744 |
+
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
745 |
+
|
746 | |
747 |
+
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
748 |
+
|
749 | |
750 |
+
resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==}
|
751 |
+
|
752 | |
753 |
+
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
754 |
+
|
755 | |
756 |
+
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
757 |
+
|
758 | |
759 |
+
resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
|
760 |
+
engines: {node: '>=18'}
|
761 |
+
hasBin: true
|
762 |
+
|
763 | |
764 |
+
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
765 |
+
engines: {node: '>=6'}
|
766 |
+
|
767 | |
768 |
+
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
769 |
+
engines: {node: '>=10'}
|
770 |
+
|
771 | |
772 |
+
resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
|
773 |
+
engines: {node: '>=10'}
|
774 |
+
peerDependencies:
|
775 |
+
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
|
776 |
+
|
777 | |
778 |
+
resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==}
|
779 |
+
peerDependencies:
|
780 |
+
eslint: '>=8.40'
|
781 |
+
|
782 | |
783 |
+
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
|
784 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
785 |
+
|
786 | |
787 |
+
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
|
788 |
+
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
789 |
+
|
790 | |
791 |
+
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
|
792 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
793 |
+
|
794 | |
795 |
+
resolution: {integrity: sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==}
|
796 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
797 |
+
hasBin: true
|
798 |
+
peerDependencies:
|
799 |
+
jiti: '*'
|
800 |
+
peerDependenciesMeta:
|
801 |
+
jiti:
|
802 |
+
optional: true
|
803 |
+
|
804 | |
805 |
+
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
|
806 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
807 |
+
|
808 | |
809 |
+
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
|
810 |
+
engines: {node: '>=0.10'}
|
811 |
+
|
812 | |
813 |
+
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
|
814 |
+
engines: {node: '>=4.0'}
|
815 |
+
|
816 | |
817 |
+
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
|
818 |
+
engines: {node: '>=4.0'}
|
819 |
+
|
820 | |
821 |
+
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
822 |
+
engines: {node: '>=0.10.0'}
|
823 |
+
|
824 | |
825 |
+
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
826 |
+
|
827 | |
828 |
+
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
829 |
+
engines: {node: '>=8.6.0'}
|
830 |
+
|
831 | |
832 |
+
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
|
833 |
+
|
834 | |
835 |
+
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
836 |
+
|
837 | |
838 |
+
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
839 |
+
|
840 | |
841 |
+
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
842 |
+
peerDependencies:
|
843 |
+
picomatch: ^3 || ^4
|
844 |
+
peerDependenciesMeta:
|
845 |
+
picomatch:
|
846 |
+
optional: true
|
847 |
+
|
848 | |
849 |
+
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
850 |
+
engines: {node: '>=16.0.0'}
|
851 |
+
|
852 | |
853 |
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
854 |
+
engines: {node: '>=8'}
|
855 |
+
|
856 | |
857 |
+
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
858 |
+
engines: {node: '>=10'}
|
859 |
+
|
860 | |
861 |
+
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
|
862 |
+
engines: {node: '>=16'}
|
863 |
+
|
864 | |
865 |
+
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
866 |
+
|
867 | |
868 |
+
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
869 |
+
engines: {node: '>=14'}
|
870 |
+
|
871 | |
872 |
+
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
873 |
+
|
874 | |
875 |
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
876 |
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
877 |
+
os: [darwin]
|
878 |
+
|
879 | |
880 |
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
881 |
+
|
882 | |
883 |
+
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
884 |
+
engines: {node: '>=6.9.0'}
|
885 |
+
|
886 | |
887 |
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
888 |
+
engines: {node: '>= 6'}
|
889 |
+
|
890 | |
891 |
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
892 |
+
engines: {node: '>=10.13.0'}
|
893 |
+
|
894 | |
895 |
+
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
896 |
+
hasBin: true
|
897 |
+
|
898 | |
899 |
+
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
900 |
+
engines: {node: '>=18'}
|
901 |
+
|
902 | |
903 |
+
resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
|
904 |
+
engines: {node: '>=18'}
|
905 |
+
|
906 | |
907 |
+
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
908 |
+
|
909 | |
910 |
+
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
911 |
+
engines: {node: '>=8'}
|
912 |
+
|
913 | |
914 |
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
915 |
+
engines: {node: '>= 0.4'}
|
916 |
+
|
917 | |
918 |
+
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
919 |
+
engines: {node: '>= 4'}
|
920 |
+
|
921 | |
922 |
+
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
923 |
+
engines: {node: '>= 4'}
|
924 |
+
|
925 | |
926 |
+
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
927 |
+
engines: {node: '>=6'}
|
928 |
+
|
929 | |
930 |
+
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
931 |
+
engines: {node: '>=0.8.19'}
|
932 |
+
|
933 | |
934 |
+
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
935 |
+
engines: {node: '>=8'}
|
936 |
+
|
937 | |
938 |
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
939 |
+
engines: {node: '>= 0.4'}
|
940 |
+
|
941 | |
942 |
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
943 |
+
engines: {node: '>=0.10.0'}
|
944 |
+
|
945 | |
946 |
+
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
947 |
+
engines: {node: '>=8'}
|
948 |
+
|
949 | |
950 |
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
951 |
+
engines: {node: '>=0.10.0'}
|
952 |
+
|
953 | |
954 |
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
955 |
+
engines: {node: '>=0.12.0'}
|
956 |
+
|
957 | |
958 |
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
959 |
+
|
960 | |
961 |
+
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
962 |
+
|
963 | |
964 |
+
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
|
965 |
+
hasBin: true
|
966 |
+
|
967 | |
968 |
+
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
|
969 |
+
hasBin: true
|
970 |
+
|
971 | |
972 |
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
973 |
+
|
974 | |
975 |
+
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
976 |
+
hasBin: true
|
977 |
+
|
978 | |
979 |
+
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
980 |
+
engines: {node: '>=6'}
|
981 |
+
hasBin: true
|
982 |
+
|
983 | |
984 |
+
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
985 |
+
|
986 | |
987 |
+
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
988 |
+
|
989 | |
990 |
+
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
991 |
+
|
992 | |
993 |
+
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
994 |
+
engines: {node: '>=6'}
|
995 |
+
hasBin: true
|
996 |
+
|
997 | |
998 |
+
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
999 |
+
|
1000 | |
1001 |
+
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
1002 |
+
engines: {node: '>= 0.8.0'}
|
1003 |
+
|
1004 | |
1005 |
+
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
|
1006 |
+
engines: {node: '>= 12.0.0'}
|
1007 |
+
cpu: [arm64]
|
1008 |
+
os: [darwin]
|
1009 |
+
|
1010 | |
1011 |
+
resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
|
1012 |
+
engines: {node: '>= 12.0.0'}
|
1013 |
+
cpu: [x64]
|
1014 |
+
os: [darwin]
|
1015 |
+
|
1016 | |
1017 |
+
resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
|
1018 |
+
engines: {node: '>= 12.0.0'}
|
1019 |
+
cpu: [x64]
|
1020 |
+
os: [freebsd]
|
1021 |
+
|
1022 | |
1023 |
+
resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
|
1024 |
+
engines: {node: '>= 12.0.0'}
|
1025 |
+
cpu: [arm]
|
1026 |
+
os: [linux]
|
1027 |
+
|
1028 | |
1029 |
+
resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
|
1030 |
+
engines: {node: '>= 12.0.0'}
|
1031 |
+
cpu: [arm64]
|
1032 |
+
os: [linux]
|
1033 |
+
|
1034 | |
1035 |
+
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
|
1036 |
+
engines: {node: '>= 12.0.0'}
|
1037 |
+
cpu: [arm64]
|
1038 |
+
os: [linux]
|
1039 |
+
|
1040 | |
1041 |
+
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
|
1042 |
+
engines: {node: '>= 12.0.0'}
|
1043 |
+
cpu: [x64]
|
1044 |
+
os: [linux]
|
1045 |
+
|
1046 | |
1047 |
+
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
|
1048 |
+
engines: {node: '>= 12.0.0'}
|
1049 |
+
cpu: [x64]
|
1050 |
+
os: [linux]
|
1051 |
+
|
1052 | |
1053 |
+
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
|
1054 |
+
engines: {node: '>= 12.0.0'}
|
1055 |
+
cpu: [arm64]
|
1056 |
+
os: [win32]
|
1057 |
+
|
1058 | |
1059 |
+
resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
|
1060 |
+
engines: {node: '>= 12.0.0'}
|
1061 |
+
cpu: [x64]
|
1062 |
+
os: [win32]
|
1063 |
+
|
1064 | |
1065 |
+
resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
|
1066 |
+
engines: {node: '>= 12.0.0'}
|
1067 |
+
|
1068 | |
1069 |
+
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
1070 |
+
engines: {node: '>=14'}
|
1071 |
+
|
1072 | |
1073 |
+
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
1074 |
+
|
1075 | |
1076 |
+
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
1077 |
+
engines: {node: '>=10'}
|
1078 |
+
|
1079 | |
1080 |
+
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
1081 |
+
|
1082 | |
1083 |
+
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
1084 |
+
|
1085 | |
1086 |
+
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
1087 |
+
|
1088 | |
1089 |
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
1090 |
+
engines: {node: '>= 8'}
|
1091 |
+
|
1092 | |
1093 |
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
1094 |
+
engines: {node: '>=8.6'}
|
1095 |
+
|
1096 | |
1097 |
+
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
1098 |
+
|
1099 | |
1100 |
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
1101 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
1102 |
+
|
1103 | |
1104 |
+
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
1105 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
1106 |
+
|
1107 | |
1108 |
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
1109 |
+
|
1110 | |
1111 |
+
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
1112 |
+
|
1113 | |
1114 |
+
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
1115 |
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
1116 |
+
hasBin: true
|
1117 |
+
|
1118 | |
1119 |
+
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
1120 |
+
|
1121 | |
1122 |
+
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
1123 |
+
|
1124 | |
1125 |
+
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
1126 |
+
engines: {node: '>=0.10.0'}
|
1127 |
+
|
1128 | |
1129 |
+
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
|
1130 |
+
engines: {node: '>=0.10.0'}
|
1131 |
+
|
1132 | |
1133 |
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
1134 |
+
engines: {node: '>=0.10.0'}
|
1135 |
+
|
1136 | |
1137 |
+
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
1138 |
+
engines: {node: '>= 6'}
|
1139 |
+
|
1140 | |
1141 |
+
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
1142 |
+
engines: {node: '>= 0.8.0'}
|
1143 |
+
|
1144 | |
1145 |
+
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
1146 |
+
engines: {node: '>=10'}
|
1147 |
+
|
1148 | |
1149 |
+
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
1150 |
+
engines: {node: '>=10'}
|
1151 |
+
|
1152 | |
1153 |
+
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
1154 |
+
|
1155 | |
1156 |
+
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
1157 |
+
engines: {node: '>=6'}
|
1158 |
+
|
1159 | |
1160 |
+
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
1161 |
+
engines: {node: '>=8'}
|
1162 |
+
|
1163 | |
1164 |
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
1165 |
+
engines: {node: '>=8'}
|
1166 |
+
|
1167 | |
1168 |
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
1169 |
+
|
1170 | |
1171 |
+
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
1172 |
+
engines: {node: '>=16 || 14 >=14.18'}
|
1173 |
+
|
1174 | |
1175 |
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
1176 |
+
|
1177 | |
1178 |
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
1179 |
+
engines: {node: '>=8.6'}
|
1180 |
+
|
1181 | |
1182 |
+
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
1183 |
+
engines: {node: '>=12'}
|
1184 |
+
|
1185 | |
1186 |
+
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
1187 |
+
engines: {node: '>=0.10.0'}
|
1188 |
+
|
1189 | |
1190 |
+
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
1191 |
+
engines: {node: '>= 6'}
|
1192 |
+
|
1193 | |
1194 |
+
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
|
1195 |
+
engines: {node: '>=14.0.0'}
|
1196 |
+
peerDependencies:
|
1197 |
+
postcss: ^8.0.0
|
1198 |
+
|
1199 | |
1200 |
+
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
|
1201 |
+
engines: {node: ^12 || ^14 || >= 16}
|
1202 |
+
peerDependencies:
|
1203 |
+
postcss: ^8.4.21
|
1204 |
+
|
1205 | |
1206 |
+
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
|
1207 |
+
engines: {node: '>= 14'}
|
1208 |
+
peerDependencies:
|
1209 |
+
postcss: '>=8.0.9'
|
1210 |
+
ts-node: '>=9.0.0'
|
1211 |
+
peerDependenciesMeta:
|
1212 |
+
postcss:
|
1213 |
+
optional: true
|
1214 |
+
ts-node:
|
1215 |
+
optional: true
|
1216 |
+
|
1217 | |
1218 |
+
resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
|
1219 |
+
engines: {node: '>=12.0'}
|
1220 |
+
peerDependencies:
|
1221 |
+
postcss: ^8.2.14
|
1222 |
+
|
1223 | |
1224 |
+
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
|
1225 |
+
engines: {node: '>=4'}
|
1226 |
+
|
1227 | |
1228 |
+
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
1229 |
+
|
1230 | |
1231 |
+
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
1232 |
+
engines: {node: ^10 || ^12 || >=14}
|
1233 |
+
|
1234 | |
1235 |
+
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
1236 |
+
engines: {node: '>= 0.8.0'}
|
1237 |
+
|
1238 | |
1239 |
+
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
1240 |
+
engines: {node: '>=6'}
|
1241 |
+
|
1242 | |
1243 |
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
1244 |
+
|
1245 | |
1246 |
+
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
|
1247 |
+
peerDependencies:
|
1248 |
+
react: ^19.1.1
|
1249 |
+
|
1250 | |
1251 |
+
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
1252 |
+
engines: {node: '>=0.10.0'}
|
1253 |
+
|
1254 | |
1255 |
+
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
1256 |
+
engines: {node: '>=0.10.0'}
|
1257 |
+
|
1258 | |
1259 |
+
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
1260 |
+
|
1261 | |
1262 |
+
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
1263 |
+
engines: {node: '>=8.10.0'}
|
1264 |
+
|
1265 | |
1266 |
+
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
1267 |
+
engines: {node: '>=4'}
|
1268 |
+
|
1269 | |
1270 |
+
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
|
1271 |
+
engines: {node: '>= 0.4'}
|
1272 |
+
hasBin: true
|
1273 |
+
|
1274 | |
1275 |
+
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
1276 |
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
1277 |
+
|
1278 | |
1279 |
+
resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
|
1280 |
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
1281 |
+
hasBin: true
|
1282 |
+
|
1283 | |
1284 |
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
1285 |
+
|
1286 | |
1287 |
+
resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
|
1288 |
+
|
1289 | |
1290 |
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
1291 |
+
hasBin: true
|
1292 |
+
|
1293 | |
1294 |
+
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
|
1295 |
+
engines: {node: '>=10'}
|
1296 |
+
hasBin: true
|
1297 |
+
|
1298 | |
1299 |
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
1300 |
+
engines: {node: '>=8'}
|
1301 |
+
|
1302 | |
1303 |
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
1304 |
+
engines: {node: '>=8'}
|
1305 |
+
|
1306 | |
1307 |
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
1308 |
+
engines: {node: '>=14'}
|
1309 |
+
|
1310 | |
1311 |
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
1312 |
+
engines: {node: '>=0.10.0'}
|
1313 |
+
|
1314 | |
1315 |
+
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
1316 |
+
engines: {node: '>=8'}
|
1317 |
+
|
1318 | |
1319 |
+
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
1320 |
+
engines: {node: '>=12'}
|
1321 |
+
|
1322 | |
1323 |
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
1324 |
+
engines: {node: '>=8'}
|
1325 |
+
|
1326 | |
1327 |
+
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
1328 |
+
engines: {node: '>=12'}
|
1329 |
+
|
1330 | |
1331 |
+
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
1332 |
+
engines: {node: '>=8'}
|
1333 |
+
|
1334 | |
1335 |
+
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
|
1336 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
1337 |
+
hasBin: true
|
1338 |
+
|
1339 | |
1340 |
+
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
1341 |
+
engines: {node: '>=8'}
|
1342 |
+
|
1343 | |
1344 |
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
1345 |
+
engines: {node: '>= 0.4'}
|
1346 |
+
|
1347 | |
1348 |
+
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
1349 |
+
engines: {node: '>=14.0.0'}
|
1350 |
+
hasBin: true
|
1351 |
+
|
1352 | |
1353 |
+
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
1354 |
+
engines: {node: '>=0.8'}
|
1355 |
+
|
1356 | |
1357 |
+
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
1358 |
+
|
1359 | |
1360 |
+
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
1361 |
+
engines: {node: '>=12.0.0'}
|
1362 |
+
|
1363 | |
1364 |
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
1365 |
+
engines: {node: '>=8.0'}
|
1366 |
+
|
1367 | |
1368 |
+
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
|
1369 |
+
engines: {node: '>=18.12'}
|
1370 |
+
peerDependencies:
|
1371 |
+
typescript: '>=4.8.4'
|
1372 |
+
|
1373 | |
1374 |
+
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
1375 |
+
|
1376 | |
1377 |
+
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
1378 |
+
engines: {node: '>= 0.8.0'}
|
1379 |
+
|
1380 | |
1381 |
+
resolution: {integrity: sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==}
|
1382 |
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
1383 |
+
peerDependencies:
|
1384 |
+
eslint: ^8.57.0 || ^9.0.0
|
1385 |
+
typescript: '>=4.8.4 <6.0.0'
|
1386 |
+
|
1387 | |
1388 |
+
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
|
1389 |
+
engines: {node: '>=14.17'}
|
1390 |
+
hasBin: true
|
1391 |
+
|
1392 | |
1393 |
+
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
|
1394 |
+
hasBin: true
|
1395 |
+
peerDependencies:
|
1396 |
+
browserslist: '>= 4.21.0'
|
1397 |
+
|
1398 | |
1399 |
+
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
1400 |
+
|
1401 | |
1402 |
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
1403 |
+
|
1404 | |
1405 |
+
resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==}
|
1406 |
+
engines: {node: ^20.19.0 || >=22.12.0}
|
1407 |
+
hasBin: true
|
1408 |
+
peerDependencies:
|
1409 |
+
'@types/node': ^20.19.0 || >=22.12.0
|
1410 |
+
jiti: '>=1.21.0'
|
1411 |
+
less: ^4.0.0
|
1412 |
+
lightningcss: ^1.21.0
|
1413 |
+
sass: ^1.70.0
|
1414 |
+
sass-embedded: ^1.70.0
|
1415 |
+
stylus: '>=0.54.8'
|
1416 |
+
sugarss: ^5.0.0
|
1417 |
+
terser: ^5.16.0
|
1418 |
+
tsx: ^4.8.1
|
1419 |
+
yaml: ^2.4.2
|
1420 |
+
peerDependenciesMeta:
|
1421 |
+
'@types/node':
|
1422 |
+
optional: true
|
1423 |
+
jiti:
|
1424 |
+
optional: true
|
1425 |
+
less:
|
1426 |
+
optional: true
|
1427 |
+
lightningcss:
|
1428 |
+
optional: true
|
1429 |
+
sass:
|
1430 |
+
optional: true
|
1431 |
+
sass-embedded:
|
1432 |
+
optional: true
|
1433 |
+
stylus:
|
1434 |
+
optional: true
|
1435 |
+
sugarss:
|
1436 |
+
optional: true
|
1437 |
+
terser:
|
1438 |
+
optional: true
|
1439 |
+
tsx:
|
1440 |
+
optional: true
|
1441 |
+
yaml:
|
1442 |
+
optional: true
|
1443 |
+
|
1444 | |
1445 |
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
1446 |
+
engines: {node: '>= 8'}
|
1447 |
+
hasBin: true
|
1448 |
+
|
1449 | |
1450 |
+
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
1451 |
+
engines: {node: '>=0.10.0'}
|
1452 |
+
|
1453 | |
1454 |
+
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
1455 |
+
engines: {node: '>=10'}
|
1456 |
+
|
1457 | |
1458 |
+
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
1459 |
+
engines: {node: '>=12'}
|
1460 |
+
|
1461 | |
1462 |
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
1463 |
+
|
1464 | |
1465 |
+
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
|
1466 |
+
engines: {node: '>= 14.6'}
|
1467 |
+
hasBin: true
|
1468 |
+
|
1469 | |
1470 |
+
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
1471 |
+
engines: {node: '>=10'}
|
1472 |
+
|
1473 |
+
snapshots:
|
1474 |
+
|
1475 |
+
'@alloc/[email protected]': {}
|
1476 |
+
|
1477 |
+
'@ampproject/[email protected]':
|
1478 |
+
dependencies:
|
1479 |
+
'@jridgewell/gen-mapping': 0.3.12
|
1480 |
+
'@jridgewell/trace-mapping': 0.3.29
|
1481 |
+
|
1482 |
+
'@babel/[email protected]':
|
1483 |
+
dependencies:
|
1484 |
+
'@babel/helper-validator-identifier': 7.27.1
|
1485 |
+
js-tokens: 4.0.0
|
1486 |
+
picocolors: 1.1.1
|
1487 |
+
|
1488 |
+
'@babel/[email protected]': {}
|
1489 |
+
|
1490 |
+
'@babel/[email protected]':
|
1491 |
+
dependencies:
|
1492 |
+
'@ampproject/remapping': 2.3.0
|
1493 |
+
'@babel/code-frame': 7.27.1
|
1494 |
+
'@babel/generator': 7.28.0
|
1495 |
+
'@babel/helper-compilation-targets': 7.27.2
|
1496 |
+
'@babel/helper-module-transforms': 7.27.3(@babel/[email protected])
|
1497 |
+
'@babel/helpers': 7.28.2
|
1498 |
+
'@babel/parser': 7.28.0
|
1499 |
+
'@babel/template': 7.27.2
|
1500 |
+
'@babel/traverse': 7.28.0
|
1501 |
+
'@babel/types': 7.28.2
|
1502 |
+
convert-source-map: 2.0.0
|
1503 |
+
debug: 4.4.1
|
1504 |
+
gensync: 1.0.0-beta.2
|
1505 |
+
json5: 2.2.3
|
1506 |
+
semver: 6.3.1
|
1507 |
+
transitivePeerDependencies:
|
1508 |
+
- supports-color
|
1509 |
+
|
1510 |
+
'@babel/[email protected]':
|
1511 |
+
dependencies:
|
1512 |
+
'@babel/parser': 7.28.0
|
1513 |
+
'@babel/types': 7.28.2
|
1514 |
+
'@jridgewell/gen-mapping': 0.3.12
|
1515 |
+
'@jridgewell/trace-mapping': 0.3.29
|
1516 |
+
jsesc: 3.1.0
|
1517 |
+
|
1518 |
+
'@babel/[email protected]':
|
1519 |
+
dependencies:
|
1520 |
+
'@babel/compat-data': 7.28.0
|
1521 |
+
'@babel/helper-validator-option': 7.27.1
|
1522 |
+
browserslist: 4.25.1
|
1523 |
+
lru-cache: 5.1.1
|
1524 |
+
semver: 6.3.1
|
1525 |
+
|
1526 |
+
'@babel/[email protected]': {}
|
1527 |
+
|
1528 |
+
'@babel/[email protected]':
|
1529 |
+
dependencies:
|
1530 |
+
'@babel/traverse': 7.28.0
|
1531 |
+
'@babel/types': 7.28.2
|
1532 |
+
transitivePeerDependencies:
|
1533 |
+
- supports-color
|
1534 |
+
|
1535 |
+
'@babel/[email protected](@babel/[email protected])':
|
1536 |
+
dependencies:
|
1537 |
+
'@babel/core': 7.28.0
|
1538 |
+
'@babel/helper-module-imports': 7.27.1
|
1539 |
+
'@babel/helper-validator-identifier': 7.27.1
|
1540 |
+
'@babel/traverse': 7.28.0
|
1541 |
+
transitivePeerDependencies:
|
1542 |
+
- supports-color
|
1543 |
+
|
1544 |
+
'@babel/[email protected]': {}
|
1545 |
+
|
1546 |
+
'@babel/[email protected]': {}
|
1547 |
+
|
1548 |
+
'@babel/[email protected]': {}
|
1549 |
+
|
1550 |
+
'@babel/[email protected]': {}
|
1551 |
+
|
1552 |
+
'@babel/[email protected]':
|
1553 |
+
dependencies:
|
1554 |
+
'@babel/template': 7.27.2
|
1555 |
+
'@babel/types': 7.28.2
|
1556 |
+
|
1557 |
+
'@babel/[email protected]':
|
1558 |
+
dependencies:
|
1559 |
+
'@babel/types': 7.28.2
|
1560 |
+
|
1561 |
+
'@babel/[email protected](@babel/[email protected])':
|
1562 |
+
dependencies:
|
1563 |
+
'@babel/core': 7.28.0
|
1564 |
+
'@babel/helper-plugin-utils': 7.27.1
|
1565 |
+
|
1566 |
+
'@babel/[email protected](@babel/[email protected])':
|
1567 |
+
dependencies:
|
1568 |
+
'@babel/core': 7.28.0
|
1569 |
+
'@babel/helper-plugin-utils': 7.27.1
|
1570 |
+
|
1571 |
+
'@babel/[email protected]':
|
1572 |
+
dependencies:
|
1573 |
+
'@babel/code-frame': 7.27.1
|
1574 |
+
'@babel/parser': 7.28.0
|
1575 |
+
'@babel/types': 7.28.2
|
1576 |
+
|
1577 |
+
'@babel/[email protected]':
|
1578 |
+
dependencies:
|
1579 |
+
'@babel/code-frame': 7.27.1
|
1580 |
+
'@babel/generator': 7.28.0
|
1581 |
+
'@babel/helper-globals': 7.28.0
|
1582 |
+
'@babel/parser': 7.28.0
|
1583 |
+
'@babel/template': 7.27.2
|
1584 |
+
'@babel/types': 7.28.2
|
1585 |
+
debug: 4.4.1
|
1586 |
+
transitivePeerDependencies:
|
1587 |
+
- supports-color
|
1588 |
+
|
1589 |
+
'@babel/[email protected]':
|
1590 |
+
dependencies:
|
1591 |
+
'@babel/helper-string-parser': 7.27.1
|
1592 |
+
'@babel/helper-validator-identifier': 7.27.1
|
1593 |
+
|
1594 |
+
'@esbuild/[email protected]':
|
1595 |
+
optional: true
|
1596 |
+
|
1597 |
+
'@esbuild/[email protected]':
|
1598 |
+
optional: true
|
1599 |
+
|
1600 |
+
'@esbuild/[email protected]':
|
1601 |
+
optional: true
|
1602 |
+
|
1603 |
+
'@esbuild/[email protected]':
|
1604 |
+
optional: true
|
1605 |
+
|
1606 |
+
'@esbuild/[email protected]':
|
1607 |
+
optional: true
|
1608 |
+
|
1609 |
+
'@esbuild/[email protected]':
|
1610 |
+
optional: true
|
1611 |
+
|
1612 |
+
'@esbuild/[email protected]':
|
1613 |
+
optional: true
|
1614 |
+
|
1615 |
+
'@esbuild/[email protected]':
|
1616 |
+
optional: true
|
1617 |
+
|
1618 |
+
'@esbuild/[email protected]':
|
1619 |
+
optional: true
|
1620 |
+
|
1621 |
+
'@esbuild/[email protected]':
|
1622 |
+
optional: true
|
1623 |
+
|
1624 |
+
'@esbuild/[email protected]':
|
1625 |
+
optional: true
|
1626 |
+
|
1627 |
+
'@esbuild/[email protected]':
|
1628 |
+
optional: true
|
1629 |
+
|
1630 |
+
'@esbuild/[email protected]':
|
1631 |
+
optional: true
|
1632 |
+
|
1633 |
+
'@esbuild/[email protected]':
|
1634 |
+
optional: true
|
1635 |
+
|
1636 |
+
'@esbuild/[email protected]':
|
1637 |
+
optional: true
|
1638 |
+
|
1639 |
+
'@esbuild/[email protected]':
|
1640 |
+
optional: true
|
1641 |
+
|
1642 |
+
'@esbuild/[email protected]':
|
1643 |
+
optional: true
|
1644 |
+
|
1645 |
+
'@esbuild/[email protected]':
|
1646 |
+
optional: true
|
1647 |
+
|
1648 |
+
'@esbuild/[email protected]':
|
1649 |
+
optional: true
|
1650 |
+
|
1651 |
+
'@esbuild/[email protected]':
|
1652 |
+
optional: true
|
1653 |
+
|
1654 |
+
'@esbuild/[email protected]':
|
1655 |
+
optional: true
|
1656 |
+
|
1657 |
+
'@esbuild/[email protected]':
|
1658 |
+
optional: true
|
1659 |
+
|
1660 |
+
'@esbuild/[email protected]':
|
1661 |
+
optional: true
|
1662 |
+
|
1663 |
+
'@esbuild/[email protected]':
|
1664 |
+
optional: true
|
1665 |
+
|
1666 |
+
'@esbuild/[email protected]':
|
1667 |
+
optional: true
|
1668 |
+
|
1669 |
+
'@esbuild/[email protected]':
|
1670 |
+
optional: true
|
1671 |
+
|
1672 |
+
'@eslint-community/[email protected]([email protected]([email protected]))':
|
1673 |
+
dependencies:
|
1674 |
+
eslint: 9.33.0([email protected])
|
1675 |
+
eslint-visitor-keys: 3.4.3
|
1676 |
+
|
1677 |
+
'@eslint-community/[email protected]': {}
|
1678 |
+
|
1679 |
+
'@eslint/[email protected]':
|
1680 |
+
dependencies:
|
1681 |
+
'@eslint/object-schema': 2.1.6
|
1682 |
+
debug: 4.4.1
|
1683 |
+
minimatch: 3.1.2
|
1684 |
+
transitivePeerDependencies:
|
1685 |
+
- supports-color
|
1686 |
+
|
1687 |
+
'@eslint/[email protected]': {}
|
1688 |
+
|
1689 |
+
'@eslint/[email protected]':
|
1690 |
+
dependencies:
|
1691 |
+
'@types/json-schema': 7.0.15
|
1692 |
+
|
1693 |
+
'@eslint/[email protected]':
|
1694 |
+
dependencies:
|
1695 |
+
ajv: 6.12.6
|
1696 |
+
debug: 4.4.1
|
1697 |
+
espree: 10.4.0
|
1698 |
+
globals: 14.0.0
|
1699 |
+
ignore: 5.3.2
|
1700 |
+
import-fresh: 3.3.1
|
1701 |
+
js-yaml: 4.1.0
|
1702 |
+
minimatch: 3.1.2
|
1703 |
+
strip-json-comments: 3.1.1
|
1704 |
+
transitivePeerDependencies:
|
1705 |
+
- supports-color
|
1706 |
+
|
1707 |
+
'@eslint/[email protected]': {}
|
1708 |
+
|
1709 |
+
'@eslint/[email protected]': {}
|
1710 |
+
|
1711 |
+
'@eslint/[email protected]':
|
1712 |
+
dependencies:
|
1713 |
+
'@eslint/core': 0.15.2
|
1714 |
+
levn: 0.4.1
|
1715 |
+
|
1716 |
+
'@humanfs/[email protected]': {}
|
1717 |
+
|
1718 |
+
'@humanfs/[email protected]':
|
1719 |
+
dependencies:
|
1720 |
+
'@humanfs/core': 0.19.1
|
1721 |
+
'@humanwhocodes/retry': 0.3.1
|
1722 |
+
|
1723 |
+
'@humanwhocodes/[email protected]': {}
|
1724 |
+
|
1725 |
+
'@humanwhocodes/[email protected]': {}
|
1726 |
+
|
1727 |
+
'@humanwhocodes/[email protected]': {}
|
1728 |
+
|
1729 |
+
'@isaacs/[email protected]':
|
1730 |
+
dependencies:
|
1731 |
+
string-width: 5.1.2
|
1732 |
+
string-width-cjs: [email protected]
|
1733 |
+
strip-ansi: 7.1.0
|
1734 |
+
strip-ansi-cjs: [email protected]
|
1735 |
+
wrap-ansi: 8.1.0
|
1736 |
+
wrap-ansi-cjs: [email protected]
|
1737 |
+
|
1738 |
+
'@jridgewell/[email protected]':
|
1739 |
+
dependencies:
|
1740 |
+
'@jridgewell/sourcemap-codec': 1.5.4
|
1741 |
+
'@jridgewell/trace-mapping': 0.3.29
|
1742 |
+
|
1743 |
+
'@jridgewell/[email protected]': {}
|
1744 |
+
|
1745 |
+
'@jridgewell/[email protected]': {}
|
1746 |
+
|
1747 |
+
'@jridgewell/[email protected]':
|
1748 |
+
dependencies:
|
1749 |
+
'@jridgewell/resolve-uri': 3.1.2
|
1750 |
+
'@jridgewell/sourcemap-codec': 1.5.4
|
1751 |
+
|
1752 |
+
'@nodelib/[email protected]':
|
1753 |
+
dependencies:
|
1754 |
+
'@nodelib/fs.stat': 2.0.5
|
1755 |
+
run-parallel: 1.2.0
|
1756 |
+
|
1757 |
+
'@nodelib/[email protected]': {}
|
1758 |
+
|
1759 |
+
'@nodelib/[email protected]':
|
1760 |
+
dependencies:
|
1761 |
+
'@nodelib/fs.scandir': 2.1.5
|
1762 |
+
fastq: 1.19.1
|
1763 |
+
|
1764 |
+
'@pkgjs/[email protected]':
|
1765 |
+
optional: true
|
1766 |
+
|
1767 |
+
'@rolldown/[email protected]': {}
|
1768 |
+
|
1769 |
+
'@rollup/[email protected]':
|
1770 |
+
optional: true
|
1771 |
+
|
1772 |
+
'@rollup/[email protected]':
|
1773 |
+
optional: true
|
1774 |
+
|
1775 |
+
'@rollup/[email protected]':
|
1776 |
+
optional: true
|
1777 |
+
|
1778 |
+
'@rollup/[email protected]':
|
1779 |
+
optional: true
|
1780 |
+
|
1781 |
+
'@rollup/[email protected]':
|
1782 |
+
optional: true
|
1783 |
+
|
1784 |
+
'@rollup/[email protected]':
|
1785 |
+
optional: true
|
1786 |
+
|
1787 |
+
'@rollup/[email protected]':
|
1788 |
+
optional: true
|
1789 |
+
|
1790 |
+
'@rollup/[email protected]':
|
1791 |
+
optional: true
|
1792 |
+
|
1793 |
+
'@rollup/[email protected]':
|
1794 |
+
optional: true
|
1795 |
+
|
1796 |
+
'@rollup/[email protected]':
|
1797 |
+
optional: true
|
1798 |
+
|
1799 |
+
'@rollup/[email protected]':
|
1800 |
+
optional: true
|
1801 |
+
|
1802 |
+
'@rollup/[email protected]':
|
1803 |
+
optional: true
|
1804 |
+
|
1805 |
+
'@rollup/[email protected]':
|
1806 |
+
optional: true
|
1807 |
+
|
1808 |
+
'@rollup/[email protected]':
|
1809 |
+
optional: true
|
1810 |
+
|
1811 |
+
'@rollup/[email protected]':
|
1812 |
+
optional: true
|
1813 |
+
|
1814 |
+
'@rollup/[email protected]':
|
1815 |
+
optional: true
|
1816 |
+
|
1817 |
+
'@rollup/[email protected]':
|
1818 |
+
optional: true
|
1819 |
+
|
1820 |
+
'@rollup/[email protected]':
|
1821 |
+
optional: true
|
1822 |
+
|
1823 |
+
'@rollup/[email protected]':
|
1824 |
+
optional: true
|
1825 |
+
|
1826 |
+
'@rollup/[email protected]':
|
1827 |
+
optional: true
|
1828 |
+
|
1829 |
+
'@types/[email protected]':
|
1830 |
+
dependencies:
|
1831 |
+
'@babel/parser': 7.28.0
|
1832 |
+
'@babel/types': 7.28.2
|
1833 |
+
'@types/babel__generator': 7.27.0
|
1834 |
+
'@types/babel__template': 7.4.4
|
1835 |
+
'@types/babel__traverse': 7.28.0
|
1836 |
+
|
1837 |
+
'@types/[email protected]':
|
1838 |
+
dependencies:
|
1839 |
+
'@babel/types': 7.28.2
|
1840 |
+
|
1841 |
+
'@types/[email protected]':
|
1842 |
+
dependencies:
|
1843 |
+
'@babel/parser': 7.28.0
|
1844 |
+
'@babel/types': 7.28.2
|
1845 |
+
|
1846 |
+
'@types/[email protected]':
|
1847 |
+
dependencies:
|
1848 |
+
'@babel/types': 7.28.2
|
1849 |
+
|
1850 |
+
'@types/[email protected]': {}
|
1851 |
+
|
1852 |
+
'@types/[email protected]': {}
|
1853 |
+
|
1854 |
+
'@types/[email protected](@types/[email protected]([email protected]))':
|
1855 |
+
dependencies:
|
1856 |
+
'@types/react': 19.1.9([email protected])
|
1857 |
+
|
1858 |
+
'@types/[email protected]([email protected])':
|
1859 |
+
dependencies:
|
1860 |
+
csstype: 3.1.3
|
1861 |
+
react: 19.1.1
|
1862 |
+
|
1863 |
+
'@typescript-eslint/[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])':
|
1864 |
+
dependencies:
|
1865 |
+
'@eslint-community/regexpp': 4.12.1
|
1866 |
+
'@typescript-eslint/parser': 8.39.0([email protected]([email protected]))([email protected])
|
1867 |
+
'@typescript-eslint/scope-manager': 8.39.0
|
1868 |
+
'@typescript-eslint/type-utils': 8.39.0([email protected]([email protected]))([email protected])
|
1869 |
+
'@typescript-eslint/utils': 8.39.0([email protected]([email protected]))([email protected])
|
1870 |
+
'@typescript-eslint/visitor-keys': 8.39.0
|
1871 |
+
eslint: 9.33.0([email protected])
|
1872 |
+
graphemer: 1.4.0
|
1873 |
+
ignore: 7.0.5
|
1874 |
+
natural-compare: 1.4.0
|
1875 |
+
ts-api-utils: 2.1.0([email protected])
|
1876 |
+
typescript: 5.8.3
|
1877 |
+
transitivePeerDependencies:
|
1878 |
+
- supports-color
|
1879 |
+
|
1880 |
+
'@typescript-eslint/[email protected]([email protected]([email protected]))([email protected])':
|
1881 |
+
dependencies:
|
1882 |
+
'@typescript-eslint/scope-manager': 8.39.0
|
1883 |
+
'@typescript-eslint/types': 8.39.0
|
1884 |
+
'@typescript-eslint/typescript-estree': 8.39.0([email protected])
|
1885 |
+
'@typescript-eslint/visitor-keys': 8.39.0
|
1886 |
+
debug: 4.4.1
|
1887 |
+
eslint: 9.33.0([email protected])
|
1888 |
+
typescript: 5.8.3
|
1889 |
+
transitivePeerDependencies:
|
1890 |
+
- supports-color
|
1891 |
+
|
1892 |
+
'@typescript-eslint/[email protected]([email protected])':
|
1893 |
+
dependencies:
|
1894 |
+
'@typescript-eslint/tsconfig-utils': 8.39.0([email protected])
|
1895 |
+
'@typescript-eslint/types': 8.39.0
|
1896 |
+
debug: 4.4.1
|
1897 |
+
typescript: 5.8.3
|
1898 |
+
transitivePeerDependencies:
|
1899 |
+
- supports-color
|
1900 |
+
|
1901 |
+
'@typescript-eslint/[email protected]':
|
1902 |
+
dependencies:
|
1903 |
+
'@typescript-eslint/types': 8.39.0
|
1904 |
+
'@typescript-eslint/visitor-keys': 8.39.0
|
1905 |
+
|
1906 |
+
'@typescript-eslint/[email protected]([email protected])':
|
1907 |
+
dependencies:
|
1908 |
+
typescript: 5.8.3
|
1909 |
+
|
1910 |
+
'@typescript-eslint/[email protected]([email protected]([email protected]))([email protected])':
|
1911 |
+
dependencies:
|
1912 |
+
'@typescript-eslint/types': 8.39.0
|
1913 |
+
'@typescript-eslint/typescript-estree': 8.39.0([email protected])
|
1914 |
+
'@typescript-eslint/utils': 8.39.0([email protected]([email protected]))([email protected])
|
1915 |
+
debug: 4.4.1
|
1916 |
+
eslint: 9.33.0([email protected])
|
1917 |
+
ts-api-utils: 2.1.0([email protected])
|
1918 |
+
typescript: 5.8.3
|
1919 |
+
transitivePeerDependencies:
|
1920 |
+
- supports-color
|
1921 |
+
|
1922 |
+
'@typescript-eslint/[email protected]': {}
|
1923 |
+
|
1924 |
+
'@typescript-eslint/[email protected]([email protected])':
|
1925 |
+
dependencies:
|
1926 |
+
'@typescript-eslint/project-service': 8.39.0([email protected])
|
1927 |
+
'@typescript-eslint/tsconfig-utils': 8.39.0([email protected])
|
1928 |
+
'@typescript-eslint/types': 8.39.0
|
1929 |
+
'@typescript-eslint/visitor-keys': 8.39.0
|
1930 |
+
debug: 4.4.1
|
1931 |
+
fast-glob: 3.3.3
|
1932 |
+
is-glob: 4.0.3
|
1933 |
+
minimatch: 9.0.5
|
1934 |
+
semver: 7.7.2
|
1935 |
+
ts-api-utils: 2.1.0([email protected])
|
1936 |
+
typescript: 5.8.3
|
1937 |
+
transitivePeerDependencies:
|
1938 |
+
- supports-color
|
1939 |
+
|
1940 |
+
'@typescript-eslint/[email protected]([email protected]([email protected]))([email protected])':
|
1941 |
+
dependencies:
|
1942 |
+
'@eslint-community/eslint-utils': 4.7.0([email protected]([email protected]))
|
1943 |
+
'@typescript-eslint/scope-manager': 8.39.0
|
1944 |
+
'@typescript-eslint/types': 8.39.0
|
1945 |
+
'@typescript-eslint/typescript-estree': 8.39.0([email protected])
|
1946 |
+
eslint: 9.33.0([email protected])
|
1947 |
+
typescript: 5.8.3
|
1948 |
+
transitivePeerDependencies:
|
1949 |
+
- supports-color
|
1950 |
+
|
1951 |
+
'@typescript-eslint/[email protected]':
|
1952 |
+
dependencies:
|
1953 |
+
'@typescript-eslint/types': 8.39.0
|
1954 |
+
eslint-visitor-keys: 4.2.1
|
1955 |
+
|
1956 | |
1957 |
+
dependencies:
|
1958 |
+
'@babel/core': 7.28.0
|
1959 |
+
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/[email protected])
|
1960 |
+
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/[email protected])
|
1961 |
+
'@rolldown/pluginutils': 1.0.0-beta.27
|
1962 |
+
'@types/babel__core': 7.20.5
|
1963 |
+
react-refresh: 0.17.0
|
1964 |
+
vite: 7.1.1([email protected])([email protected])([email protected])
|
1965 |
+
transitivePeerDependencies:
|
1966 |
+
- supports-color
|
1967 |
+
|
1968 | |
1969 |
+
dependencies:
|
1970 |
+
acorn: 8.15.0
|
1971 |
+
|
1972 |
+
[email protected]: {}
|
1973 |
+
|
1974 | |
1975 |
+
dependencies:
|
1976 |
+
fast-deep-equal: 3.1.3
|
1977 |
+
fast-json-stable-stringify: 2.1.0
|
1978 |
+
json-schema-traverse: 0.4.1
|
1979 |
+
uri-js: 4.4.1
|
1980 |
+
|
1981 |
+
[email protected]: {}
|
1982 |
+
|
1983 |
+
[email protected]: {}
|
1984 |
+
|
1985 | |
1986 |
+
dependencies:
|
1987 |
+
color-convert: 2.0.1
|
1988 |
+
|
1989 |
+
[email protected]: {}
|
1990 |
+
|
1991 |
+
[email protected]: {}
|
1992 |
+
|
1993 | |
1994 |
+
dependencies:
|
1995 |
+
normalize-path: 3.0.0
|
1996 |
+
picomatch: 2.3.1
|
1997 |
+
|
1998 |
+
[email protected]: {}
|
1999 |
+
|
2000 |
+
[email protected]: {}
|
2001 |
+
|
2002 | |
2003 |
+
dependencies:
|
2004 |
+
browserslist: 4.25.1
|
2005 |
+
caniuse-lite: 1.0.30001733
|
2006 |
+
fraction.js: 4.3.7
|
2007 |
+
normalize-range: 0.1.2
|
2008 |
+
picocolors: 1.1.1
|
2009 |
+
postcss: 8.5.6
|
2010 |
+
postcss-value-parser: 4.2.0
|
2011 |
+
|
2012 |
+
[email protected]: {}
|
2013 |
+
|
2014 |
+
[email protected]: {}
|
2015 |
+
|
2016 | |
2017 |
+
dependencies:
|
2018 |
+
balanced-match: 1.0.2
|
2019 |
+
concat-map: 0.0.1
|
2020 |
+
|
2021 | |
2022 |
+
dependencies:
|
2023 |
+
balanced-match: 1.0.2
|
2024 |
+
|
2025 | |
2026 |
+
dependencies:
|
2027 |
+
fill-range: 7.1.1
|
2028 |
+
|
2029 | |
2030 |
+
dependencies:
|
2031 |
+
caniuse-lite: 1.0.30001733
|
2032 |
+
electron-to-chromium: 1.5.199
|
2033 |
+
node-releases: 2.0.19
|
2034 |
+
update-browserslist-db: 1.1.3([email protected])
|
2035 |
+
|
2036 |
+
[email protected]: {}
|
2037 |
+
|
2038 |
+
[email protected]: {}
|
2039 |
+
|
2040 |
+
[email protected]: {}
|
2041 |
+
|
2042 | |
2043 |
+
dependencies:
|
2044 |
+
ansi-styles: 4.3.0
|
2045 |
+
supports-color: 7.2.0
|
2046 |
+
|
2047 | |
2048 |
+
dependencies:
|
2049 |
+
anymatch: 3.1.3
|
2050 |
+
braces: 3.0.3
|
2051 |
+
glob-parent: 5.1.2
|
2052 |
+
is-binary-path: 2.1.0
|
2053 |
+
is-glob: 4.0.3
|
2054 |
+
normalize-path: 3.0.0
|
2055 |
+
readdirp: 3.6.0
|
2056 |
+
optionalDependencies:
|
2057 |
+
fsevents: 2.3.3
|
2058 |
+
|
2059 |
+
[email protected]: {}
|
2060 |
+
|
2061 | |
2062 |
+
dependencies:
|
2063 |
+
color-name: 1.1.4
|
2064 |
+
|
2065 |
+
[email protected]: {}
|
2066 |
+
|
2067 |
+
[email protected]: {}
|
2068 |
+
|
2069 |
+
[email protected]: {}
|
2070 |
+
|
2071 |
+
[email protected]: {}
|
2072 |
+
|
2073 | |
2074 |
+
dependencies:
|
2075 |
+
path-key: 3.1.1
|
2076 |
+
shebang-command: 2.0.0
|
2077 |
+
which: 2.0.2
|
2078 |
+
|
2079 |
+
[email protected]: {}
|
2080 |
+
|
2081 |
+
[email protected]: {}
|
2082 |
+
|
2083 | |
2084 |
+
dependencies:
|
2085 |
+
ms: 2.1.3
|
2086 |
+
|
2087 |
+
[email protected]: {}
|
2088 |
+
|
2089 | |
2090 |
+
optional: true
|
2091 |
+
|
2092 |
+
[email protected]: {}
|
2093 |
+
|
2094 |
+
[email protected]: {}
|
2095 |
+
|
2096 |
+
[email protected]: {}
|
2097 |
+
|
2098 |
+
[email protected]: {}
|
2099 |
+
|
2100 |
+
[email protected]: {}
|
2101 |
+
|
2102 |
+
[email protected]: {}
|
2103 |
+
|
2104 | |
2105 |
+
optionalDependencies:
|
2106 |
+
'@esbuild/aix-ppc64': 0.25.8
|
2107 |
+
'@esbuild/android-arm': 0.25.8
|
2108 |
+
'@esbuild/android-arm64': 0.25.8
|
2109 |
+
'@esbuild/android-x64': 0.25.8
|
2110 |
+
'@esbuild/darwin-arm64': 0.25.8
|
2111 |
+
'@esbuild/darwin-x64': 0.25.8
|
2112 |
+
'@esbuild/freebsd-arm64': 0.25.8
|
2113 |
+
'@esbuild/freebsd-x64': 0.25.8
|
2114 |
+
'@esbuild/linux-arm': 0.25.8
|
2115 |
+
'@esbuild/linux-arm64': 0.25.8
|
2116 |
+
'@esbuild/linux-ia32': 0.25.8
|
2117 |
+
'@esbuild/linux-loong64': 0.25.8
|
2118 |
+
'@esbuild/linux-mips64el': 0.25.8
|
2119 |
+
'@esbuild/linux-ppc64': 0.25.8
|
2120 |
+
'@esbuild/linux-riscv64': 0.25.8
|
2121 |
+
'@esbuild/linux-s390x': 0.25.8
|
2122 |
+
'@esbuild/linux-x64': 0.25.8
|
2123 |
+
'@esbuild/netbsd-arm64': 0.25.8
|
2124 |
+
'@esbuild/netbsd-x64': 0.25.8
|
2125 |
+
'@esbuild/openbsd-arm64': 0.25.8
|
2126 |
+
'@esbuild/openbsd-x64': 0.25.8
|
2127 |
+
'@esbuild/openharmony-arm64': 0.25.8
|
2128 |
+
'@esbuild/sunos-x64': 0.25.8
|
2129 |
+
'@esbuild/win32-arm64': 0.25.8
|
2130 |
+
'@esbuild/win32-ia32': 0.25.8
|
2131 |
+
'@esbuild/win32-x64': 0.25.8
|
2132 |
+
|
2133 |
+
[email protected]: {}
|
2134 |
+
|
2135 |
+
[email protected]: {}
|
2136 |
+
|
2137 | |
2138 |
+
dependencies:
|
2139 |
+
eslint: 9.33.0([email protected])
|
2140 |
+
|
2141 | |
2142 |
+
dependencies:
|
2143 |
+
eslint: 9.33.0([email protected])
|
2144 |
+
|
2145 | |
2146 |
+
dependencies:
|
2147 |
+
esrecurse: 4.3.0
|
2148 |
+
estraverse: 5.3.0
|
2149 |
+
|
2150 |
+
[email protected]: {}
|
2151 |
+
|
2152 |
+
[email protected]: {}
|
2153 |
+
|
2154 | |
2155 |
+
dependencies:
|
2156 |
+
'@eslint-community/eslint-utils': 4.7.0([email protected]([email protected]))
|
2157 |
+
'@eslint-community/regexpp': 4.12.1
|
2158 |
+
'@eslint/config-array': 0.21.0
|
2159 |
+
'@eslint/config-helpers': 0.3.1
|
2160 |
+
'@eslint/core': 0.15.2
|
2161 |
+
'@eslint/eslintrc': 3.3.1
|
2162 |
+
'@eslint/js': 9.33.0
|
2163 |
+
'@eslint/plugin-kit': 0.3.5
|
2164 |
+
'@humanfs/node': 0.16.6
|
2165 |
+
'@humanwhocodes/module-importer': 1.0.1
|
2166 |
+
'@humanwhocodes/retry': 0.4.3
|
2167 |
+
'@types/estree': 1.0.8
|
2168 |
+
'@types/json-schema': 7.0.15
|
2169 |
+
ajv: 6.12.6
|
2170 |
+
chalk: 4.1.2
|
2171 |
+
cross-spawn: 7.0.6
|
2172 |
+
debug: 4.4.1
|
2173 |
+
escape-string-regexp: 4.0.0
|
2174 |
+
eslint-scope: 8.4.0
|
2175 |
+
eslint-visitor-keys: 4.2.1
|
2176 |
+
espree: 10.4.0
|
2177 |
+
esquery: 1.6.0
|
2178 |
+
esutils: 2.0.3
|
2179 |
+
fast-deep-equal: 3.1.3
|
2180 |
+
file-entry-cache: 8.0.0
|
2181 |
+
find-up: 5.0.0
|
2182 |
+
glob-parent: 6.0.2
|
2183 |
+
ignore: 5.3.2
|
2184 |
+
imurmurhash: 0.1.4
|
2185 |
+
is-glob: 4.0.3
|
2186 |
+
json-stable-stringify-without-jsonify: 1.0.1
|
2187 |
+
lodash.merge: 4.6.2
|
2188 |
+
minimatch: 3.1.2
|
2189 |
+
natural-compare: 1.4.0
|
2190 |
+
optionator: 0.9.4
|
2191 |
+
optionalDependencies:
|
2192 |
+
jiti: 2.5.1
|
2193 |
+
transitivePeerDependencies:
|
2194 |
+
- supports-color
|
2195 |
+
|
2196 | |
2197 |
+
dependencies:
|
2198 |
+
acorn: 8.15.0
|
2199 |
+
acorn-jsx: 5.3.2([email protected])
|
2200 |
+
eslint-visitor-keys: 4.2.1
|
2201 |
+
|
2202 | |
2203 |
+
dependencies:
|
2204 |
+
estraverse: 5.3.0
|
2205 |
+
|
2206 | |
2207 |
+
dependencies:
|
2208 |
+
estraverse: 5.3.0
|
2209 |
+
|
2210 |
+
[email protected]: {}
|
2211 |
+
|
2212 |
+
[email protected]: {}
|
2213 |
+
|
2214 |
+
[email protected]: {}
|
2215 |
+
|
2216 | |
2217 |
+
dependencies:
|
2218 |
+
'@nodelib/fs.stat': 2.0.5
|
2219 |
+
'@nodelib/fs.walk': 1.2.8
|
2220 |
+
glob-parent: 5.1.2
|
2221 |
+
merge2: 1.4.1
|
2222 |
+
micromatch: 4.0.8
|
2223 |
+
|
2224 |
+
[email protected]: {}
|
2225 |
+
|
2226 |
+
[email protected]: {}
|
2227 |
+
|
2228 | |
2229 |
+
dependencies:
|
2230 |
+
reusify: 1.1.0
|
2231 |
+
|
2232 | |
2233 |
+
optionalDependencies:
|
2234 |
+
picomatch: 4.0.3
|
2235 |
+
|
2236 | |
2237 |
+
dependencies:
|
2238 |
+
flat-cache: 4.0.1
|
2239 |
+
|
2240 | |
2241 |
+
dependencies:
|
2242 |
+
to-regex-range: 5.0.1
|
2243 |
+
|
2244 | |
2245 |
+
dependencies:
|
2246 |
+
locate-path: 6.0.0
|
2247 |
+
path-exists: 4.0.0
|
2248 |
+
|
2249 | |
2250 |
+
dependencies:
|
2251 |
+
flatted: 3.3.3
|
2252 |
+
keyv: 4.5.4
|
2253 |
+
|
2254 |
+
[email protected]: {}
|
2255 |
+
|
2256 | |
2257 |
+
dependencies:
|
2258 |
+
cross-spawn: 7.0.6
|
2259 |
+
signal-exit: 4.1.0
|
2260 |
+
|
2261 |
+
[email protected]: {}
|
2262 |
+
|
2263 | |
2264 |
+
optional: true
|
2265 |
+
|
2266 |
+
[email protected]: {}
|
2267 |
+
|
2268 |
+
[email protected]: {}
|
2269 |
+
|
2270 | |
2271 |
+
dependencies:
|
2272 |
+
is-glob: 4.0.3
|
2273 |
+
|
2274 | |
2275 |
+
dependencies:
|
2276 |
+
is-glob: 4.0.3
|
2277 |
+
|
2278 | |
2279 |
+
dependencies:
|
2280 |
+
foreground-child: 3.3.1
|
2281 |
+
jackspeak: 3.4.3
|
2282 |
+
minimatch: 9.0.5
|
2283 |
+
minipass: 7.1.2
|
2284 |
+
package-json-from-dist: 1.0.1
|
2285 |
+
path-scurry: 1.11.1
|
2286 |
+
|
2287 |
+
[email protected]: {}
|
2288 |
+
|
2289 |
+
[email protected]: {}
|
2290 |
+
|
2291 |
+
[email protected]: {}
|
2292 |
+
|
2293 |
+
[email protected]: {}
|
2294 |
+
|
2295 | |
2296 |
+
dependencies:
|
2297 |
+
function-bind: 1.1.2
|
2298 |
+
|
2299 |
+
[email protected]: {}
|
2300 |
+
|
2301 |
+
[email protected]: {}
|
2302 |
+
|
2303 | |
2304 |
+
dependencies:
|
2305 |
+
parent-module: 1.0.1
|
2306 |
+
resolve-from: 4.0.0
|
2307 |
+
|
2308 |
+
[email protected]: {}
|
2309 |
+
|
2310 | |
2311 |
+
dependencies:
|
2312 |
+
binary-extensions: 2.3.0
|
2313 |
+
|
2314 | |
2315 |
+
dependencies:
|
2316 |
+
hasown: 2.0.2
|
2317 |
+
|
2318 |
+
[email protected]: {}
|
2319 |
+
|
2320 |
+
[email protected]: {}
|
2321 |
+
|
2322 | |
2323 |
+
dependencies:
|
2324 |
+
is-extglob: 2.1.1
|
2325 |
+
|
2326 |
+
[email protected]: {}
|
2327 |
+
|
2328 |
+
[email protected]: {}
|
2329 |
+
|
2330 | |
2331 |
+
dependencies:
|
2332 |
+
'@isaacs/cliui': 8.0.2
|
2333 |
+
optionalDependencies:
|
2334 |
+
'@pkgjs/parseargs': 0.11.0
|
2335 |
+
|
2336 |
+
[email protected]: {}
|
2337 |
+
|
2338 | |
2339 |
+
optional: true
|
2340 |
+
|
2341 |
+
[email protected]: {}
|
2342 |
+
|
2343 | |
2344 |
+
dependencies:
|
2345 |
+
argparse: 2.0.1
|
2346 |
+
|
2347 |
+
[email protected]: {}
|
2348 |
+
|
2349 |
+
[email protected]: {}
|
2350 |
+
|
2351 |
+
[email protected]: {}
|
2352 |
+
|
2353 |
+
[email protected]: {}
|
2354 |
+
|
2355 |
+
[email protected]: {}
|
2356 |
+
|
2357 | |
2358 |
+
dependencies:
|
2359 |
+
json-buffer: 3.0.1
|
2360 |
+
|
2361 | |
2362 |
+
dependencies:
|
2363 |
+
prelude-ls: 1.2.1
|
2364 |
+
type-check: 0.4.0
|
2365 |
+
|
2366 | |
2367 |
+
optional: true
|
2368 |
+
|
2369 | |
2370 |
+
optional: true
|
2371 |
+
|
2372 | |
2373 |
+
optional: true
|
2374 |
+
|
2375 | |
2376 |
+
optional: true
|
2377 |
+
|
2378 | |
2379 |
+
optional: true
|
2380 |
+
|
2381 | |
2382 |
+
optional: true
|
2383 |
+
|
2384 | |
2385 |
+
optional: true
|
2386 |
+
|
2387 | |
2388 |
+
optional: true
|
2389 |
+
|
2390 | |
2391 |
+
optional: true
|
2392 |
+
|
2393 | |
2394 |
+
optional: true
|
2395 |
+
|
2396 | |
2397 |
+
dependencies:
|
2398 |
+
detect-libc: 2.0.4
|
2399 |
+
optionalDependencies:
|
2400 |
+
lightningcss-darwin-arm64: 1.30.1
|
2401 |
+
lightningcss-darwin-x64: 1.30.1
|
2402 |
+
lightningcss-freebsd-x64: 1.30.1
|
2403 |
+
lightningcss-linux-arm-gnueabihf: 1.30.1
|
2404 |
+
lightningcss-linux-arm64-gnu: 1.30.1
|
2405 |
+
lightningcss-linux-arm64-musl: 1.30.1
|
2406 |
+
lightningcss-linux-x64-gnu: 1.30.1
|
2407 |
+
lightningcss-linux-x64-musl: 1.30.1
|
2408 |
+
lightningcss-win32-arm64-msvc: 1.30.1
|
2409 |
+
lightningcss-win32-x64-msvc: 1.30.1
|
2410 |
+
optional: true
|
2411 |
+
|
2412 |
+
[email protected]: {}
|
2413 |
+
|
2414 |
+
[email protected]: {}
|
2415 |
+
|
2416 | |
2417 |
+
dependencies:
|
2418 |
+
p-locate: 5.0.0
|
2419 |
+
|
2420 |
+
[email protected]: {}
|
2421 |
+
|
2422 |
+
[email protected]: {}
|
2423 |
+
|
2424 | |
2425 |
+
dependencies:
|
2426 |
+
yallist: 3.1.1
|
2427 |
+
|
2428 |
+
[email protected]: {}
|
2429 |
+
|
2430 | |
2431 |
+
dependencies:
|
2432 |
+
braces: 3.0.3
|
2433 |
+
picomatch: 2.3.1
|
2434 |
+
|
2435 | |
2436 |
+
dependencies:
|
2437 |
+
brace-expansion: 1.1.12
|
2438 |
+
|
2439 | |
2440 |
+
dependencies:
|
2441 |
+
brace-expansion: 2.0.2
|
2442 |
+
|
2443 |
+
[email protected]: {}
|
2444 |
+
|
2445 |
+
[email protected]: {}
|
2446 |
+
|
2447 | |
2448 |
+
dependencies:
|
2449 |
+
any-promise: 1.3.0
|
2450 |
+
object-assign: 4.1.1
|
2451 |
+
thenify-all: 1.6.0
|
2452 |
+
|
2453 |
+
[email protected]: {}
|
2454 |
+
|
2455 |
+
[email protected]: {}
|
2456 |
+
|
2457 |
+
[email protected]: {}
|
2458 |
+
|
2459 |
+
[email protected]: {}
|
2460 |
+
|
2461 |
+
[email protected]: {}
|
2462 |
+
|
2463 |
+
[email protected]: {}
|
2464 |
+
|
2465 |
+
[email protected]: {}
|
2466 |
+
|
2467 | |
2468 |
+
dependencies:
|
2469 |
+
deep-is: 0.1.4
|
2470 |
+
fast-levenshtein: 2.0.6
|
2471 |
+
levn: 0.4.1
|
2472 |
+
prelude-ls: 1.2.1
|
2473 |
+
type-check: 0.4.0
|
2474 |
+
word-wrap: 1.2.5
|
2475 |
+
|
2476 | |
2477 |
+
dependencies:
|
2478 |
+
yocto-queue: 0.1.0
|
2479 |
+
|
2480 | |
2481 |
+
dependencies:
|
2482 |
+
p-limit: 3.1.0
|
2483 |
+
|
2484 |
+
[email protected]: {}
|
2485 |
+
|
2486 | |
2487 |
+
dependencies:
|
2488 |
+
callsites: 3.1.0
|
2489 |
+
|
2490 |
+
[email protected]: {}
|
2491 |
+
|
2492 |
+
[email protected]: {}
|
2493 |
+
|
2494 |
+
[email protected]: {}
|
2495 |
+
|
2496 | |
2497 |
+
dependencies:
|
2498 |
+
lru-cache: 10.4.3
|
2499 |
+
minipass: 7.1.2
|
2500 |
+
|
2501 |
+
[email protected]: {}
|
2502 |
+
|
2503 |
+
[email protected]: {}
|
2504 |
+
|
2505 |
+
[email protected]: {}
|
2506 |
+
|
2507 |
+
[email protected]: {}
|
2508 |
+
|
2509 |
+
[email protected]: {}
|
2510 |
+
|
2511 | |
2512 |
+
dependencies:
|
2513 |
+
postcss: 8.5.6
|
2514 |
+
postcss-value-parser: 4.2.0
|
2515 |
+
read-cache: 1.0.0
|
2516 |
+
resolve: 1.22.10
|
2517 |
+
|
2518 | |
2519 |
+
dependencies:
|
2520 |
+
camelcase-css: 2.0.1
|
2521 |
+
postcss: 8.5.6
|
2522 |
+
|
2523 | |
2524 |
+
dependencies:
|
2525 |
+
lilconfig: 3.1.3
|
2526 |
+
yaml: 2.8.1
|
2527 |
+
optionalDependencies:
|
2528 |
+
postcss: 8.5.6
|
2529 |
+
|
2530 | |
2531 |
+
dependencies:
|
2532 |
+
postcss: 8.5.6
|
2533 |
+
postcss-selector-parser: 6.1.2
|
2534 |
+
|
2535 | |
2536 |
+
dependencies:
|
2537 |
+
cssesc: 3.0.0
|
2538 |
+
util-deprecate: 1.0.2
|
2539 |
+
|
2540 |
+
[email protected]: {}
|
2541 |
+
|
2542 | |
2543 |
+
dependencies:
|
2544 |
+
nanoid: 3.3.11
|
2545 |
+
picocolors: 1.1.1
|
2546 |
+
source-map-js: 1.2.1
|
2547 |
+
|
2548 |
+
[email protected]: {}
|
2549 |
+
|
2550 |
+
[email protected]: {}
|
2551 |
+
|
2552 |
+
[email protected]: {}
|
2553 |
+
|
2554 | |
2555 |
+
dependencies:
|
2556 |
+
react: 19.1.1
|
2557 |
+
scheduler: 0.26.0
|
2558 |
+
|
2559 |
+
[email protected]: {}
|
2560 |
+
|
2561 |
+
[email protected]: {}
|
2562 |
+
|
2563 | |
2564 |
+
dependencies:
|
2565 |
+
pify: 2.3.0
|
2566 |
+
|
2567 | |
2568 |
+
dependencies:
|
2569 |
+
picomatch: 2.3.1
|
2570 |
+
|
2571 |
+
[email protected]: {}
|
2572 |
+
|
2573 | |
2574 |
+
dependencies:
|
2575 |
+
is-core-module: 2.16.1
|
2576 |
+
path-parse: 1.0.7
|
2577 |
+
supports-preserve-symlinks-flag: 1.0.0
|
2578 |
+
|
2579 |
+
[email protected]: {}
|
2580 |
+
|
2581 | |
2582 |
+
dependencies:
|
2583 |
+
'@types/estree': 1.0.8
|
2584 |
+
optionalDependencies:
|
2585 |
+
'@rollup/rollup-android-arm-eabi': 4.46.2
|
2586 |
+
'@rollup/rollup-android-arm64': 4.46.2
|
2587 |
+
'@rollup/rollup-darwin-arm64': 4.46.2
|
2588 |
+
'@rollup/rollup-darwin-x64': 4.46.2
|
2589 |
+
'@rollup/rollup-freebsd-arm64': 4.46.2
|
2590 |
+
'@rollup/rollup-freebsd-x64': 4.46.2
|
2591 |
+
'@rollup/rollup-linux-arm-gnueabihf': 4.46.2
|
2592 |
+
'@rollup/rollup-linux-arm-musleabihf': 4.46.2
|
2593 |
+
'@rollup/rollup-linux-arm64-gnu': 4.46.2
|
2594 |
+
'@rollup/rollup-linux-arm64-musl': 4.46.2
|
2595 |
+
'@rollup/rollup-linux-loongarch64-gnu': 4.46.2
|
2596 |
+
'@rollup/rollup-linux-ppc64-gnu': 4.46.2
|
2597 |
+
'@rollup/rollup-linux-riscv64-gnu': 4.46.2
|
2598 |
+
'@rollup/rollup-linux-riscv64-musl': 4.46.2
|
2599 |
+
'@rollup/rollup-linux-s390x-gnu': 4.46.2
|
2600 |
+
'@rollup/rollup-linux-x64-gnu': 4.46.2
|
2601 |
+
'@rollup/rollup-linux-x64-musl': 4.46.2
|
2602 |
+
'@rollup/rollup-win32-arm64-msvc': 4.46.2
|
2603 |
+
'@rollup/rollup-win32-ia32-msvc': 4.46.2
|
2604 |
+
'@rollup/rollup-win32-x64-msvc': 4.46.2
|
2605 |
+
fsevents: 2.3.3
|
2606 |
+
|
2607 | |
2608 |
+
dependencies:
|
2609 |
+
queue-microtask: 1.2.3
|
2610 |
+
|
2611 |
+
[email protected]: {}
|
2612 |
+
|
2613 |
+
[email protected]: {}
|
2614 |
+
|
2615 |
+
[email protected]: {}
|
2616 |
+
|
2617 | |
2618 |
+
dependencies:
|
2619 |
+
shebang-regex: 3.0.0
|
2620 |
+
|
2621 |
+
[email protected]: {}
|
2622 |
+
|
2623 |
+
[email protected]: {}
|
2624 |
+
|
2625 |
+
[email protected]: {}
|
2626 |
+
|
2627 | |
2628 |
+
dependencies:
|
2629 |
+
emoji-regex: 8.0.0
|
2630 |
+
is-fullwidth-code-point: 3.0.0
|
2631 |
+
strip-ansi: 6.0.1
|
2632 |
+
|
2633 | |
2634 |
+
dependencies:
|
2635 |
+
eastasianwidth: 0.2.0
|
2636 |
+
emoji-regex: 9.2.2
|
2637 |
+
strip-ansi: 7.1.0
|
2638 |
+
|
2639 | |
2640 |
+
dependencies:
|
2641 |
+
ansi-regex: 5.0.1
|
2642 |
+
|
2643 | |
2644 |
+
dependencies:
|
2645 |
+
ansi-regex: 6.1.0
|
2646 |
+
|
2647 |
+
[email protected]: {}
|
2648 |
+
|
2649 | |
2650 |
+
dependencies:
|
2651 |
+
'@jridgewell/gen-mapping': 0.3.12
|
2652 |
+
commander: 4.1.1
|
2653 |
+
glob: 10.4.5
|
2654 |
+
lines-and-columns: 1.2.4
|
2655 |
+
mz: 2.7.0
|
2656 |
+
pirates: 4.0.7
|
2657 |
+
ts-interface-checker: 0.1.13
|
2658 |
+
|
2659 | |
2660 |
+
dependencies:
|
2661 |
+
has-flag: 4.0.0
|
2662 |
+
|
2663 |
+
[email protected]: {}
|
2664 |
+
|
2665 | |
2666 |
+
dependencies:
|
2667 |
+
'@alloc/quick-lru': 5.2.0
|
2668 |
+
arg: 5.0.2
|
2669 |
+
chokidar: 3.6.0
|
2670 |
+
didyoumean: 1.2.2
|
2671 |
+
dlv: 1.1.3
|
2672 |
+
fast-glob: 3.3.3
|
2673 |
+
glob-parent: 6.0.2
|
2674 |
+
is-glob: 4.0.3
|
2675 |
+
jiti: 1.21.7
|
2676 |
+
lilconfig: 3.1.3
|
2677 |
+
micromatch: 4.0.8
|
2678 |
+
normalize-path: 3.0.0
|
2679 |
+
object-hash: 3.0.0
|
2680 |
+
picocolors: 1.1.1
|
2681 |
+
postcss: 8.5.6
|
2682 |
+
postcss-import: 15.1.0([email protected])
|
2683 |
+
postcss-js: 4.0.1([email protected])
|
2684 |
+
postcss-load-config: 4.0.2([email protected])
|
2685 |
+
postcss-nested: 6.2.0([email protected])
|
2686 |
+
postcss-selector-parser: 6.1.2
|
2687 |
+
resolve: 1.22.10
|
2688 |
+
sucrase: 3.35.0
|
2689 |
+
transitivePeerDependencies:
|
2690 |
+
- ts-node
|
2691 |
+
|
2692 | |
2693 |
+
dependencies:
|
2694 |
+
thenify: 3.3.1
|
2695 |
+
|
2696 | |
2697 |
+
dependencies:
|
2698 |
+
any-promise: 1.3.0
|
2699 |
+
|
2700 | |
2701 |
+
dependencies:
|
2702 |
+
fdir: 6.4.6([email protected])
|
2703 |
+
picomatch: 4.0.3
|
2704 |
+
|
2705 | |
2706 |
+
dependencies:
|
2707 |
+
is-number: 7.0.0
|
2708 |
+
|
2709 | |
2710 |
+
dependencies:
|
2711 |
+
typescript: 5.8.3
|
2712 |
+
|
2713 |
+
[email protected]: {}
|
2714 |
+
|
2715 | |
2716 |
+
dependencies:
|
2717 |
+
prelude-ls: 1.2.1
|
2718 |
+
|
2719 | |
2720 |
+
dependencies:
|
2721 |
+
'@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
|
2722 |
+
'@typescript-eslint/parser': 8.39.0([email protected]([email protected]))([email protected])
|
2723 |
+
'@typescript-eslint/typescript-estree': 8.39.0([email protected])
|
2724 |
+
'@typescript-eslint/utils': 8.39.0([email protected]([email protected]))([email protected])
|
2725 |
+
eslint: 9.33.0([email protected])
|
2726 |
+
typescript: 5.8.3
|
2727 |
+
transitivePeerDependencies:
|
2728 |
+
- supports-color
|
2729 |
+
|
2730 |
+
[email protected]: {}
|
2731 |
+
|
2732 | |
2733 |
+
dependencies:
|
2734 |
+
browserslist: 4.25.1
|
2735 |
+
escalade: 3.2.0
|
2736 |
+
picocolors: 1.1.1
|
2737 |
+
|
2738 | |
2739 |
+
dependencies:
|
2740 |
+
punycode: 2.3.1
|
2741 |
+
|
2742 |
+
[email protected]: {}
|
2743 |
+
|
2744 | |
2745 |
+
dependencies:
|
2746 |
+
esbuild: 0.25.8
|
2747 |
+
fdir: 6.4.6([email protected])
|
2748 |
+
picomatch: 4.0.3
|
2749 |
+
postcss: 8.5.6
|
2750 |
+
rollup: 4.46.2
|
2751 |
+
tinyglobby: 0.2.14
|
2752 |
+
optionalDependencies:
|
2753 |
+
fsevents: 2.3.3
|
2754 |
+
jiti: 2.5.1
|
2755 |
+
lightningcss: 1.30.1
|
2756 |
+
yaml: 2.8.1
|
2757 |
+
|
2758 | |
2759 |
+
dependencies:
|
2760 |
+
isexe: 2.0.0
|
2761 |
+
|
2762 |
+
[email protected]: {}
|
2763 |
+
|
2764 | |
2765 |
+
dependencies:
|
2766 |
+
ansi-styles: 4.3.0
|
2767 |
+
string-width: 4.2.3
|
2768 |
+
strip-ansi: 6.0.1
|
2769 |
+
|
2770 | |
2771 |
+
dependencies:
|
2772 |
+
ansi-styles: 6.2.1
|
2773 |
+
string-width: 5.1.2
|
2774 |
+
strip-ansi: 7.1.0
|
2775 |
+
|
2776 |
+
[email protected]: {}
|
2777 |
+
|
2778 |
+
[email protected]: {}
|
2779 |
+
|
2780 |
+
[email protected]: {}
|
frontend/postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
frontend/public/logo.svg
ADDED
|
frontend/public/vite.svg
ADDED
|
frontend/src/App.css
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Main app styles - matches original Next.js design */
|
2 |
+
|
3 |
+
/* Card styling to match original */
|
4 |
+
.card {
|
5 |
+
@apply bg-white rounded-xl border border-gray-200 shadow-sm transition-shadow duration-200;
|
6 |
+
}
|
7 |
+
|
8 |
+
.card:hover {
|
9 |
+
@apply shadow-md;
|
10 |
+
}
|
11 |
+
|
12 |
+
/* Button styling to match original */
|
13 |
+
.btn {
|
14 |
+
@apply inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium shadow disabled:opacity-50 disabled:cursor-not-allowed;
|
15 |
+
background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
16 |
+
color: white;
|
17 |
+
}
|
18 |
+
|
19 |
+
/* Input styling with animated blue glow */
|
20 |
+
.input {
|
21 |
+
@apply rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2;
|
22 |
+
transition: box-shadow .2s ease;
|
23 |
+
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.5);
|
24 |
+
}
|
25 |
+
.input:focus {
|
26 |
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.35);
|
27 |
+
}
|
28 |
+
|
29 |
+
/* Badge styling for potential use */
|
30 |
+
.badge {
|
31 |
+
@apply inline-flex items-center rounded-full bg-gray-100 px-2 py-1 text-xs font-medium text-gray-700;
|
32 |
+
}
|
33 |
+
|
34 |
+
/* Custom spinner animation */
|
35 |
+
@keyframes spin {
|
36 |
+
to {
|
37 |
+
transform: rotate(360deg);
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
.animate-spin {
|
42 |
+
animation: spin 1s linear infinite;
|
43 |
+
}
|
44 |
+
|
45 |
+
/* Line clamp utility for older browsers */
|
46 |
+
.line-clamp-2 {
|
47 |
+
display: -webkit-box;
|
48 |
+
-webkit-line-clamp: 2;
|
49 |
+
-webkit-box-orient: vertical;
|
50 |
+
overflow: hidden;
|
51 |
+
}
|
52 |
+
|
53 |
+
/* Prose styling for newsletter content */
|
54 |
+
.prose {
|
55 |
+
max-width: 65ch;
|
56 |
+
}
|
57 |
+
|
58 |
+
.prose-sm {
|
59 |
+
font-size: 0.875rem;
|
60 |
+
line-height: 1.5;
|
61 |
+
}
|
62 |
+
|
63 |
+
/* Ensure the app fills the viewport */
|
64 |
+
#root {
|
65 |
+
min-height: 100vh;
|
66 |
+
}
|
frontend/src/App.tsx
ADDED
@@ -0,0 +1,594 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useMemo, useState } from 'react'
|
2 |
+
import FeedPicker from './components/FeedPicker'
|
3 |
+
import EditorModal from './components/EditorModal'
|
4 |
+
import TweetCards from './components/TweetCards'
|
5 |
+
import './App.css'
|
6 |
+
|
7 |
+
type Article = { title: string, link: string, summary?: string, published?: string, source?: string }
|
8 |
+
|
9 |
+
// Simple spinner component
|
10 |
+
function Spinner() {
|
11 |
+
return (
|
12 |
+
<div className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]" role="status">
|
13 |
+
<span className="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]">Loading...</span>
|
14 |
+
</div>
|
15 |
+
)
|
16 |
+
}
|
17 |
+
|
18 |
+
function App() {
|
19 |
+
const [sessionId] = useState(() => Math.random().toString(36).slice(2))
|
20 |
+
const [apiBase] = useState('/api')
|
21 |
+
const [sources, setSources] = useState<string[]>([])
|
22 |
+
const [articles, setArticles] = useState<Article[]>([])
|
23 |
+
const [summary, setSummary] = useState('')
|
24 |
+
const [tweets, setTweets] = useState<Array<{id: string, content: string, summary_title: string, summary_link: string, summary_source: string}>>([])
|
25 |
+
|
26 |
+
const [newsletterHtml, setNewsletterHtml] = useState('')
|
27 |
+
const [loadingHighlights, setLoadingHighlights] = useState(false)
|
28 |
+
const [loadingSummaries, setLoadingSummaries] = useState(false)
|
29 |
+
const [loadingTweets, setLoadingTweets] = useState(false)
|
30 |
+
const [loadingNewsletter, setLoadingNewsletter] = useState(false)
|
31 |
+
const [summariesMode, setSummariesMode] = useState(false)
|
32 |
+
const [pageIndex, setPageIndex] = useState(0)
|
33 |
+
const [highlights, setHighlights] = useState<Array<{ title: string, link: string, source?: string, summary: string }>>([])
|
34 |
+
|
35 |
+
const [selectedArticles, setSelectedArticles] = useState<string[]>([]) // Array of article URLs
|
36 |
+
const [isPaginated, setIsPaginated] = useState(true) // Default to paginated view
|
37 |
+
const [currentEditingTweetId, setCurrentEditingTweetId] = useState<string | null>(null)
|
38 |
+
const [tweetConversations, setTweetConversations] = useState<Record<string, Array<{role: string, content: string}>>>({})
|
39 |
+
|
40 |
+
const [pendingTweetUpdate, setPendingTweetUpdate] = useState<string | null>(null)
|
41 |
+
const hasHighlights = useMemo(() => !!summary, [summary])
|
42 |
+
|
43 |
+
const [editorOpen, setEditorOpen] = useState(false)
|
44 |
+
const [editorTitle, setEditorTitle] = useState('Editor')
|
45 |
+
const [editorText, setEditorText] = useState('')
|
46 |
+
const [editTarget, setEditTarget] = useState<'summary' | `tweet-${number}` | 'newsletter'>('summary')
|
47 |
+
|
48 |
+
async function fetchAndSummarize() {
|
49 |
+
setLoadingHighlights(true)
|
50 |
+
try {
|
51 |
+
// 1) Aggregate articles (uses selected sources or backend defaults)
|
52 |
+
const resAgg = await fetch(`${apiBase}/aggregate`, {
|
53 |
+
method: 'POST',
|
54 |
+
headers: { 'Content-Type': 'application/json' },
|
55 |
+
body: JSON.stringify({ sources })
|
56 |
+
})
|
57 |
+
const dataAgg = await resAgg.json()
|
58 |
+
|
59 |
+
// Auto-select all articles by default
|
60 |
+
const articleUrls = dataAgg.articles.map((a: any) => a.link)
|
61 |
+
|
62 |
+
// 2) Try to summarize using the freshly fetched articles
|
63 |
+
let summaryText = ''
|
64 |
+
try {
|
65 |
+
const resSum = await fetch(`${apiBase}/highlights`, {
|
66 |
+
method: 'POST',
|
67 |
+
headers: { 'Content-Type': 'application/json' },
|
68 |
+
body: JSON.stringify({ session_id: sessionId, articles: dataAgg.articles })
|
69 |
+
})
|
70 |
+
const dataSum = await resSum.json()
|
71 |
+
|
72 |
+
// Set appropriate message based on whether articles were found
|
73 |
+
if (dataAgg.articles && dataAgg.articles.length > 0) {
|
74 |
+
summaryText = dataSum.summary_markdown || 'Highlights generated successfully.'
|
75 |
+
} else {
|
76 |
+
summaryText = 'No articles found for the selected sources in the past 7 days.'
|
77 |
+
}
|
78 |
+
} catch (summaryError) {
|
79 |
+
// If summary fails (e.g., no OpenAI key), still show articles and allow progression
|
80 |
+
console.error('Summary generation failed:', summaryError)
|
81 |
+
if (dataAgg.articles && dataAgg.articles.length > 0) {
|
82 |
+
summaryText = 'Articles fetched successfully. Click "Get Summaries" to process selected articles.'
|
83 |
+
} else {
|
84 |
+
summaryText = 'No articles found for the selected sources in the past 7 days.'
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
// Update all state together after loading is complete
|
89 |
+
setLoadingHighlights(false)
|
90 |
+
setArticles(dataAgg.articles)
|
91 |
+
setSelectedArticles(articleUrls)
|
92 |
+
setSummariesMode(false)
|
93 |
+
setSummary(summaryText)
|
94 |
+
} catch (error) {
|
95 |
+
setLoadingHighlights(false)
|
96 |
+
console.error('Failed to fetch articles:', error)
|
97 |
+
setSummary('Failed to fetch articles. Please try again.')
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
async function getHighlights() {
|
102 |
+
// Validate selection limit before making API call
|
103 |
+
if (selectedArticles.length > 5) {
|
104 |
+
alert('Please select 5 or fewer articles for summarization. You currently have ' + selectedArticles.length + ' articles selected.')
|
105 |
+
return
|
106 |
+
}
|
107 |
+
|
108 |
+
setLoadingSummaries(true)
|
109 |
+
try {
|
110 |
+
// Only scrape selected articles
|
111 |
+
const selectedArticleData = articles.filter(a => selectedArticles.includes(a.link))
|
112 |
+
|
113 |
+
const res = await fetch(`${apiBase}/summaries_selected`, {
|
114 |
+
method: 'POST',
|
115 |
+
headers: { 'Content-Type': 'application/json' },
|
116 |
+
body: JSON.stringify({ articles: selectedArticleData })
|
117 |
+
})
|
118 |
+
const data = await res.json()
|
119 |
+
const items: { title: string, link: string, source?: string, summary: string }[] = data.items || []
|
120 |
+
|
121 |
+
// Batch all state updates after loading is complete
|
122 |
+
setLoadingSummaries(false)
|
123 |
+
setHighlights(items)
|
124 |
+
setSummariesMode(true)
|
125 |
+
setPageIndex(0)
|
126 |
+
} catch (error) {
|
127 |
+
setLoadingSummaries(false)
|
128 |
+
throw error
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
function resetSummaries() {
|
133 |
+
setSummariesMode(false)
|
134 |
+
setSummary('')
|
135 |
+
setArticles([])
|
136 |
+
setTweets([])
|
137 |
+
setNewsletterHtml('')
|
138 |
+
setPageIndex(0)
|
139 |
+
setHighlights([])
|
140 |
+
setSelectedArticles([])
|
141 |
+
setIsPaginated(true) // Reset to default paginated view
|
142 |
+
}
|
143 |
+
|
144 |
+
function toggleArticleSelection(articleUrl: string) {
|
145 |
+
setSelectedArticles(prev =>
|
146 |
+
prev.includes(articleUrl)
|
147 |
+
? prev.filter(url => url !== articleUrl)
|
148 |
+
: [...prev, articleUrl]
|
149 |
+
)
|
150 |
+
}
|
151 |
+
|
152 |
+
function selectAllArticles() {
|
153 |
+
setSelectedArticles(articles.map(a => a.link))
|
154 |
+
}
|
155 |
+
|
156 |
+
function deselectAllArticles() {
|
157 |
+
setSelectedArticles([])
|
158 |
+
}
|
159 |
+
|
160 |
+
async function makeTweets() {
|
161 |
+
setLoadingTweets(true)
|
162 |
+
try {
|
163 |
+
const res = await fetch(`${apiBase}/tweets`, {
|
164 |
+
method: 'POST',
|
165 |
+
headers: { 'Content-Type': 'application/json' },
|
166 |
+
body: JSON.stringify({
|
167 |
+
session_id: sessionId,
|
168 |
+
summaries: highlights // Send highlights instead of summary_markdown
|
169 |
+
})
|
170 |
+
})
|
171 |
+
const data = await res.json()
|
172 |
+
|
173 |
+
// Batch state updates after loading is complete
|
174 |
+
setLoadingTweets(false)
|
175 |
+
setTweets(data.tweets)
|
176 |
+
} catch (error) {
|
177 |
+
setLoadingTweets(false)
|
178 |
+
throw error
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
async function makeNewsletter() {
|
183 |
+
setLoadingNewsletter(true)
|
184 |
+
try {
|
185 |
+
const res = await fetch(`${apiBase}/newsletter`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId, summary_markdown: summary, articles }) })
|
186 |
+
const data = await res.json()
|
187 |
+
|
188 |
+
// Batch state updates after loading is complete
|
189 |
+
setLoadingNewsletter(false)
|
190 |
+
setNewsletterHtml(data.html)
|
191 |
+
} catch (error) {
|
192 |
+
setLoadingNewsletter(false)
|
193 |
+
throw error
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
function openEditor(target: 'summary' | `tweet-${number}` | 'newsletter') {
|
198 |
+
setEditTarget(target)
|
199 |
+
if (target === 'summary') {
|
200 |
+
setEditorTitle('Edit Summary')
|
201 |
+
setEditorText(summary)
|
202 |
+
} else if (target.startsWith('tweet-')) {
|
203 |
+
const idx = Number(target.split('-')[1])
|
204 |
+
setEditorTitle(`Edit Tweet ${idx + 1}`)
|
205 |
+
setEditorText(tweets[idx]?.content || '')
|
206 |
+
} else {
|
207 |
+
setEditorTitle('Edit Newsletter (HTML)')
|
208 |
+
setEditorText(newsletterHtml)
|
209 |
+
}
|
210 |
+
setEditorOpen(true)
|
211 |
+
}
|
212 |
+
|
213 |
+
function openTweetEditor(tweet: {id: string, content: string, summary_title: string, summary_link: string, summary_source: string}) {
|
214 |
+
// Close any existing editor first, then open the new one
|
215 |
+
if (currentEditingTweetId === tweet.id) {
|
216 |
+
setCurrentEditingTweetId(null)
|
217 |
+
setPendingTweetUpdate(null)
|
218 |
+
} else {
|
219 |
+
setCurrentEditingTweetId(tweet.id)
|
220 |
+
setPendingTweetUpdate(null)
|
221 |
+
}
|
222 |
+
}
|
223 |
+
|
224 |
+
async function sendTweetMessage(message: string) {
|
225 |
+
if (!currentEditingTweetId) return
|
226 |
+
|
227 |
+
const currentTweet = tweets.find(t => t.id === currentEditingTweetId)
|
228 |
+
if (!currentTweet) return
|
229 |
+
|
230 |
+
try {
|
231 |
+
const res = await fetch(`${apiBase}/edit_tweet`, {
|
232 |
+
method: 'POST',
|
233 |
+
headers: { 'Content-Type': 'application/json' },
|
234 |
+
body: JSON.stringify({
|
235 |
+
session_id: sessionId,
|
236 |
+
tweet_id: currentTweet.id,
|
237 |
+
current_tweet: currentTweet.content,
|
238 |
+
original_summary: highlights.find(h => h.link === currentTweet.summary_link)?.summary || '',
|
239 |
+
user_message: message,
|
240 |
+
conversation_history: tweetConversations[currentTweet.id] || []
|
241 |
+
})
|
242 |
+
})
|
243 |
+
|
244 |
+
const data = await res.json()
|
245 |
+
|
246 |
+
// Store pending update instead of immediately applying it
|
247 |
+
setPendingTweetUpdate(data.new_tweet)
|
248 |
+
|
249 |
+
// Update conversation history
|
250 |
+
setTweetConversations(prev => ({
|
251 |
+
...prev,
|
252 |
+
[currentTweet.id]: data.conversation_history
|
253 |
+
}))
|
254 |
+
|
255 |
+
return data.ai_response
|
256 |
+
} catch (error) {
|
257 |
+
console.error('Error editing tweet:', error)
|
258 |
+
return 'Sorry, I encountered an error while processing your request.'
|
259 |
+
}
|
260 |
+
}
|
261 |
+
|
262 |
+
function acceptTweetUpdate() {
|
263 |
+
if (!currentEditingTweetId || !pendingTweetUpdate) return
|
264 |
+
|
265 |
+
// Update tweet content in main list
|
266 |
+
const updatedTweets = tweets.map(tweet =>
|
267 |
+
tweet.id === currentEditingTweetId
|
268 |
+
? { ...tweet, content: pendingTweetUpdate }
|
269 |
+
: tweet
|
270 |
+
)
|
271 |
+
setTweets(updatedTweets)
|
272 |
+
setPendingTweetUpdate(null)
|
273 |
+
}
|
274 |
+
|
275 |
+
function rejectTweetUpdate() {
|
276 |
+
setPendingTweetUpdate(null)
|
277 |
+
}
|
278 |
+
|
279 |
+
function onEditorDone(newText: string) {
|
280 |
+
if (editTarget === 'summary') setSummary(newText)
|
281 |
+
else if (editTarget.startsWith('tweet-')) {
|
282 |
+
const idx = Number(editTarget.split('-')[1])
|
283 |
+
const next = tweets.slice()
|
284 |
+
if (next[idx]) {
|
285 |
+
next[idx] = { ...next[idx], content: newText }
|
286 |
+
setTweets(next)
|
287 |
+
}
|
288 |
+
} else setNewsletterHtml(newText)
|
289 |
+
}
|
290 |
+
|
291 |
+
async function download() {
|
292 |
+
const res = await fetch(`${apiBase}/download_html`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId, html: newsletterHtml }) })
|
293 |
+
const blob = await res.blob()
|
294 |
+
const url = URL.createObjectURL(blob)
|
295 |
+
const a = document.createElement('a')
|
296 |
+
a.href = url
|
297 |
+
a.download = 'ai_weekly.html'
|
298 |
+
document.body.appendChild(a)
|
299 |
+
a.click()
|
300 |
+
URL.revokeObjectURL(url)
|
301 |
+
a.remove()
|
302 |
+
}
|
303 |
+
|
304 |
+
return (
|
305 |
+
<div className="min-h-screen bg-gradient-to-b from-indigo-50 to-purple-50 text-gray-900">
|
306 |
+
<div className="mx-auto max-w-5xl px-4 py-6">
|
307 |
+
<header className="mb-6">
|
308 |
+
<div className="flex items-center gap-3">
|
309 |
+
<img src="/logo.svg" alt="AI Newsletter" className="h-10 w-10" />
|
310 |
+
<div>
|
311 |
+
<h1 className="text-xl font-semibold bg-gradient-to-r from-indigo-500 to-purple-500 bg-clip-text text-transparent">
|
312 |
+
AI Newsletter Generator
|
313 |
+
</h1>
|
314 |
+
<p className="text-xs text-gray-500">Curate, summarize, and publish—fast.</p>
|
315 |
+
</div>
|
316 |
+
</div>
|
317 |
+
</header>
|
318 |
+
<main className="space-y-4">
|
319 |
+
|
320 |
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
321 |
+
<div className="md:col-span-1">
|
322 |
+
<FeedPicker selected={sources} setSelected={setSources} />
|
323 |
+
</div>
|
324 |
+
<div className="md:col-span-2 space-y-3">
|
325 |
+
<div className="card p-4">
|
326 |
+
<div className="mb-2 flex items-center justify-between">
|
327 |
+
<h3 className="text-sm font-semibold">Weekly Highlights</h3>
|
328 |
+
<div className="flex flex-wrap items-center gap-2">
|
329 |
+
{!hasHighlights && !summariesMode && (
|
330 |
+
<button className="btn flex items-center gap-2" onClick={fetchAndSummarize} disabled={loadingHighlights}>
|
331 |
+
{loadingHighlights && <Spinner />}
|
332 |
+
Get Highlights
|
333 |
+
</button>
|
334 |
+
)}
|
335 |
+
{hasHighlights && !summariesMode && (
|
336 |
+
<>
|
337 |
+
<button className="btn" onClick={resetSummaries}>Reset</button>
|
338 |
+
<button
|
339 |
+
className="btn flex items-center gap-2"
|
340 |
+
onClick={async () => { await getHighlights(); /* switches to summariesMode */ }}
|
341 |
+
disabled={loadingSummaries || selectedArticles.length === 0}
|
342 |
+
title={selectedArticles.length === 0 ? "Please select at least one article" : `Process ${selectedArticles.length} selected articles`}
|
343 |
+
>
|
344 |
+
{loadingSummaries && <Spinner />}
|
345 |
+
Get Summaries ({selectedArticles.length})
|
346 |
+
</button>
|
347 |
+
</>
|
348 |
+
)}
|
349 |
+
{summariesMode && (
|
350 |
+
<>
|
351 |
+
<button className="btn" onClick={resetSummaries}>Reset</button>
|
352 |
+
<label className="flex items-center gap-2 text-sm">
|
353 |
+
<input
|
354 |
+
type="checkbox"
|
355 |
+
checked={isPaginated}
|
356 |
+
onChange={(e) => {
|
357 |
+
setIsPaginated(e.target.checked)
|
358 |
+
setPageIndex(0) // Reset to first page when toggling
|
359 |
+
}}
|
360 |
+
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
361 |
+
/>
|
362 |
+
Paginated View
|
363 |
+
</label>
|
364 |
+
</>
|
365 |
+
)}
|
366 |
+
</div>
|
367 |
+
</div>
|
368 |
+
{!summariesMode ? (
|
369 |
+
<>
|
370 |
+
{!hasHighlights && (
|
371 |
+
summary ? (
|
372 |
+
<div className="max-h-96 overflow-y-auto">
|
373 |
+
<pre className="whitespace-pre-wrap text-sm text-gray-900">{summary}</pre>
|
374 |
+
</div>
|
375 |
+
) : (
|
376 |
+
<div className="text-sm text-gray-500">No highlights yet.</div>
|
377 |
+
)
|
378 |
+
)}
|
379 |
+
|
380 |
+
{hasHighlights && articles.length === 0 && (
|
381 |
+
<div className="mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
|
382 |
+
<div className="text-sm text-yellow-800">
|
383 |
+
No articles found for the selected sources in the past 7 days.
|
384 |
+
</div>
|
385 |
+
</div>
|
386 |
+
)}
|
387 |
+
|
388 |
+
{articles.length > 0 && (
|
389 |
+
<div className="mt-4 border-t pt-4">
|
390 |
+
<div className="flex items-center justify-between mb-3">
|
391 |
+
<h4 className="text-sm font-medium text-gray-700">
|
392 |
+
Articles Found ({articles.length})
|
393 |
+
</h4>
|
394 |
+
<div className="flex gap-2">
|
395 |
+
<button
|
396 |
+
className="text-xs text-blue-600 hover:text-blue-800"
|
397 |
+
onClick={selectAllArticles}
|
398 |
+
>
|
399 |
+
Select All
|
400 |
+
</button>
|
401 |
+
<button
|
402 |
+
className="text-xs text-gray-600 hover:text-gray-800"
|
403 |
+
onClick={deselectAllArticles}
|
404 |
+
>
|
405 |
+
Deselect All
|
406 |
+
</button>
|
407 |
+
</div>
|
408 |
+
</div>
|
409 |
+
<div className="max-h-64 overflow-y-auto space-y-2">
|
410 |
+
{articles.map((article, i) => (
|
411 |
+
<div key={`${article.link}-${i}`} className="flex items-start gap-3 p-3 border rounded-lg hover:bg-gray-50">
|
412 |
+
<input
|
413 |
+
type="checkbox"
|
414 |
+
checked={selectedArticles.includes(article.link)}
|
415 |
+
onChange={() => toggleArticleSelection(article.link)}
|
416 |
+
className="mt-1 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
417 |
+
/>
|
418 |
+
<div className="flex-1 min-w-0">
|
419 |
+
<a
|
420 |
+
href={article.link}
|
421 |
+
target="_blank"
|
422 |
+
rel="noopener noreferrer"
|
423 |
+
className="text-sm font-medium text-blue-600 hover:text-blue-800 block truncate"
|
424 |
+
>
|
425 |
+
{article.title}
|
426 |
+
</a>
|
427 |
+
<div className="text-xs text-gray-500 mt-1">
|
428 |
+
{article.source} {article.published && `• ${article.published}`}
|
429 |
+
</div>
|
430 |
+
{article.summary && (
|
431 |
+
<div className="text-xs text-gray-600 mt-1 line-clamp-2">
|
432 |
+
{article.summary}
|
433 |
+
</div>
|
434 |
+
)}
|
435 |
+
</div>
|
436 |
+
</div>
|
437 |
+
))}
|
438 |
+
</div>
|
439 |
+
<div className="mt-3 text-xs text-gray-600">
|
440 |
+
{selectedArticles.length} of {articles.length} articles selected
|
441 |
+
</div>
|
442 |
+
</div>
|
443 |
+
)}
|
444 |
+
</>
|
445 |
+
) : (
|
446 |
+
<>
|
447 |
+
{isPaginated ? (
|
448 |
+
// Paginated view - one summary per page
|
449 |
+
<div className="space-y-4">
|
450 |
+
{highlights.length > 0 && (
|
451 |
+
<div className="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
452 |
+
<div className="flex items-start justify-between mb-2">
|
453 |
+
<a
|
454 |
+
href={highlights[pageIndex]?.link}
|
455 |
+
target="_blank"
|
456 |
+
rel="noopener noreferrer"
|
457 |
+
className="text-lg font-semibold text-blue-600 hover:text-blue-800 leading-tight"
|
458 |
+
>
|
459 |
+
{highlights[pageIndex]?.title}
|
460 |
+
</a>
|
461 |
+
</div>
|
462 |
+
<div className="text-sm text-gray-500 mb-3">
|
463 |
+
{highlights[pageIndex]?.source}
|
464 |
+
</div>
|
465 |
+
<div className="prose prose-sm max-w-none">
|
466 |
+
<div className="text-gray-700 whitespace-pre-wrap">{highlights[pageIndex]?.summary}</div>
|
467 |
+
</div>
|
468 |
+
</div>
|
469 |
+
)}
|
470 |
+
|
471 |
+
{/* Pagination controls */}
|
472 |
+
{highlights.length > 1 && (
|
473 |
+
<div className="flex items-center justify-center gap-4 mt-4">
|
474 |
+
<button
|
475 |
+
className="px-3 py-1 border rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
|
476 |
+
onClick={() => setPageIndex(Math.max(0, pageIndex - 1))}
|
477 |
+
disabled={pageIndex === 0}
|
478 |
+
>
|
479 |
+
← Previous
|
480 |
+
</button>
|
481 |
+
<span className="text-sm text-gray-600">
|
482 |
+
{pageIndex + 1} of {highlights.length}
|
483 |
+
</span>
|
484 |
+
<button
|
485 |
+
className="px-3 py-1 border rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
|
486 |
+
onClick={() => setPageIndex(Math.min(highlights.length - 1, pageIndex + 1))}
|
487 |
+
disabled={pageIndex === highlights.length - 1}
|
488 |
+
>
|
489 |
+
Next →
|
490 |
+
</button>
|
491 |
+
</div>
|
492 |
+
)}
|
493 |
+
</div>
|
494 |
+
) : (
|
495 |
+
// List view - all summaries at once (original behavior)
|
496 |
+
<div className="max-h-96 overflow-y-auto space-y-4">
|
497 |
+
{highlights.map((it, i) => (
|
498 |
+
<div key={`${it.link}-${i}`} className="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
499 |
+
<div className="flex items-start justify-between mb-2">
|
500 |
+
<a
|
501 |
+
href={it.link}
|
502 |
+
target="_blank"
|
503 |
+
rel="noopener noreferrer"
|
504 |
+
className="text-lg font-semibold text-blue-600 hover:text-blue-800 leading-tight"
|
505 |
+
>
|
506 |
+
{it.title}
|
507 |
+
</a>
|
508 |
+
</div>
|
509 |
+
<div className="text-sm text-gray-500 mb-3">
|
510 |
+
{it.source}
|
511 |
+
</div>
|
512 |
+
<div className="prose prose-sm max-w-none">
|
513 |
+
<div className="text-gray-700 whitespace-pre-wrap">{it.summary}</div>
|
514 |
+
</div>
|
515 |
+
</div>
|
516 |
+
))}
|
517 |
+
<div className="text-center text-sm text-gray-600 pt-2">
|
518 |
+
{highlights.length} summaries generated
|
519 |
+
</div>
|
520 |
+
</div>
|
521 |
+
)}
|
522 |
+
</>
|
523 |
+
)}
|
524 |
+
</div>
|
525 |
+
<div className="card p-4">
|
526 |
+
<div className="mb-2 flex items-center justify-between">
|
527 |
+
<h3 className="text-sm font-semibold">X/Tweets</h3>
|
528 |
+
<div className="flex gap-2">
|
529 |
+
<button className="btn flex items-center gap-2" onClick={makeTweets} disabled={highlights.length === 0 || loadingTweets}>
|
530 |
+
{loadingTweets && <Spinner />}
|
531 |
+
Generate X/Tweets
|
532 |
+
</button>
|
533 |
+
</div>
|
534 |
+
</div>
|
535 |
+
{tweets.length > 0 ? (
|
536 |
+
<TweetCards
|
537 |
+
tweets={tweets}
|
538 |
+
onEdit={(tweet) => openTweetEditor(tweet)}
|
539 |
+
currentEditingTweetId={currentEditingTweetId}
|
540 |
+
tweetConversations={tweetConversations}
|
541 |
+
pendingTweetUpdate={pendingTweetUpdate}
|
542 |
+
highlights={highlights}
|
543 |
+
onSendMessage={sendTweetMessage}
|
544 |
+
onAcceptUpdate={acceptTweetUpdate}
|
545 |
+
onRejectUpdate={rejectTweetUpdate}
|
546 |
+
onCloseEditor={() => {
|
547 |
+
setCurrentEditingTweetId(null)
|
548 |
+
setPendingTweetUpdate(null)
|
549 |
+
}}
|
550 |
+
/>
|
551 |
+
) : (
|
552 |
+
<div className="text-sm text-gray-500">No tweets yet.</div>
|
553 |
+
)}
|
554 |
+
</div>
|
555 |
+
<div className="card p-4">
|
556 |
+
<div className="mb-2 flex items-center justify-between">
|
557 |
+
<h3 className="text-sm font-semibold">Newsletter</h3>
|
558 |
+
<div className="flex gap-2">
|
559 |
+
<button
|
560 |
+
className="btn flex items-center gap-2"
|
561 |
+
onClick={makeNewsletter}
|
562 |
+
disabled={highlights.length === 0 || loadingNewsletter}
|
563 |
+
title={highlights.length === 0 ? "Please create summaries first by clicking 'Get Summaries'" : "Generate newsletter from summaries"}
|
564 |
+
>
|
565 |
+
{loadingNewsletter && <Spinner />}
|
566 |
+
Generate
|
567 |
+
</button>
|
568 |
+
<button className="btn" onClick={() => openEditor('newsletter')} disabled={!newsletterHtml}>Edit with AI</button>
|
569 |
+
<button className="btn" onClick={download} disabled={!newsletterHtml}>Download</button>
|
570 |
+
</div>
|
571 |
+
</div>
|
572 |
+
<div className="overflow-hidden rounded-lg border">
|
573 |
+
{newsletterHtml ? <iframe srcDoc={newsletterHtml} className="h-[500px] w-full" /> : <div className="p-4 text-sm text-gray-500">No newsletter yet.</div>}
|
574 |
+
</div>
|
575 |
+
</div>
|
576 |
+
</div>
|
577 |
+
</div>
|
578 |
+
|
579 |
+
<EditorModal
|
580 |
+
open={editorOpen}
|
581 |
+
onClose={() => setEditorOpen(false)}
|
582 |
+
onDone={onEditorDone}
|
583 |
+
initialText={editorText}
|
584 |
+
title={editorTitle}
|
585 |
+
sessionId={sessionId}
|
586 |
+
apiBase={apiBase}
|
587 |
+
/>
|
588 |
+
</main>
|
589 |
+
</div>
|
590 |
+
</div>
|
591 |
+
)
|
592 |
+
}
|
593 |
+
|
594 |
+
export default App
|
frontend/src/assets/react.svg
ADDED
|
frontend/src/components/EditorModal.tsx
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState } from 'react'
|
2 |
+
|
3 |
+
interface EditorModalProps {
|
4 |
+
open: boolean
|
5 |
+
onClose: () => void
|
6 |
+
onDone: (text: string) => void
|
7 |
+
initialText: string
|
8 |
+
title: string
|
9 |
+
sessionId: string
|
10 |
+
apiBase: string
|
11 |
+
}
|
12 |
+
|
13 |
+
export default function EditorModal({
|
14 |
+
open,
|
15 |
+
onClose,
|
16 |
+
onDone,
|
17 |
+
initialText,
|
18 |
+
title,
|
19 |
+
sessionId: _,
|
20 |
+
apiBase: __
|
21 |
+
}: EditorModalProps) {
|
22 |
+
const [text, setText] = useState(initialText)
|
23 |
+
|
24 |
+
if (!open) return null
|
25 |
+
|
26 |
+
return (
|
27 |
+
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
28 |
+
<div className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] flex flex-col">
|
29 |
+
<div className="flex items-center justify-between p-4 border-b">
|
30 |
+
<h2 className="text-lg font-semibold">{title}</h2>
|
31 |
+
<button
|
32 |
+
onClick={onClose}
|
33 |
+
className="text-gray-500 hover:text-gray-700"
|
34 |
+
>
|
35 |
+
✕
|
36 |
+
</button>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<div className="flex-1 p-4 overflow-hidden">
|
40 |
+
<textarea
|
41 |
+
value={text}
|
42 |
+
onChange={(e) => setText(e.target.value)}
|
43 |
+
className="w-full h-full resize-none border rounded p-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
44 |
+
placeholder="Enter your text here..."
|
45 |
+
/>
|
46 |
+
</div>
|
47 |
+
|
48 |
+
<div className="flex justify-end gap-2 p-4 border-t">
|
49 |
+
<button
|
50 |
+
onClick={onClose}
|
51 |
+
className="px-4 py-2 text-gray-600 border rounded hover:bg-gray-50"
|
52 |
+
>
|
53 |
+
Cancel
|
54 |
+
</button>
|
55 |
+
<button
|
56 |
+
onClick={() => {
|
57 |
+
onDone(text)
|
58 |
+
onClose()
|
59 |
+
}}
|
60 |
+
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
61 |
+
>
|
62 |
+
Save
|
63 |
+
</button>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
)
|
68 |
+
}
|
frontend/src/components/FeedPicker.tsx
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useEffect, useState } from 'react'
|
2 |
+
|
3 |
+
export default function FeedPicker({
|
4 |
+
selected,
|
5 |
+
setSelected,
|
6 |
+
apiBase = '/api',
|
7 |
+
}: {
|
8 |
+
selected: string[]
|
9 |
+
setSelected: (s: string[]) => void
|
10 |
+
apiBase?: string
|
11 |
+
}) {
|
12 |
+
const [defaults, setDefaults] = useState<Record<string, string>>({})
|
13 |
+
// const [custom, setCustom] = useState('') // Temporarily disabled
|
14 |
+
|
15 |
+
useEffect(() => {
|
16 |
+
let cancelled = false
|
17 |
+
fetch(`${apiBase}/defaults`).then(r => r.json()).then((data: Record<string, string>) => {
|
18 |
+
if (cancelled) return
|
19 |
+
setDefaults(data)
|
20 |
+
// Default select all if none selected yet
|
21 |
+
if (selected.length === 0) {
|
22 |
+
const all = Object.values(data)
|
23 |
+
setSelected(all)
|
24 |
+
}
|
25 |
+
}).catch(console.error)
|
26 |
+
return () => { cancelled = true }
|
27 |
+
}, [apiBase])
|
28 |
+
|
29 |
+
function toggle(url: string) {
|
30 |
+
setSelected(selected.includes(url) ? selected.filter(u => u !== url) : [...selected, url])
|
31 |
+
}
|
32 |
+
|
33 |
+
// function addCustom() { // Temporarily disabled
|
34 |
+
// try {
|
35 |
+
// const url = new URL(custom).toString()
|
36 |
+
// if (!selected.includes(url)) setSelected([...selected, url])
|
37 |
+
// setCustom('')
|
38 |
+
// } catch { /* ignore invalid */ }
|
39 |
+
// }
|
40 |
+
|
41 |
+
return (
|
42 |
+
<div className="card p-4">
|
43 |
+
<h3 className="mb-3 text-sm font-semibold">Sources</h3>
|
44 |
+
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
45 |
+
{Object.entries(defaults).map(([name, url]) => (
|
46 |
+
<label key={url} className="flex items-center gap-2">
|
47 |
+
<input type="checkbox" checked={selected.includes(url)} onChange={() => toggle(url)} />
|
48 |
+
<span className="text-sm">{name}</span>
|
49 |
+
</label>
|
50 |
+
))}
|
51 |
+
</div>
|
52 |
+
{/* Custom RSS URL input - temporarily disabled */}
|
53 |
+
{/*
|
54 |
+
<div className="mt-4 flex gap-2">
|
55 |
+
<input
|
56 |
+
type="text"
|
57 |
+
placeholder="Custom RSS URL"
|
58 |
+
className="input flex-1"
|
59 |
+
value={custom}
|
60 |
+
onChange={e => setCustom(e.target.value)}
|
61 |
+
/>
|
62 |
+
<button className="btn" onClick={addCustom}>Add</button>
|
63 |
+
</div>
|
64 |
+
*/}
|
65 |
+
</div>
|
66 |
+
)
|
67 |
+
}
|
frontend/src/components/TweetCards.tsx
ADDED
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
type Tweet = {
|
2 |
+
id: string
|
3 |
+
content: string
|
4 |
+
summary_title: string
|
5 |
+
summary_link: string
|
6 |
+
summary_source: string
|
7 |
+
}
|
8 |
+
|
9 |
+
type Highlight = {
|
10 |
+
title: string
|
11 |
+
link: string
|
12 |
+
source?: string
|
13 |
+
summary: string
|
14 |
+
}
|
15 |
+
|
16 |
+
export default function TweetCards({
|
17 |
+
tweets,
|
18 |
+
onEdit,
|
19 |
+
currentEditingTweetId,
|
20 |
+
tweetConversations,
|
21 |
+
pendingTweetUpdate,
|
22 |
+
highlights: _highlights,
|
23 |
+
onSendMessage,
|
24 |
+
onAcceptUpdate,
|
25 |
+
onRejectUpdate,
|
26 |
+
onCloseEditor
|
27 |
+
}: {
|
28 |
+
tweets: Tweet[],
|
29 |
+
onEdit: (tweet: Tweet) => void,
|
30 |
+
currentEditingTweetId: string | null,
|
31 |
+
tweetConversations: Record<string, Array<{role: string, content: string}>>,
|
32 |
+
pendingTweetUpdate: string | null,
|
33 |
+
highlights: Highlight[],
|
34 |
+
onSendMessage: (message: string) => Promise<string | undefined>,
|
35 |
+
onAcceptUpdate: () => void,
|
36 |
+
onRejectUpdate: () => void,
|
37 |
+
onCloseEditor: () => void
|
38 |
+
}) {
|
39 |
+
const XLogo = () => (
|
40 |
+
<svg viewBox="0 0 24 24" className="h-5 w-5 fill-current" aria-hidden="true">
|
41 |
+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
42 |
+
</svg>
|
43 |
+
)
|
44 |
+
|
45 |
+
return (
|
46 |
+
<div className="space-y-4">
|
47 |
+
{tweets.map((tweet) => (
|
48 |
+
<div key={tweet.id} className="bg-white border border-gray-200 rounded-xl p-4 hover:bg-gray-50 transition-colors">
|
49 |
+
{/* Header */}
|
50 |
+
<div className="flex items-start space-x-3">
|
51 |
+
<div className="flex-shrink-0">
|
52 |
+
<div className="w-10 h-10 bg-black rounded-full flex items-center justify-center">
|
53 |
+
<XLogo />
|
54 |
+
</div>
|
55 |
+
</div>
|
56 |
+
<div className="flex-1 min-w-0">
|
57 |
+
<div className="flex items-center space-x-2">
|
58 |
+
<div className="text-sm font-bold text-gray-900">AI Newsletter</div>
|
59 |
+
<div className="text-sm text-gray-500">@AI_Newsletter</div>
|
60 |
+
<div className="text-sm text-gray-500">·</div>
|
61 |
+
<div className="text-sm text-gray-500">now</div>
|
62 |
+
</div>
|
63 |
+
|
64 |
+
{/* Tweet Content */}
|
65 |
+
<div className="mt-2">
|
66 |
+
<div className="text-gray-900 whitespace-pre-wrap break-words">
|
67 |
+
{tweet.content}
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
{/* Source Link */}
|
72 |
+
<div className="mt-3 p-3 border border-gray-200 rounded-lg bg-gray-50">
|
73 |
+
<div className="text-xs text-gray-500 mb-1">{tweet.summary_source}</div>
|
74 |
+
<a
|
75 |
+
href={tweet.summary_link}
|
76 |
+
target="_blank"
|
77 |
+
rel="noopener noreferrer"
|
78 |
+
className="text-sm font-medium text-blue-600 hover:text-blue-800 line-clamp-2"
|
79 |
+
>
|
80 |
+
{tweet.summary_title}
|
81 |
+
</a>
|
82 |
+
</div>
|
83 |
+
|
84 |
+
{/* Actions */}
|
85 |
+
<div className="mt-4 flex items-center justify-between">
|
86 |
+
<div className="flex items-center space-x-6 text-gray-500">
|
87 |
+
<button className="flex items-center space-x-2 hover:text-blue-500 transition-colors">
|
88 |
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
89 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
90 |
+
</svg>
|
91 |
+
<span className="text-sm">0</span>
|
92 |
+
</button>
|
93 |
+
<button className="flex items-center space-x-2 hover:text-green-500 transition-colors">
|
94 |
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
95 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" />
|
96 |
+
</svg>
|
97 |
+
<span className="text-sm">0</span>
|
98 |
+
</button>
|
99 |
+
<button className="flex items-center space-x-2 hover:text-red-500 transition-colors">
|
100 |
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
101 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
102 |
+
</svg>
|
103 |
+
<span className="text-sm">0</span>
|
104 |
+
</button>
|
105 |
+
<button className="flex items-center space-x-2 hover:text-blue-500 transition-colors">
|
106 |
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
107 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z" />
|
108 |
+
</svg>
|
109 |
+
<span className="text-sm">0</span>
|
110 |
+
</button>
|
111 |
+
</div>
|
112 |
+
{currentEditingTweetId !== tweet.id && (
|
113 |
+
<button
|
114 |
+
onClick={() => onEdit(tweet)}
|
115 |
+
className="px-3 py-1 text-sm bg-blue-500 text-white rounded-full hover:bg-blue-600 transition-colors"
|
116 |
+
>
|
117 |
+
Edit with AI
|
118 |
+
</button>
|
119 |
+
)}
|
120 |
+
</div>
|
121 |
+
</div>
|
122 |
+
</div>
|
123 |
+
|
124 |
+
{/* Inline Chatbot - Show only for the currently editing tweet */}
|
125 |
+
{currentEditingTweetId === tweet.id && (
|
126 |
+
<div className="mt-4 border-t border-gray-200 pt-4">
|
127 |
+
<div className="bg-gray-50 rounded-lg p-4">
|
128 |
+
<div className="flex items-center justify-between mb-3">
|
129 |
+
<h4 className="text-sm font-medium text-gray-700">Edit Tweet with AI</h4>
|
130 |
+
<button
|
131 |
+
onClick={onCloseEditor}
|
132 |
+
className="text-gray-500 hover:text-gray-700"
|
133 |
+
>
|
134 |
+
✕
|
135 |
+
</button>
|
136 |
+
</div>
|
137 |
+
|
138 |
+
{/* Current Tweet Preview */}
|
139 |
+
<div className="mb-4 p-3 bg-white rounded border">
|
140 |
+
<div className="text-xs font-medium text-gray-600 mb-1">Current Tweet:</div>
|
141 |
+
<div className="text-sm text-gray-900">{tweet.content}</div>
|
142 |
+
<div className="text-xs text-gray-500 mt-1">
|
143 |
+
About: {tweet.summary_title}
|
144 |
+
</div>
|
145 |
+
</div>
|
146 |
+
|
147 |
+
{/* Conversation History */}
|
148 |
+
<div className="max-h-64 overflow-y-auto mb-4">
|
149 |
+
<div className="space-y-2">
|
150 |
+
{(tweetConversations[tweet.id] || []).map((msg, i) => {
|
151 |
+
const isLastMessage = i === (tweetConversations[tweet.id] || []).length - 1
|
152 |
+
const isAIMessage = msg.role === 'assistant'
|
153 |
+
const hasUpdateInMessage = pendingTweetUpdate && isLastMessage && isAIMessage
|
154 |
+
|
155 |
+
return (
|
156 |
+
<div key={i}>
|
157 |
+
<div className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
158 |
+
<div className={`max-w-[80%] p-2 rounded text-sm ${
|
159 |
+
msg.role === 'user'
|
160 |
+
? 'bg-blue-500 text-white'
|
161 |
+
: 'bg-white border text-gray-900'
|
162 |
+
}`}>
|
163 |
+
{msg.content}
|
164 |
+
</div>
|
165 |
+
</div>
|
166 |
+
|
167 |
+
{/* Show accept/reject buttons after the last AI message with an update */}
|
168 |
+
{hasUpdateInMessage && (
|
169 |
+
<div className="mt-2 ml-auto max-w-[80%]">
|
170 |
+
<div className="p-3 bg-blue-50 border border-blue-200 rounded">
|
171 |
+
<div className="flex items-center justify-between mb-1">
|
172 |
+
<div className="text-xs font-medium text-blue-700">Suggested Tweet:</div>
|
173 |
+
<div className={`text-xs font-mono ${
|
174 |
+
pendingTweetUpdate.length > 280 ? 'text-red-600' :
|
175 |
+
pendingTweetUpdate.length > 260 ? 'text-yellow-600' : 'text-green-600'
|
176 |
+
}`}>
|
177 |
+
{pendingTweetUpdate.length}/280
|
178 |
+
</div>
|
179 |
+
</div>
|
180 |
+
<div className="text-sm text-gray-900 mb-2 p-2 bg-white rounded border">
|
181 |
+
{pendingTweetUpdate}
|
182 |
+
</div>
|
183 |
+
<div className="flex gap-2">
|
184 |
+
<button
|
185 |
+
onClick={onAcceptUpdate}
|
186 |
+
className="px-2 py-1 bg-green-500 text-white text-xs rounded hover:bg-green-600 transition-colors"
|
187 |
+
disabled={pendingTweetUpdate.length > 280}
|
188 |
+
>
|
189 |
+
✓ Accept
|
190 |
+
</button>
|
191 |
+
<button
|
192 |
+
onClick={onRejectUpdate}
|
193 |
+
className="px-2 py-1 bg-red-500 text-white text-xs rounded hover:bg-red-600 transition-colors"
|
194 |
+
>
|
195 |
+
✗ Reject
|
196 |
+
</button>
|
197 |
+
</div>
|
198 |
+
{pendingTweetUpdate.length > 280 && (
|
199 |
+
<div className="mt-1 text-xs text-red-600">
|
200 |
+
Tweet is too long! Ask the AI to shorten it.
|
201 |
+
</div>
|
202 |
+
)}
|
203 |
+
</div>
|
204 |
+
</div>
|
205 |
+
)}
|
206 |
+
</div>
|
207 |
+
)
|
208 |
+
})}
|
209 |
+
</div>
|
210 |
+
</div>
|
211 |
+
|
212 |
+
{/* Message Input */}
|
213 |
+
<form onSubmit={async (e) => {
|
214 |
+
e.preventDefault()
|
215 |
+
const formData = new FormData(e.target as HTMLFormElement)
|
216 |
+
const message = formData.get('message') as string
|
217 |
+
if (!message.trim()) return
|
218 |
+
|
219 |
+
// Clear input
|
220 |
+
const form = e.target as HTMLFormElement
|
221 |
+
form.reset()
|
222 |
+
|
223 |
+
// Send message and get AI response
|
224 |
+
await onSendMessage(message)
|
225 |
+
}}>
|
226 |
+
<div className="flex gap-2">
|
227 |
+
<input
|
228 |
+
name="message"
|
229 |
+
type="text"
|
230 |
+
placeholder="Tell me how to improve this tweet..."
|
231 |
+
className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
232 |
+
/>
|
233 |
+
<button
|
234 |
+
type="submit"
|
235 |
+
className="px-3 py-2 bg-blue-500 text-white text-sm rounded hover:bg-blue-600 transition-colors"
|
236 |
+
>
|
237 |
+
Send
|
238 |
+
</button>
|
239 |
+
</div>
|
240 |
+
</form>
|
241 |
+
</div>
|
242 |
+
</div>
|
243 |
+
)}
|
244 |
+
</div>
|
245 |
+
))}
|
246 |
+
</div>
|
247 |
+
)
|
248 |
+
}
|
frontend/src/index.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
frontend/src/main.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { StrictMode } from 'react'
|
2 |
+
import { createRoot } from 'react-dom/client'
|
3 |
+
import './index.css'
|
4 |
+
import App from './App.tsx'
|
5 |
+
|
6 |
+
createRoot(document.getElementById('root')!).render(
|
7 |
+
<StrictMode>
|
8 |
+
<App />
|
9 |
+
</StrictMode>,
|
10 |
+
)
|
frontend/src/vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
frontend/tailwind.config.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: [
|
4 |
+
"./index.html",
|
5 |
+
"./src/**/*.{js,ts,jsx,tsx}",
|
6 |
+
],
|
7 |
+
theme: {
|
8 |
+
extend: {},
|
9 |
+
},
|
10 |
+
plugins: [],
|
11 |
+
}
|
frontend/tsconfig.app.json
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
4 |
+
"target": "ES2022",
|
5 |
+
"useDefineForClassFields": true,
|
6 |
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
7 |
+
"module": "ESNext",
|
8 |
+
"skipLibCheck": true,
|
9 |
+
|
10 |
+
/* Bundler mode */
|
11 |
+
"moduleResolution": "bundler",
|
12 |
+
"allowImportingTsExtensions": true,
|
13 |
+
"verbatimModuleSyntax": true,
|
14 |
+
"moduleDetection": "force",
|
15 |
+
"noEmit": true,
|
16 |
+
"jsx": "react-jsx",
|
17 |
+
|
18 |
+
/* Linting */
|
19 |
+
"strict": true,
|
20 |
+
"noUnusedLocals": true,
|
21 |
+
"noUnusedParameters": true,
|
22 |
+
"erasableSyntaxOnly": true,
|
23 |
+
"noFallthroughCasesInSwitch": true,
|
24 |
+
"noUncheckedSideEffectImports": true
|
25 |
+
},
|
26 |
+
"include": ["src"]
|
27 |
+
}
|
frontend/tsconfig.json
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"files": [],
|
3 |
+
"references": [
|
4 |
+
{ "path": "./tsconfig.app.json" },
|
5 |
+
{ "path": "./tsconfig.node.json" }
|
6 |
+
]
|
7 |
+
}
|
frontend/tsconfig.node.json
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
4 |
+
"target": "ES2023",
|
5 |
+
"lib": ["ES2023"],
|
6 |
+
"module": "ESNext",
|
7 |
+
"skipLibCheck": true,
|
8 |
+
|
9 |
+
/* Bundler mode */
|
10 |
+
"moduleResolution": "bundler",
|
11 |
+
"allowImportingTsExtensions": true,
|
12 |
+
"verbatimModuleSyntax": true,
|
13 |
+
"moduleDetection": "force",
|
14 |
+
"noEmit": true,
|
15 |
+
|
16 |
+
/* Linting */
|
17 |
+
"strict": true,
|
18 |
+
"noUnusedLocals": true,
|
19 |
+
"noUnusedParameters": true,
|
20 |
+
"erasableSyntaxOnly": true,
|
21 |
+
"noFallthroughCasesInSwitch": true,
|
22 |
+
"noUncheckedSideEffectImports": true
|
23 |
+
},
|
24 |
+
"include": ["vite.config.ts"]
|
25 |
+
}
|
frontend/vite.config.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react'
|
3 |
+
|
4 |
+
// https://vite.dev/config/
|
5 |
+
export default defineConfig({
|
6 |
+
plugins: [react()],
|
7 |
+
})
|
pyproject.toml
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "ai-newsletter"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "AI Newsletter Generator - Full Stack Application"
|
5 |
+
requires-python = ">=3.12"
|
6 |
+
dependencies = [
|
7 |
+
"fastapi>=0.116.0",
|
8 |
+
"uvicorn>=0.35.0",
|
9 |
+
"openai>=1.99.0",
|
10 |
+
"feedparser>=6.0.11",
|
11 |
+
"pydantic>=2.11.0",
|
12 |
+
"httpx>=0.28.0",
|
13 |
+
"python-dateutil>=2.9.0",
|
14 |
+
"python-dotenv>=1.1.0",
|
15 |
+
]
|
16 |
+
|
17 |
+
[tool.uv]
|
18 |
+
dev-dependencies = []
|
19 |
+
|
20 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This file was autogenerated by uv via the following command:
|
2 |
+
# uv export --format requirements-txt --no-hashes
|
3 |
+
annotated-types==0.7.0
|
4 |
+
# via pydantic
|
5 |
+
anyio==4.10.0
|
6 |
+
# via
|
7 |
+
# httpx
|
8 |
+
# openai
|
9 |
+
# starlette
|
10 |
+
certifi==2025.8.3
|
11 |
+
# via
|
12 |
+
# httpcore
|
13 |
+
# httpx
|
14 |
+
click==8.2.1
|
15 |
+
# via uvicorn
|
16 |
+
colorama==0.4.6 ; sys_platform == 'win32'
|
17 |
+
# via
|
18 |
+
# click
|
19 |
+
# tqdm
|
20 |
+
distro==1.9.0
|
21 |
+
# via openai
|
22 |
+
fastapi==0.116.1
|
23 |
+
# via ai-newsletter
|
24 |
+
feedparser==6.0.11
|
25 |
+
# via ai-newsletter
|
26 |
+
h11==0.16.0
|
27 |
+
# via
|
28 |
+
# httpcore
|
29 |
+
# uvicorn
|
30 |
+
httpcore==1.0.9
|
31 |
+
# via httpx
|
32 |
+
httpx==0.28.1
|
33 |
+
# via
|
34 |
+
# ai-newsletter
|
35 |
+
# openai
|
36 |
+
idna==3.10
|
37 |
+
# via
|
38 |
+
# anyio
|
39 |
+
# httpx
|
40 |
+
jiter==0.10.0
|
41 |
+
# via openai
|
42 |
+
openai==1.99.5
|
43 |
+
# via ai-newsletter
|
44 |
+
pydantic==2.11.7
|
45 |
+
# via
|
46 |
+
# ai-newsletter
|
47 |
+
# fastapi
|
48 |
+
# openai
|
49 |
+
pydantic-core==2.33.2
|
50 |
+
# via pydantic
|
51 |
+
python-dateutil==2.9.0.post0
|
52 |
+
# via ai-newsletter
|
53 |
+
python-dotenv==1.1.1
|
54 |
+
# via ai-newsletter
|
55 |
+
sgmllib3k==1.0.0
|
56 |
+
# via feedparser
|
57 |
+
six==1.17.0
|
58 |
+
# via python-dateutil
|
59 |
+
sniffio==1.3.1
|
60 |
+
# via
|
61 |
+
# anyio
|
62 |
+
# openai
|
63 |
+
starlette==0.47.2
|
64 |
+
# via fastapi
|
65 |
+
tqdm==4.67.1
|
66 |
+
# via openai
|
67 |
+
typing-extensions==4.14.1
|
68 |
+
# via
|
69 |
+
# anyio
|
70 |
+
# fastapi
|
71 |
+
# openai
|
72 |
+
# pydantic
|
73 |
+
# pydantic-core
|
74 |
+
# starlette
|
75 |
+
# typing-inspection
|
76 |
+
typing-inspection==0.4.1
|
77 |
+
# via pydantic
|
78 |
+
uvicorn==0.35.0
|
79 |
+
# via ai-newsletter
|
uv.lock
ADDED
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version = 1
|
2 |
+
revision = 1
|
3 |
+
requires-python = ">=3.12"
|
4 |
+
resolution-markers = [
|
5 |
+
"python_full_version >= '3.13'",
|
6 |
+
"python_full_version < '3.13'",
|
7 |
+
]
|
8 |
+
|
9 |
+
[[package]]
|
10 |
+
name = "ai-newsletter"
|
11 |
+
version = "0.1.0"
|
12 |
+
source = { virtual = "." }
|
13 |
+
dependencies = [
|
14 |
+
{ name = "fastapi" },
|
15 |
+
{ name = "feedparser" },
|
16 |
+
{ name = "httpx" },
|
17 |
+
{ name = "openai" },
|
18 |
+
{ name = "pydantic" },
|
19 |
+
{ name = "python-dateutil" },
|
20 |
+
{ name = "python-dotenv" },
|
21 |
+
{ name = "uvicorn" },
|
22 |
+
]
|
23 |
+
|
24 |
+
[package.metadata]
|
25 |
+
requires-dist = [
|
26 |
+
{ name = "fastapi", specifier = ">=0.116.0" },
|
27 |
+
{ name = "feedparser", specifier = ">=6.0.11" },
|
28 |
+
{ name = "httpx", specifier = ">=0.28.0" },
|
29 |
+
{ name = "openai", specifier = ">=1.99.0" },
|
30 |
+
{ name = "pydantic", specifier = ">=2.11.0" },
|
31 |
+
{ name = "python-dateutil", specifier = ">=2.9.0" },
|
32 |
+
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
33 |
+
{ name = "uvicorn", specifier = ">=0.35.0" },
|
34 |
+
]
|
35 |
+
|
36 |
+
[package.metadata.requires-dev]
|
37 |
+
dev = []
|
38 |
+
|
39 |
+
[[package]]
|
40 |
+
name = "annotated-types"
|
41 |
+
version = "0.7.0"
|
42 |
+
source = { registry = "https://pypi.org/simple" }
|
43 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
|
44 |
+
wheels = [
|
45 |
+
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
46 |
+
]
|
47 |
+
|
48 |
+
[[package]]
|
49 |
+
name = "anyio"
|
50 |
+
version = "4.10.0"
|
51 |
+
source = { registry = "https://pypi.org/simple" }
|
52 |
+
dependencies = [
|
53 |
+
{ name = "idna" },
|
54 |
+
{ name = "sniffio" },
|
55 |
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
56 |
+
]
|
57 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252 }
|
58 |
+
wheels = [
|
59 |
+
{ url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213 },
|
60 |
+
]
|
61 |
+
|
62 |
+
[[package]]
|
63 |
+
name = "certifi"
|
64 |
+
version = "2025.8.3"
|
65 |
+
source = { registry = "https://pypi.org/simple" }
|
66 |
+
sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386 }
|
67 |
+
wheels = [
|
68 |
+
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216 },
|
69 |
+
]
|
70 |
+
|
71 |
+
[[package]]
|
72 |
+
name = "click"
|
73 |
+
version = "8.2.1"
|
74 |
+
source = { registry = "https://pypi.org/simple" }
|
75 |
+
dependencies = [
|
76 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
77 |
+
]
|
78 |
+
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 }
|
79 |
+
wheels = [
|
80 |
+
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 },
|
81 |
+
]
|
82 |
+
|
83 |
+
[[package]]
|
84 |
+
name = "colorama"
|
85 |
+
version = "0.4.6"
|
86 |
+
source = { registry = "https://pypi.org/simple" }
|
87 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
88 |
+
wheels = [
|
89 |
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
90 |
+
]
|
91 |
+
|
92 |
+
[[package]]
|
93 |
+
name = "distro"
|
94 |
+
version = "1.9.0"
|
95 |
+
source = { registry = "https://pypi.org/simple" }
|
96 |
+
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 }
|
97 |
+
wheels = [
|
98 |
+
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
|
99 |
+
]
|
100 |
+
|
101 |
+
[[package]]
|
102 |
+
name = "fastapi"
|
103 |
+
version = "0.116.1"
|
104 |
+
source = { registry = "https://pypi.org/simple" }
|
105 |
+
dependencies = [
|
106 |
+
{ name = "pydantic" },
|
107 |
+
{ name = "starlette" },
|
108 |
+
{ name = "typing-extensions" },
|
109 |
+
]
|
110 |
+
sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485 }
|
111 |
+
wheels = [
|
112 |
+
{ url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631 },
|
113 |
+
]
|
114 |
+
|
115 |
+
[[package]]
|
116 |
+
name = "feedparser"
|
117 |
+
version = "6.0.11"
|
118 |
+
source = { registry = "https://pypi.org/simple" }
|
119 |
+
dependencies = [
|
120 |
+
{ name = "sgmllib3k" },
|
121 |
+
]
|
122 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197 }
|
123 |
+
wheels = [
|
124 |
+
{ url = "https://files.pythonhosted.org/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343 },
|
125 |
+
]
|
126 |
+
|
127 |
+
[[package]]
|
128 |
+
name = "h11"
|
129 |
+
version = "0.16.0"
|
130 |
+
source = { registry = "https://pypi.org/simple" }
|
131 |
+
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 }
|
132 |
+
wheels = [
|
133 |
+
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
|
134 |
+
]
|
135 |
+
|
136 |
+
[[package]]
|
137 |
+
name = "httpcore"
|
138 |
+
version = "1.0.9"
|
139 |
+
source = { registry = "https://pypi.org/simple" }
|
140 |
+
dependencies = [
|
141 |
+
{ name = "certifi" },
|
142 |
+
{ name = "h11" },
|
143 |
+
]
|
144 |
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 }
|
145 |
+
wheels = [
|
146 |
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
|
147 |
+
]
|
148 |
+
|
149 |
+
[[package]]
|
150 |
+
name = "httpx"
|
151 |
+
version = "0.28.1"
|
152 |
+
source = { registry = "https://pypi.org/simple" }
|
153 |
+
dependencies = [
|
154 |
+
{ name = "anyio" },
|
155 |
+
{ name = "certifi" },
|
156 |
+
{ name = "httpcore" },
|
157 |
+
{ name = "idna" },
|
158 |
+
]
|
159 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
|
160 |
+
wheels = [
|
161 |
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
|
162 |
+
]
|
163 |
+
|
164 |
+
[[package]]
|
165 |
+
name = "idna"
|
166 |
+
version = "3.10"
|
167 |
+
source = { registry = "https://pypi.org/simple" }
|
168 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
169 |
+
wheels = [
|
170 |
+
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
171 |
+
]
|
172 |
+
|
173 |
+
[[package]]
|
174 |
+
name = "jiter"
|
175 |
+
version = "0.10.0"
|
176 |
+
source = { registry = "https://pypi.org/simple" }
|
177 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 }
|
178 |
+
wheels = [
|
179 |
+
{ url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 },
|
180 |
+
{ url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 },
|
181 |
+
{ url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 },
|
182 |
+
{ url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 },
|
183 |
+
{ url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 },
|
184 |
+
{ url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 },
|
185 |
+
{ url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 },
|
186 |
+
{ url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 },
|
187 |
+
{ url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 },
|
188 |
+
{ url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 },
|
189 |
+
{ url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 },
|
190 |
+
{ url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 },
|
191 |
+
{ url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 },
|
192 |
+
{ url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 },
|
193 |
+
{ url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 },
|
194 |
+
{ url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 },
|
195 |
+
{ url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 },
|
196 |
+
{ url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 },
|
197 |
+
{ url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 },
|
198 |
+
{ url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 },
|
199 |
+
{ url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 },
|
200 |
+
{ url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 },
|
201 |
+
{ url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 },
|
202 |
+
{ url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 },
|
203 |
+
{ url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 },
|
204 |
+
{ url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 },
|
205 |
+
{ url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 },
|
206 |
+
{ url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 },
|
207 |
+
{ url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 },
|
208 |
+
{ url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 },
|
209 |
+
{ url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 },
|
210 |
+
{ url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 },
|
211 |
+
{ url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 },
|
212 |
+
{ url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 },
|
213 |
+
{ url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 },
|
214 |
+
{ url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 },
|
215 |
+
{ url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 },
|
216 |
+
{ url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 },
|
217 |
+
{ url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 },
|
218 |
+
{ url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 },
|
219 |
+
]
|
220 |
+
|
221 |
+
[[package]]
|
222 |
+
name = "openai"
|
223 |
+
version = "1.99.5"
|
224 |
+
source = { registry = "https://pypi.org/simple" }
|
225 |
+
dependencies = [
|
226 |
+
{ name = "anyio" },
|
227 |
+
{ name = "distro" },
|
228 |
+
{ name = "httpx" },
|
229 |
+
{ name = "jiter" },
|
230 |
+
{ name = "pydantic" },
|
231 |
+
{ name = "sniffio" },
|
232 |
+
{ name = "tqdm" },
|
233 |
+
{ name = "typing-extensions" },
|
234 |
+
]
|
235 |
+
sdist = { url = "https://files.pythonhosted.org/packages/2f/4a/16b1b6ee8a62cbfb59057f97f6d9b7bb5ce529047d80bc0b406f65dfdc48/openai-1.99.5.tar.gz", hash = "sha256:aa97ac3326cac7949c5e4ac0274c454c1d19c939760107ae0d3948fc26a924ca", size = 505144 }
|
236 |
+
wheels = [
|
237 |
+
{ url = "https://files.pythonhosted.org/packages/e6/f2/2472ae020f5156a994710bf926a76915c71bc7b5debf7b81a11506ec8414/openai-1.99.5-py3-none-any.whl", hash = "sha256:4e870f9501b7c36132e2be13313ce3c4d6915a837e7a299c483aab6a6d4412e9", size = 786246 },
|
238 |
+
]
|
239 |
+
|
240 |
+
[[package]]
|
241 |
+
name = "pydantic"
|
242 |
+
version = "2.11.7"
|
243 |
+
source = { registry = "https://pypi.org/simple" }
|
244 |
+
dependencies = [
|
245 |
+
{ name = "annotated-types" },
|
246 |
+
{ name = "pydantic-core" },
|
247 |
+
{ name = "typing-extensions" },
|
248 |
+
{ name = "typing-inspection" },
|
249 |
+
]
|
250 |
+
sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 }
|
251 |
+
wheels = [
|
252 |
+
{ url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 },
|
253 |
+
]
|
254 |
+
|
255 |
+
[[package]]
|
256 |
+
name = "pydantic-core"
|
257 |
+
version = "2.33.2"
|
258 |
+
source = { registry = "https://pypi.org/simple" }
|
259 |
+
dependencies = [
|
260 |
+
{ name = "typing-extensions" },
|
261 |
+
]
|
262 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 }
|
263 |
+
wheels = [
|
264 |
+
{ url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 },
|
265 |
+
{ url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 },
|
266 |
+
{ url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 },
|
267 |
+
{ url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 },
|
268 |
+
{ url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 },
|
269 |
+
{ url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 },
|
270 |
+
{ url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 },
|
271 |
+
{ url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 },
|
272 |
+
{ url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 },
|
273 |
+
{ url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 },
|
274 |
+
{ url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 },
|
275 |
+
{ url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 },
|
276 |
+
{ url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 },
|
277 |
+
{ url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 },
|
278 |
+
{ url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 },
|
279 |
+
{ url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 },
|
280 |
+
{ url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 },
|
281 |
+
{ url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 },
|
282 |
+
{ url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 },
|
283 |
+
{ url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 },
|
284 |
+
{ url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 },
|
285 |
+
{ url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 },
|
286 |
+
{ url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 },
|
287 |
+
{ url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 },
|
288 |
+
{ url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 },
|
289 |
+
{ url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 },
|
290 |
+
{ url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 },
|
291 |
+
{ url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 },
|
292 |
+
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 },
|
293 |
+
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 },
|
294 |
+
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 },
|
295 |
+
]
|
296 |
+
|
297 |
+
[[package]]
|
298 |
+
name = "python-dateutil"
|
299 |
+
version = "2.9.0.post0"
|
300 |
+
source = { registry = "https://pypi.org/simple" }
|
301 |
+
dependencies = [
|
302 |
+
{ name = "six" },
|
303 |
+
]
|
304 |
+
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
|
305 |
+
wheels = [
|
306 |
+
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
|
307 |
+
]
|
308 |
+
|
309 |
+
[[package]]
|
310 |
+
name = "python-dotenv"
|
311 |
+
version = "1.1.1"
|
312 |
+
source = { registry = "https://pypi.org/simple" }
|
313 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 }
|
314 |
+
wheels = [
|
315 |
+
{ url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 },
|
316 |
+
]
|
317 |
+
|
318 |
+
[[package]]
|
319 |
+
name = "sgmllib3k"
|
320 |
+
version = "1.0.0"
|
321 |
+
source = { registry = "https://pypi.org/simple" }
|
322 |
+
sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750 }
|
323 |
+
|
324 |
+
[[package]]
|
325 |
+
name = "six"
|
326 |
+
version = "1.17.0"
|
327 |
+
source = { registry = "https://pypi.org/simple" }
|
328 |
+
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
|
329 |
+
wheels = [
|
330 |
+
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
|
331 |
+
]
|
332 |
+
|
333 |
+
[[package]]
|
334 |
+
name = "sniffio"
|
335 |
+
version = "1.3.1"
|
336 |
+
source = { registry = "https://pypi.org/simple" }
|
337 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
|
338 |
+
wheels = [
|
339 |
+
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
340 |
+
]
|
341 |
+
|
342 |
+
[[package]]
|
343 |
+
name = "starlette"
|
344 |
+
version = "0.47.2"
|
345 |
+
source = { registry = "https://pypi.org/simple" }
|
346 |
+
dependencies = [
|
347 |
+
{ name = "anyio" },
|
348 |
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
349 |
+
]
|
350 |
+
sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948 }
|
351 |
+
wheels = [
|
352 |
+
{ url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984 },
|
353 |
+
]
|
354 |
+
|
355 |
+
[[package]]
|
356 |
+
name = "tqdm"
|
357 |
+
version = "4.67.1"
|
358 |
+
source = { registry = "https://pypi.org/simple" }
|
359 |
+
dependencies = [
|
360 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
361 |
+
]
|
362 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
|
363 |
+
wheels = [
|
364 |
+
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 },
|
365 |
+
]
|
366 |
+
|
367 |
+
[[package]]
|
368 |
+
name = "typing-extensions"
|
369 |
+
version = "4.14.1"
|
370 |
+
source = { registry = "https://pypi.org/simple" }
|
371 |
+
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673 }
|
372 |
+
wheels = [
|
373 |
+
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906 },
|
374 |
+
]
|
375 |
+
|
376 |
+
[[package]]
|
377 |
+
name = "typing-inspection"
|
378 |
+
version = "0.4.1"
|
379 |
+
source = { registry = "https://pypi.org/simple" }
|
380 |
+
dependencies = [
|
381 |
+
{ name = "typing-extensions" },
|
382 |
+
]
|
383 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 }
|
384 |
+
wheels = [
|
385 |
+
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 },
|
386 |
+
]
|
387 |
+
|
388 |
+
[[package]]
|
389 |
+
name = "uvicorn"
|
390 |
+
version = "0.35.0"
|
391 |
+
source = { registry = "https://pypi.org/simple" }
|
392 |
+
dependencies = [
|
393 |
+
{ name = "click" },
|
394 |
+
{ name = "h11" },
|
395 |
+
]
|
396 |
+
sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473 }
|
397 |
+
wheels = [
|
398 |
+
{ url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406 },
|
399 |
+
]
|