Compare commits
	
		
			142 Commits
		
	
	
		
			admin-pass
			...
			feat/struc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | df33229934 | ||
|  | 49fafaa096 | ||
|  | ca9b32a23b | ||
|  | 76e3ff4e02 | ||
|  | eabf3ca7d3 | ||
|  | 94e6db10bb | ||
|  | 26e1d5fec3 | ||
|  | 66be87b688 | ||
|  | f7b4e32218 | ||
|  | 57407112fb | ||
|  | b280cc2e01 | ||
|  | e6ebf892c5 | ||
|  | b754641058 | ||
|  | 722f4f760e | ||
|  | 01e04a209f | ||
|  | 0299fd1ea0 | ||
|  | cf8dec53ca | ||
|  | d5c012d748 | ||
|  | 2ccbd9a44c | ||
|  | ccd89d48d9 | ||
|  | 87d788ddef | ||
|  | 809b625a34 | ||
|  | 95c753a549 | ||
|  | 0bb8b7ec5c | ||
|  | c6d084f5dc | ||
|  | 0024ce36c8 | ||
|  | c44e746807 | ||
|  | b1826066f4 | ||
|  | b0b8acc45b | ||
|  | e2b9ffc072 | ||
|  | 68c43ea372 | ||
|  | 3b46baca4f | ||
|  | 772e461c08 | ||
|  | 5c6018a0f9 | ||
|  | 0b7989c3d3 | ||
|  | 8cfcc3e39c | ||
|  | 3a57261590 | ||
|  | a86a1a461c | ||
|  | 2257e1df0c | ||
|  | ccb72c8970 | ||
|  | 740ff941a5 | ||
|  | 117a683d9a | ||
|  | 9eba4b7373 | ||
|  | 91306dc0c7 | ||
|  | 1716dd5a65 | ||
|  | 66f9a674f1 | ||
|  | 41fc5274ff | ||
|  | bcebdb5fd9 | ||
|  | 876487ad11 | ||
|  | 18da75ad97 | ||
|  | c80ac1415d | ||
|  | bb21184ea2 | ||
|  | 0c3740fdf2 | ||
|  | 701819d018 | ||
|  | 68e151b2bd | ||
|  | 06ff272541 | ||
|  | 4154d5e4b1 | ||
|  | 1862491496 | ||
|  | 073b5e897c | ||
|  | 7e1d6ebd19 | ||
|  | 9a332e79e4 | ||
|  | 72450b9217 | ||
|  | 7e1dc33a08 | ||
|  | aa240009ab | ||
|  | 41b258e4d8 | ||
|  | da1123d84b | ||
|  | 627775c430 | ||
|  | 245573efca | ||
|  | 28b9cca413 | ||
|  | a85f762c58 | ||
|  | 3ddcceda0a | ||
|  | e226645bc7 | ||
|  | 5447530ece | ||
|  | ed6d46a440 | ||
|  | 588e68e93e | ||
|  | c4440327db | ||
|  | 64e2d457cc | ||
|  | bf705afc21 | ||
|  | 2e4433a6b3 | ||
|  | 09661ae11d | ||
|  | a8d410bc2f | ||
|  | 7d52fbb368 | ||
|  | 4b8e0ea1aa | ||
|  | 5b1055e8c9 | ||
|  | 4b2a7916fd | ||
|  | 97e64aa65e | ||
|  | 90e303f737 | ||
|  | 7955d8e408 | ||
|  | b285cb4323 | ||
|  | 5d60ab1139 | ||
|  | 9095996356 | ||
|  | 310c8a75fd | ||
|  | 191d1dc25f | ||
|  | d3b2f8983d | ||
|  | 27286465a3 | ||
|  | defc677932 | ||
|  | 45df9dc5bf | ||
|  | 06db95d7c0 | ||
|  | 74f7eaed6e | ||
|  | dddd944a18 | ||
|  | 7eccd4d75b | ||
|  | 62e6c24840 | ||
|  | 04a0342b52 | ||
|  | 5c016127cb | ||
|  | 8b552010f9 | ||
|  | 97804e7b4d | ||
|  | 33b895b75e | ||
|  | 048de2cb74 | ||
|  | 274e6ca88c | ||
|  | f628b6e416 | ||
|  | cf7144db96 | ||
|  | ffa793056d | ||
|  | 584d02b92a | ||
|  | 008c7cbec0 | ||
|  | 4d1ee79b8d | ||
|  | ea638279e5 | ||
|  | 403d13eb50 | ||
|  | 217736d05a | ||
|  | 8a24572cd2 | ||
|  | 649c68f292 | ||
|  | bab5dba6e1 | ||
|  | c24edac16d | ||
|  | 3150c21f17 | ||
|  | c46fd7a9c8 | ||
|  | bab32e8d70 | ||
|  | 1130746f5d | ||
|  | d1e9361665 | ||
|  | 3bf2337697 | ||
|  | ee6e197ec0 | ||
|  | 32f26bb4e8 | ||
|  | 4cb20542a5 | ||
|  | 97f6196d9b | ||
|  | 6c227cab6f | ||
|  | e9e34ddff9 | ||
|  | e29a08dc46 | ||
|  | 5c313e9bed | ||
|  | 6b5bd9d79b | ||
|  | 64d2a467b0 | ||
|  | 9a2c4fe3b6 | ||
|  | 060c68a900 | ||
|  | e6b87f89ec | ||
|  | 8aaee2c40c | 
							
								
								
									
										0
									
								
								.assets/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										127
									
								
								.github/workflows/docker-build.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -8,18 +8,12 @@ on: | |||||||
|     types: [published] |     types: [published] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   build-and-push: |   build-amd64: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     strategy: |  | ||||||
|       matrix: |  | ||||||
|         service: [backend, app] |  | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout code |       - name: Checkout code | ||||||
|         uses: actions/checkout@v3 |         uses: actions/checkout@v3 | ||||||
|  |  | ||||||
|       - name: Set up QEMU |  | ||||||
|         uses: docker/setup-qemu-action@v2 |  | ||||||
|  |  | ||||||
|       - name: Set up Docker Buildx |       - name: Set up Docker Buildx | ||||||
|         uses: docker/setup-buildx-action@v2 |         uses: docker/setup-buildx-action@v2 | ||||||
|         with: |         with: | ||||||
| @@ -36,38 +30,109 @@ jobs: | |||||||
|         id: version |         id: version | ||||||
|         run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV |         run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV | ||||||
|  |  | ||||||
|       - name: Build and push Docker image for ${{ matrix.service }} |       - name: Build and push AMD64 Docker image | ||||||
|         if: github.ref == 'refs/heads/master' && github.event_name == 'push' |         if: github.ref == 'refs/heads/master' && github.event_name == 'push' | ||||||
|         run: | |         run: | | ||||||
|           docker buildx create --use |           DOCKERFILE=app.dockerfile | ||||||
|           if [[ "${{ matrix.service }}" == "backend" ]]; then \ |           IMAGE_NAME=perplexica | ||||||
|             DOCKERFILE=backend.dockerfile; \ |           docker buildx build --platform linux/amd64 \ | ||||||
|             IMAGE_NAME=perplexica-backend; \ |             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:amd64 \ | ||||||
|           else \ |  | ||||||
|             DOCKERFILE=app.dockerfile; \ |  | ||||||
|             IMAGE_NAME=perplexica-frontend; \ |  | ||||||
|           fi |  | ||||||
|           docker buildx build --platform linux/amd64,linux/arm64 \ |  | ||||||
|             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:main \ |  | ||||||
|             --cache-to=type=inline \ |             --cache-to=type=inline \ | ||||||
|  |             --provenance false \ | ||||||
|             -f $DOCKERFILE \ |             -f $DOCKERFILE \ | ||||||
|             -t itzcrazykns1337/${IMAGE_NAME}:main \ |             -t itzcrazykns1337/${IMAGE_NAME}:amd64 \ | ||||||
|             --push . |             --push . | ||||||
|  |  | ||||||
|       - name: Build and push release Docker image for ${{ matrix.service }} |       - name: Build and push AMD64 release Docker image | ||||||
|         if: github.event_name == 'release' |         if: github.event_name == 'release' | ||||||
|         run: | |         run: | | ||||||
|           docker buildx create --use |           DOCKERFILE=app.dockerfile | ||||||
|           if [[ "${{ matrix.service }}" == "backend" ]]; then \ |           IMAGE_NAME=perplexica | ||||||
|             DOCKERFILE=backend.dockerfile; \ |           docker buildx build --platform linux/amd64 \ | ||||||
|             IMAGE_NAME=perplexica-backend; \ |             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-amd64 \ | ||||||
|           else \ |  | ||||||
|             DOCKERFILE=app.dockerfile; \ |  | ||||||
|             IMAGE_NAME=perplexica-frontend; \ |  | ||||||
|           fi |  | ||||||
|           docker buildx build --platform linux/amd64,linux/arm64 \ |  | ||||||
|             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \ |  | ||||||
|             --cache-to=type=inline \ |             --cache-to=type=inline \ | ||||||
|  |             --provenance false \ | ||||||
|             -f $DOCKERFILE \ |             -f $DOCKERFILE \ | ||||||
|             -t itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \ |             -t itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-amd64 \ | ||||||
|             --push . |             --push . | ||||||
|  |  | ||||||
|  |   build-arm64: | ||||||
|  |     runs-on: ubuntu-24.04-arm | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout code | ||||||
|  |         uses: actions/checkout@v3 | ||||||
|  |  | ||||||
|  |       - name: Set up Docker Buildx | ||||||
|  |         uses: docker/setup-buildx-action@v2 | ||||||
|  |         with: | ||||||
|  |           install: true | ||||||
|  |  | ||||||
|  |       - name: Log in to DockerHub | ||||||
|  |         uses: docker/login-action@v2 | ||||||
|  |         with: | ||||||
|  |           username: ${{ secrets.DOCKER_USERNAME }} | ||||||
|  |           password: ${{ secrets.DOCKER_PASSWORD }} | ||||||
|  |  | ||||||
|  |       - name: Extract version from release tag | ||||||
|  |         if: github.event_name == 'release' | ||||||
|  |         id: version | ||||||
|  |         run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV | ||||||
|  |  | ||||||
|  |       - name: Build and push ARM64 Docker image | ||||||
|  |         if: github.ref == 'refs/heads/master' && github.event_name == 'push' | ||||||
|  |         run: | | ||||||
|  |           DOCKERFILE=app.dockerfile | ||||||
|  |           IMAGE_NAME=perplexica | ||||||
|  |           docker buildx build --platform linux/arm64 \ | ||||||
|  |             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:arm64 \ | ||||||
|  |             --cache-to=type=inline \ | ||||||
|  |             --provenance false \ | ||||||
|  |             -f $DOCKERFILE \ | ||||||
|  |             -t itzcrazykns1337/${IMAGE_NAME}:arm64 \ | ||||||
|  |             --push . | ||||||
|  |  | ||||||
|  |       - name: Build and push ARM64 release Docker image | ||||||
|  |         if: github.event_name == 'release' | ||||||
|  |         run: | | ||||||
|  |           DOCKERFILE=app.dockerfile | ||||||
|  |           IMAGE_NAME=perplexica | ||||||
|  |           docker buildx build --platform linux/arm64 \ | ||||||
|  |             --cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-arm64 \ | ||||||
|  |             --cache-to=type=inline \ | ||||||
|  |             --provenance false \ | ||||||
|  |             -f $DOCKERFILE \ | ||||||
|  |             -t itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-arm64 \ | ||||||
|  |             --push . | ||||||
|  |  | ||||||
|  |   manifest: | ||||||
|  |     needs: [build-amd64, build-arm64] | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Log in to DockerHub | ||||||
|  |         uses: docker/login-action@v2 | ||||||
|  |         with: | ||||||
|  |           username: ${{ secrets.DOCKER_USERNAME }} | ||||||
|  |           password: ${{ secrets.DOCKER_PASSWORD }} | ||||||
|  |  | ||||||
|  |       - name: Extract version from release tag | ||||||
|  |         if: github.event_name == 'release' | ||||||
|  |         id: version | ||||||
|  |         run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV | ||||||
|  |  | ||||||
|  |       - name: Create and push multi-arch manifest for main | ||||||
|  |         if: github.ref == 'refs/heads/master' && github.event_name == 'push' | ||||||
|  |         run: | | ||||||
|  |           IMAGE_NAME=perplexica | ||||||
|  |           docker manifest create itzcrazykns1337/${IMAGE_NAME}:main \ | ||||||
|  |             --amend itzcrazykns1337/${IMAGE_NAME}:amd64 \ | ||||||
|  |             --amend itzcrazykns1337/${IMAGE_NAME}:arm64 | ||||||
|  |           docker manifest push itzcrazykns1337/${IMAGE_NAME}:main | ||||||
|  |  | ||||||
|  |       - name: Create and push multi-arch manifest for releases | ||||||
|  |         if: github.event_name == 'release' | ||||||
|  |         run: | | ||||||
|  |           IMAGE_NAME=perplexica | ||||||
|  |           docker manifest create itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \ | ||||||
|  |             --amend itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-amd64 \ | ||||||
|  |             --amend itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }}-arm64 | ||||||
|  |           docker manifest push itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -4,9 +4,9 @@ npm-debug.log | |||||||
| yarn-error.log | yarn-error.log | ||||||
|  |  | ||||||
| # Build output | # Build output | ||||||
| /.next/ | .next/ | ||||||
| /out/ | out/ | ||||||
| /dist/ | dist/ | ||||||
|  |  | ||||||
| # IDE/Editor specific | # IDE/Editor specific | ||||||
| .vscode/ | .vscode/ | ||||||
| @@ -37,3 +37,5 @@ Thumbs.db | |||||||
| # Db | # Db | ||||||
| db.sqlite | db.sqlite | ||||||
| /searxng | /searxng | ||||||
|  |  | ||||||
|  | certificates | ||||||
| @@ -6,7 +6,6 @@ const config = { | |||||||
|   endOfLine: 'auto', |   endOfLine: 'auto', | ||||||
|   singleQuote: true, |   singleQuote: true, | ||||||
|   tabWidth: 2, |   tabWidth: 2, | ||||||
|   semi: true, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| module.exports = config; | module.exports = config; | ||||||
|   | |||||||
| @@ -1,32 +1,43 @@ | |||||||
| # How to Contribute to Perplexica | # How to Contribute to Perplexica | ||||||
|  |  | ||||||
| Hey there, thanks for deciding to contribute to Perplexica. Anything you help with will support the development of Perplexica and will make it better. Let's walk you through the key aspects to ensure your contributions are effective and in harmony with the project's setup. | Thanks for your interest in contributing to Perplexica! Your help makes this project better. This guide explains how to contribute effectively. | ||||||
|  |  | ||||||
|  | Perplexica is a modern AI chat application with advanced search capabilities. | ||||||
|  |  | ||||||
| ## Project Structure | ## Project Structure | ||||||
|  |  | ||||||
| Perplexica's design consists of two main domains: | Perplexica's codebase is organized as follows: | ||||||
|  |  | ||||||
| - **Frontend (`ui` directory)**: This is a Next.js application holding all user interface components. It's a self-contained environment that manages everything the user interacts with. | - **UI Components and Pages**: | ||||||
| - **Backend (root and `src` directory)**: The backend logic is situated in the `src` folder, but the root directory holds the main `package.json` for backend dependency management. |   - **Components (`src/components`)**: Reusable UI components. | ||||||
|   - All of the focus modes are created using the Meta Search Agent class present in `src/search/metaSearchAgent.ts`. The main logic behind Perplexica lies there. |   - **Pages and Routes (`src/app`)**: Next.js app directory structure with page components. | ||||||
|  |     - Main app routes include: home (`/`), chat (`/c`), discover (`/discover`), library (`/library`), and settings (`/settings`). | ||||||
|  |   - **API Routes (`src/app/api`)**: API endpoints implemented with Next.js API routes. | ||||||
|  |     - `/api/chat`: Handles chat interactions. | ||||||
|  |     - `/api/search`: Provides direct access to Perplexica's search capabilities. | ||||||
|  |     - Other endpoints for models, files, and suggestions. | ||||||
|  | - **Backend Logic (`src/lib`)**: Contains all the backend functionality including search, database, and API logic. | ||||||
|  |   - The search functionality is present inside `src/lib/search` directory. | ||||||
|  |   - All of the focus modes are implemented using the Meta Search Agent class in `src/lib/search/metaSearchAgent.ts`. | ||||||
|  |   - Database functionality is in `src/lib/db`. | ||||||
|  |   - Chat model and embedding model providers are managed in `src/lib/providers`. | ||||||
|  |   - Prompt templates and LLM chain definitions are in `src/lib/prompts` and `src/lib/chains` respectively. | ||||||
|  |  | ||||||
|  | ## API Documentation | ||||||
|  |  | ||||||
|  | Perplexica exposes several API endpoints for programmatic access, including: | ||||||
|  |  | ||||||
|  | - **Search API**: Access Perplexica's advanced search capabilities directly via the `/api/search` endpoint. For detailed documentation, see `docs/api/search.md`. | ||||||
|  |  | ||||||
| ## Setting Up Your Environment | ## Setting Up Your Environment | ||||||
|  |  | ||||||
| Before diving into coding, setting up your local environment is key. Here's what you need to do: | Before diving into coding, setting up your local environment is key. Here's what you need to do: | ||||||
|  |  | ||||||
| ### Backend |  | ||||||
|  |  | ||||||
| 1. In the root directory, locate the `sample.config.toml` file. | 1. In the root directory, locate the `sample.config.toml` file. | ||||||
| 2. Rename it to `config.toml` and fill in the necessary configuration fields specific to the backend. | 2. Rename it to `config.toml` and fill in the necessary configuration fields. | ||||||
| 3. Run `npm install` to install dependencies. | 3. Run `npm install` to install all dependencies. | ||||||
| 4. Run `npm run db:push` to set up the local sqlite. | 4. Run `npm run db:push` to set up the local sqlite database. | ||||||
| 5. Use `npm run dev` to start the backend in development mode. | 5. Use `npm run dev` to start the application in development mode. | ||||||
|  |  | ||||||
| ### Frontend |  | ||||||
|  |  | ||||||
| 1. Navigate to the `ui` folder and repeat the process of renaming `.env.example` to `.env`, making sure to provide the frontend-specific variables. |  | ||||||
| 2. Execute `npm install` within the `ui` directory to get the frontend dependencies ready. |  | ||||||
| 3. Launch the frontend development server with `npm run dev`. |  | ||||||
|  |  | ||||||
| **Please note**: Docker configurations are present for setting up production environments, whereas `npm run dev` is used for development purposes. | **Please note**: Docker configurations are present for setting up production environments, whereas `npm run dev` is used for development purposes. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -16,7 +16,7 @@ | |||||||
|  |  | ||||||
| <hr/> | <hr/> | ||||||
|  |  | ||||||
| [](https://discord.gg/26aArMy8tT) | [](https://discord.gg/26aArMy8tT) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -90,6 +90,9 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. | |||||||
|    - `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**. |    - `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**. | ||||||
|    - `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models**. |    - `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models**. | ||||||
|    - `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**. |    - `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**. | ||||||
|  |    - `Gemini`: Your Gemini API key. **You only need to fill this if you wish to use Google's models**. | ||||||
|  |    - `DEEPSEEK`: Your Deepseek API key. **Only needed if you want Deepseek models.** | ||||||
|  |    - `AIMLAPI`: Your AI/ML API key. **Only needed if you want to use AI/ML API models and embeddings.** | ||||||
|  |  | ||||||
|      **Note**: You can change these after starting Perplexica from the settings dialog. |      **Note**: You can change these after starting Perplexica from the settings dialog. | ||||||
|  |  | ||||||
| @@ -109,14 +112,13 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. | |||||||
|  |  | ||||||
| 1. Install SearXNG and allow `JSON` format in the SearXNG settings. | 1. Install SearXNG and allow `JSON` format in the SearXNG settings. | ||||||
| 2. Clone the repository and rename the `sample.config.toml` file to `config.toml` in the root directory. Ensure you complete all required fields in this file. | 2. Clone the repository and rename the `sample.config.toml` file to `config.toml` in the root directory. Ensure you complete all required fields in this file. | ||||||
| 3. Rename the `.env.example` file to `.env` in the `ui` folder and fill in all necessary fields. | 3. After populating the configuration run `npm i`. | ||||||
| 4. After populating the configuration and environment files, run `npm i` in both the `ui` folder and the root directory. | 4. Install the dependencies and then execute `npm run build`. | ||||||
| 5. Install the dependencies and then execute `npm run build` in both the `ui` folder and the root directory. | 5. Finally, start the app by running `npm run start` | ||||||
| 6. Finally, start both the frontend and the backend by running `npm run start` in both the `ui` folder and the root directory. |  | ||||||
|  |  | ||||||
| **Note**: Using Docker is recommended as it simplifies the setup process, especially for managing environment variables and dependencies. | **Note**: Using Docker is recommended as it simplifies the setup process, especially for managing environment variables and dependencies. | ||||||
|  |  | ||||||
| See the [installation documentation](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/installation) for more information like exposing it your network, etc. | See the [installation documentation](https://github.com/ItzCrazyKns/Perplexica/tree/master/docs/installation) for more information like updating, etc. | ||||||
|  |  | ||||||
| ### Ollama Connection Errors | ### Ollama Connection Errors | ||||||
|  |  | ||||||
| @@ -133,7 +135,7 @@ If you're encountering an Ollama connection error, it is likely due to the backe | |||||||
|  |  | ||||||
| 3. **Linux Users - Expose Ollama to Network:** | 3. **Linux Users - Expose Ollama to Network:** | ||||||
|  |  | ||||||
|    - Inside `/etc/systemd/system/ollama.service`, you need to add `Environment="OLLAMA_HOST=0.0.0.0"`. Then restart Ollama by `systemctl restart ollama`. For more information see [Ollama docs](https://github.com/ollama/ollama/blob/main/docs/faq.md#setting-environment-variables-on-linux) |    - Inside `/etc/systemd/system/ollama.service`, you need to add `Environment="OLLAMA_HOST=0.0.0.0:11434"`. (Change the port number if you are using a different one.) Then reload the systemd manager configuration with `systemctl daemon-reload`, and restart Ollama by `systemctl restart ollama`. For more information see [Ollama docs](https://github.com/ollama/ollama/blob/main/docs/faq.md#setting-environment-variables-on-linux) | ||||||
|  |  | ||||||
|    - Ensure that the port (default is 11434) is not blocked by your firewall. |    - Ensure that the port (default is 11434) is not blocked by your firewall. | ||||||
|  |  | ||||||
| @@ -154,12 +156,13 @@ For more details, check out the full documentation [here](https://github.com/Itz | |||||||
|  |  | ||||||
| ## Expose Perplexica to network | ## Expose Perplexica to network | ||||||
|  |  | ||||||
| You can access Perplexica over your home network by following our networking guide [here](https://github.com/ItzCrazyKns/Perplexica/blob/master/docs/installation/NETWORKING.md). | Perplexica runs on Next.js and handles all API requests. It works right away on the same network and stays accessible even with port forwarding. | ||||||
|  |  | ||||||
| ## One-Click Deployment | ## One-Click Deployment | ||||||
|  |  | ||||||
| [](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica) | [](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica) | ||||||
| [](https://repocloud.io/details/?app_id=267) | [](https://repocloud.io/details/?app_id=267) | ||||||
|  | [](https://template.run.claw.cloud/?referralCode=U11MRQ8U9RM4&openapp=system-fastdeploy%3FtemplateName%3Dperplexica) | ||||||
|  |  | ||||||
| ## Upcoming Features | ## Upcoming Features | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,15 +1,35 @@ | |||||||
| FROM node:20.18.0-alpine | FROM node:20.18.0-slim AS builder | ||||||
|  |  | ||||||
| ARG NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001 |  | ||||||
| ARG NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api |  | ||||||
| ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL} |  | ||||||
| ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} |  | ||||||
|  |  | ||||||
| WORKDIR /home/perplexica | WORKDIR /home/perplexica | ||||||
|  |  | ||||||
| COPY ui /home/perplexica/ | COPY package.json yarn.lock ./ | ||||||
|  | RUN yarn install --frozen-lockfile --network-timeout 600000 | ||||||
|  |  | ||||||
| RUN yarn install --frozen-lockfile | COPY tsconfig.json next.config.mjs next-env.d.ts postcss.config.js drizzle.config.ts tailwind.config.ts ./ | ||||||
|  | COPY src ./src | ||||||
|  | COPY public ./public | ||||||
|  |  | ||||||
|  | RUN mkdir -p /home/perplexica/data | ||||||
| RUN yarn build | RUN yarn build | ||||||
|  |  | ||||||
| CMD ["yarn", "start"] | RUN yarn add --dev @vercel/ncc | ||||||
|  | RUN yarn ncc build ./src/lib/db/migrate.ts -o migrator | ||||||
|  |  | ||||||
|  | FROM node:20.18.0-slim | ||||||
|  |  | ||||||
|  | WORKDIR /home/perplexica | ||||||
|  |  | ||||||
|  | COPY --from=builder /home/perplexica/public ./public | ||||||
|  | COPY --from=builder /home/perplexica/.next/static ./public/_next/static | ||||||
|  |  | ||||||
|  | COPY --from=builder /home/perplexica/.next/standalone ./ | ||||||
|  | COPY --from=builder /home/perplexica/data ./data | ||||||
|  | COPY drizzle ./drizzle | ||||||
|  | COPY --from=builder /home/perplexica/migrator/build ./build | ||||||
|  | COPY --from=builder /home/perplexica/migrator/index.js ./migrate.js | ||||||
|  |  | ||||||
|  | RUN mkdir /home/perplexica/uploads | ||||||
|  |  | ||||||
|  | COPY entrypoint.sh ./entrypoint.sh | ||||||
|  | RUN chmod +x ./entrypoint.sh | ||||||
|  | CMD ["./entrypoint.sh"] | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| FROM node:18-slim |  | ||||||
|  |  | ||||||
| WORKDIR /home/perplexica |  | ||||||
|  |  | ||||||
| COPY src /home/perplexica/src |  | ||||||
| COPY tsconfig.json /home/perplexica/ |  | ||||||
| COPY drizzle.config.ts /home/perplexica/ |  | ||||||
| COPY package.json /home/perplexica/ |  | ||||||
| COPY yarn.lock /home/perplexica/ |  | ||||||
|  |  | ||||||
| RUN mkdir /home/perplexica/data |  | ||||||
| RUN mkdir /home/perplexica/uploads |  | ||||||
|  |  | ||||||
| RUN yarn install --frozen-lockfile --network-timeout 600000 |  | ||||||
| RUN yarn build |  | ||||||
|  |  | ||||||
| CMD ["yarn", "start"] |  | ||||||
| @@ -9,41 +9,22 @@ services: | |||||||
|       - perplexica-network |       - perplexica-network | ||||||
|     restart: unless-stopped |     restart: unless-stopped | ||||||
|  |  | ||||||
|   perplexica-backend: |   app: | ||||||
|     build: |     image: itzcrazykns1337/perplexica:main | ||||||
|       context: . |  | ||||||
|       dockerfile: backend.dockerfile |  | ||||||
|     image: itzcrazykns1337/perplexica-backend:main |  | ||||||
|     environment: |  | ||||||
|       - SEARXNG_API_URL=http://searxng:8080 |  | ||||||
|     depends_on: |  | ||||||
|       - searxng |  | ||||||
|     ports: |  | ||||||
|       - 3001:3001 |  | ||||||
|     volumes: |  | ||||||
|       - backend-dbstore:/home/perplexica/data |  | ||||||
|       - uploads:/home/perplexica/uploads |  | ||||||
|       - ./config.toml:/home/perplexica/config.toml |  | ||||||
|     extra_hosts: |  | ||||||
|       - 'host.docker.internal:host-gateway' |  | ||||||
|     networks: |  | ||||||
|       - perplexica-network |  | ||||||
|     restart: unless-stopped |  | ||||||
|  |  | ||||||
|   perplexica-frontend: |  | ||||||
|     build: |     build: | ||||||
|       context: . |       context: . | ||||||
|       dockerfile: app.dockerfile |       dockerfile: app.dockerfile | ||||||
|       args: |     environment: | ||||||
|         - NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api |       - SEARXNG_API_URL=http://searxng:8080 | ||||||
|         - NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001 |       - DATA_DIR=/home/perplexica | ||||||
|     image: itzcrazykns1337/perplexica-frontend:main |  | ||||||
|     depends_on: |  | ||||||
|       - perplexica-backend |  | ||||||
|     ports: |     ports: | ||||||
|       - 3000:3000 |       - 3000:3000 | ||||||
|     networks: |     networks: | ||||||
|       - perplexica-network |       - perplexica-network | ||||||
|  |     volumes: | ||||||
|  |       - backend-dbstore:/home/perplexica/data | ||||||
|  |       - uploads:/home/perplexica/uploads | ||||||
|  |       - ./config.toml:/home/perplexica/config.toml | ||||||
|     restart: unless-stopped |     restart: unless-stopped | ||||||
|  |  | ||||||
| networks: | networks: | ||||||
|   | |||||||
| @@ -6,9 +6,9 @@ Perplexica’s Search API makes it easy to use our AI-powered search engine. You | |||||||
|  |  | ||||||
| ## Endpoint | ## Endpoint | ||||||
|  |  | ||||||
| ### **POST** `http://localhost:3001/api/search` | ### **POST** `http://localhost:3000/api/search` | ||||||
|  |  | ||||||
| **Note**: Replace `3001` with any other port if you've changed the default PORT | **Note**: Replace `3000` with any other port if you've changed the default PORT | ||||||
|  |  | ||||||
| ### Request | ### Request | ||||||
|  |  | ||||||
| @@ -20,11 +20,11 @@ The API accepts a JSON object in the request body, where you define the focus mo | |||||||
| { | { | ||||||
|   "chatModel": { |   "chatModel": { | ||||||
|     "provider": "openai", |     "provider": "openai", | ||||||
|     "model": "gpt-4o-mini" |     "name": "gpt-4o-mini" | ||||||
|   }, |   }, | ||||||
|   "embeddingModel": { |   "embeddingModel": { | ||||||
|     "provider": "openai", |     "provider": "openai", | ||||||
|     "model": "text-embedding-3-large" |     "name": "text-embedding-3-large" | ||||||
|   }, |   }, | ||||||
|   "optimizationMode": "speed", |   "optimizationMode": "speed", | ||||||
|   "focusMode": "webSearch", |   "focusMode": "webSearch", | ||||||
| @@ -32,24 +32,26 @@ The API accepts a JSON object in the request body, where you define the focus mo | |||||||
|   "history": [ |   "history": [ | ||||||
|     ["human", "Hi, how are you?"], |     ["human", "Hi, how are you?"], | ||||||
|     ["assistant", "I am doing well, how can I help you today?"] |     ["assistant", "I am doing well, how can I help you today?"] | ||||||
|   ] |   ], | ||||||
|  |   "systemInstructions": "Focus on providing technical details about Perplexica's architecture.", | ||||||
|  |   "stream": false | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Request Parameters | ### Request Parameters | ||||||
|  |  | ||||||
| - **`chatModel`** (object, optional): Defines the chat model to be used for the query. For model details you can send a GET request at `http://localhost:3001/api/models`. Make sure to use the key value (For example "gpt-4o-mini" instead of the display name "GPT 4 omni mini"). | - **`chatModel`** (object, optional): Defines the chat model to be used for the query. For model details you can send a GET request at `http://localhost:3000/api/models`. Make sure to use the key value (For example "gpt-4o-mini" instead of the display name "GPT 4 omni mini"). | ||||||
|  |  | ||||||
|   - `provider`: Specifies the provider for the chat model (e.g., `openai`, `ollama`). |   - `provider`: Specifies the provider for the chat model (e.g., `openai`, `ollama`). | ||||||
|   - `model`: The specific model from the chosen provider (e.g., `gpt-4o-mini`). |   - `name`: The specific model from the chosen provider (e.g., `gpt-4o-mini`). | ||||||
|   - Optional fields for custom OpenAI configuration: |   - Optional fields for custom OpenAI configuration: | ||||||
|     - `customOpenAIBaseURL`: If you’re using a custom OpenAI instance, provide the base URL. |     - `customOpenAIBaseURL`: If you’re using a custom OpenAI instance, provide the base URL. | ||||||
|     - `customOpenAIKey`: The API key for a custom OpenAI instance. |     - `customOpenAIKey`: The API key for a custom OpenAI instance. | ||||||
|  |  | ||||||
| - **`embeddingModel`** (object, optional): Defines the embedding model for similarity-based searching. For model details you can send a GET request at `http://localhost:3001/api/models`. Make sure to use the key value (For example "text-embedding-3-large" instead of the display name "Text Embedding 3 Large"). | - **`embeddingModel`** (object, optional): Defines the embedding model for similarity-based searching. For model details you can send a GET request at `http://localhost:3000/api/models`. Make sure to use the key value (For example "text-embedding-3-large" instead of the display name "Text Embedding 3 Large"). | ||||||
|  |  | ||||||
|   - `provider`: The provider for the embedding model (e.g., `openai`). |   - `provider`: The provider for the embedding model (e.g., `openai`). | ||||||
|   - `model`: The specific embedding model (e.g., `text-embedding-3-large`). |   - `name`: The specific embedding model (e.g., `text-embedding-3-large`). | ||||||
|  |  | ||||||
| - **`focusMode`** (string, required): Specifies which focus mode to use. Available modes: | - **`focusMode`** (string, required): Specifies which focus mode to use. Available modes: | ||||||
|  |  | ||||||
| @@ -62,6 +64,8 @@ The API accepts a JSON object in the request body, where you define the focus mo | |||||||
|  |  | ||||||
| - **`query`** (string, required): The search query or question. | - **`query`** (string, required): The search query or question. | ||||||
|  |  | ||||||
|  | - **`systemInstructions`** (string, optional): Custom instructions provided by the user to guide the AI's response. These instructions are treated as user preferences and have lower priority than the system's core instructions. For example, you can specify a particular writing style, format, or focus area. | ||||||
|  |  | ||||||
| - **`history`** (array, optional): An array of message pairs representing the conversation history. Each pair consists of a role (either 'human' or 'assistant') and the message content. This allows the system to use the context of the conversation to refine results. Example: | - **`history`** (array, optional): An array of message pairs representing the conversation history. Each pair consists of a role (either 'human' or 'assistant') and the message content. This allows the system to use the context of the conversation to refine results. Example: | ||||||
|  |  | ||||||
|   ```json |   ```json | ||||||
| @@ -71,11 +75,13 @@ The API accepts a JSON object in the request body, where you define the focus mo | |||||||
|   ] |   ] | ||||||
|   ``` |   ``` | ||||||
|  |  | ||||||
|  | - **`stream`** (boolean, optional): When set to `true`, enables streaming responses. Default is `false`. | ||||||
|  |  | ||||||
| ### Response | ### Response | ||||||
|  |  | ||||||
| The response from the API includes both the final message and the sources used to generate that message. | The response from the API includes both the final message and the sources used to generate that message. | ||||||
|  |  | ||||||
| #### Example Response | #### Standard Response (stream: false) | ||||||
|  |  | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
| @@ -100,6 +106,28 @@ The response from the API includes both the final message and the sources used t | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | #### Streaming Response (stream: true) | ||||||
|  |  | ||||||
|  | When streaming is enabled, the API returns a stream of newline-delimited JSON objects. Each line contains a complete, valid JSON object. The response has Content-Type: application/json. | ||||||
|  |  | ||||||
|  | Example of streamed response objects: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | {"type":"init","data":"Stream connected"} | ||||||
|  | {"type":"sources","data":[{"pageContent":"...","metadata":{"title":"...","url":"..."}},...]} | ||||||
|  | {"type":"response","data":"Perplexica is an "} | ||||||
|  | {"type":"response","data":"innovative, open-source "} | ||||||
|  | {"type":"response","data":"AI-powered search engine..."} | ||||||
|  | {"type":"done"} | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Clients should process each line as a separate JSON object. The different message types include: | ||||||
|  |  | ||||||
|  | - **`init`**: Initial connection message | ||||||
|  | - **`sources`**: All sources used for the response | ||||||
|  | - **`response`**: Chunks of the generated answer text | ||||||
|  | - **`done`**: Indicates the stream is complete | ||||||
|  |  | ||||||
| ### Fields in the Response | ### Fields in the Response | ||||||
|  |  | ||||||
| - **`message`** (string): The search result, generated based on the query and focus mode. | - **`message`** (string): The search result, generated based on the query and focus mode. | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ Curious about how Perplexica works? Don't worry, we'll cover it here. Before we | |||||||
|  |  | ||||||
| We'll understand how Perplexica works by taking an example of a scenario where a user asks: "How does an A.C. work?". We'll break down the process into steps to make it easier to understand. The steps are as follows: | We'll understand how Perplexica works by taking an example of a scenario where a user asks: "How does an A.C. work?". We'll break down the process into steps to make it easier to understand. The steps are as follows: | ||||||
|  |  | ||||||
| 1. The message is sent via WS to the backend server where it invokes the chain. The chain will depend on your focus mode. For this example, let's assume we use the "webSearch" focus mode. | 1. The message is sent to the `/api/chat` route where it invokes the chain. The chain will depend on your focus mode. For this example, let's assume we use the "webSearch" focus mode. | ||||||
| 2. The chain is now invoked; first, the message is passed to another chain where it first predicts (using the chat history and the question) whether there is a need for sources and searching the web. If there is, it will generate a query (in accordance with the chat history) for searching the web that we'll take up later. If not, the chain will end there, and then the answer generator chain, also known as the response generator, will be started. | 2. The chain is now invoked; first, the message is passed to another chain where it first predicts (using the chat history and the question) whether there is a need for sources and searching the web. If there is, it will generate a query (in accordance with the chat history) for searching the web that we'll take up later. If not, the chain will end there, and then the answer generator chain, also known as the response generator, will be started. | ||||||
| 3. The query returned by the first chain is passed to SearXNG to search the web for information. | 3. The query returned by the first chain is passed to SearXNG to search the web for information. | ||||||
| 4. After the information is retrieved, it is based on keyword-based search. We then convert the information into embeddings and the query as well, then we perform a similarity search to find the most relevant sources to answer the query. | 4. After the information is retrieved, it is based on keyword-based search. We then convert the information into embeddings and the query as well, then we perform a similarity search to find the most relevant sources to answer the query. | ||||||
|   | |||||||
| @@ -1,109 +0,0 @@ | |||||||
| # Expose Perplexica to a network |  | ||||||
|  |  | ||||||
| This guide will show you how to make Perplexica available over a network. Follow these steps to allow computers on the same network to interact with Perplexica. Choose the instructions that match the operating system you are using. |  | ||||||
|  |  | ||||||
| ## Windows |  | ||||||
|  |  | ||||||
| 1. Open PowerShell as Administrator |  | ||||||
|  |  | ||||||
| 2. Navigate to the directory containing the `docker-compose.yaml` file |  | ||||||
|  |  | ||||||
| 3. Stop and remove the existing Perplexica containers and images: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose down --rmi all |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 4. Open the `docker-compose.yaml` file in a text editor like Notepad++ |  | ||||||
|  |  | ||||||
| 5. Replace `127.0.0.1` with the IP address of the server Perplexica is running on in these two lines: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    args: |  | ||||||
|      - NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api |  | ||||||
|      - NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001 |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 6. Save and close the `docker-compose.yaml` file |  | ||||||
|  |  | ||||||
| 7. Rebuild and restart the Perplexica container: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose up -d --build |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| ## macOS |  | ||||||
|  |  | ||||||
| 1. Open the Terminal application |  | ||||||
|  |  | ||||||
| 2. Navigate to the directory with the `docker-compose.yaml` file: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    cd /path/to/docker-compose.yaml |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 3. Stop and remove existing containers and images: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose down --rmi all |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 4. Open `docker-compose.yaml` in a text editor like Sublime Text: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    nano docker-compose.yaml |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 5. Replace `127.0.0.1` with the server IP in these lines: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    args: |  | ||||||
|      - NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api |  | ||||||
|      - NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001 |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 6. Save and exit the editor |  | ||||||
|  |  | ||||||
| 7. Rebuild and restart Perplexica: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose up -d --build |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| ## Linux |  | ||||||
|  |  | ||||||
| 1. Open the terminal |  | ||||||
|  |  | ||||||
| 2. Navigate to the `docker-compose.yaml` directory: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    cd /path/to/docker-compose.yaml |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 3. Stop and remove containers and images: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose down --rmi all |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 4. Edit `docker-compose.yaml`: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    nano docker-compose.yaml |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 5. Replace `127.0.0.1` with the server IP: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    args: |  | ||||||
|      - NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api |  | ||||||
|      - NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001 |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 6. Save and exit the editor |  | ||||||
|  |  | ||||||
| 7. Rebuild and restart Perplexica: |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    docker compose up -d --build |  | ||||||
|    ``` |  | ||||||
| @@ -39,11 +39,8 @@ To update Perplexica to the latest version, follow these steps: | |||||||
| 2. Navigate to the project directory. | 2. Navigate to the project directory. | ||||||
|  |  | ||||||
| 3. Check for changes in the configuration files. If the `sample.config.toml` file contains new fields, delete your existing `config.toml` file, rename `sample.config.toml` to `config.toml`, and update the configuration accordingly. | 3. Check for changes in the configuration files. If the `sample.config.toml` file contains new fields, delete your existing `config.toml` file, rename `sample.config.toml` to `config.toml`, and update the configuration accordingly. | ||||||
|  | 4. After populating the configuration run `npm i`. | ||||||
| 4. Execute `npm i` in both the `ui` folder and the root directory. | 5. Install the dependencies and then execute `npm run build`. | ||||||
|  | 6. Finally, start the app by running `npm run start` | ||||||
| 5. Once the packages are updated, execute `npm run build` in both the `ui` folder and the root directory. |  | ||||||
|  |  | ||||||
| 6. Finally, start both the frontend and the backend by running `npm run start` in both the `ui` folder and the root directory. |  | ||||||
|  |  | ||||||
| --- | --- | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| import { defineConfig } from 'drizzle-kit'; | import { defineConfig } from 'drizzle-kit'; | ||||||
|  | import path from 'path'; | ||||||
|  |  | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|   dialect: 'sqlite', |   dialect: 'sqlite', | ||||||
|   schema: './src/db/schema.ts', |   schema: './src/lib/db/schema.ts', | ||||||
|   out: './drizzle', |   out: './drizzle', | ||||||
|   dbCredentials: { |   dbCredentials: { | ||||||
|     url: './data/db.sqlite', |     url: path.join(process.cwd(), 'data', 'db.sqlite'), | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								drizzle/0000_fuzzy_randall.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,16 @@ | |||||||
|  | CREATE TABLE IF NOT EXISTS `chats` ( | ||||||
|  | 	`id` text PRIMARY KEY NOT NULL, | ||||||
|  | 	`title` text NOT NULL, | ||||||
|  | 	`createdAt` text NOT NULL, | ||||||
|  | 	`focusMode` text NOT NULL, | ||||||
|  | 	`files` text DEFAULT '[]' | ||||||
|  | ); | ||||||
|  | --> statement-breakpoint | ||||||
|  | CREATE TABLE IF NOT EXISTS `messages` ( | ||||||
|  | 	`id` integer PRIMARY KEY NOT NULL, | ||||||
|  | 	`content` text NOT NULL, | ||||||
|  | 	`chatId` text NOT NULL, | ||||||
|  | 	`messageId` text NOT NULL, | ||||||
|  | 	`type` text, | ||||||
|  | 	`metadata` text | ||||||
|  | ); | ||||||
							
								
								
									
										116
									
								
								drizzle/meta/0000_snapshot.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,116 @@ | |||||||
|  | { | ||||||
|  |   "version": "6", | ||||||
|  |   "dialect": "sqlite", | ||||||
|  |   "id": "ef3a044b-0f34-40b5-babb-2bb3a909ba27", | ||||||
|  |   "prevId": "00000000-0000-0000-0000-000000000000", | ||||||
|  |   "tables": { | ||||||
|  |     "chats": { | ||||||
|  |       "name": "chats", | ||||||
|  |       "columns": { | ||||||
|  |         "id": { | ||||||
|  |           "name": "id", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": true, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "title": { | ||||||
|  |           "name": "title", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "createdAt": { | ||||||
|  |           "name": "createdAt", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "focusMode": { | ||||||
|  |           "name": "focusMode", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "files": { | ||||||
|  |           "name": "files", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": false, | ||||||
|  |           "autoincrement": false, | ||||||
|  |           "default": "'[]'" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "indexes": {}, | ||||||
|  |       "foreignKeys": {}, | ||||||
|  |       "compositePrimaryKeys": {}, | ||||||
|  |       "uniqueConstraints": {}, | ||||||
|  |       "checkConstraints": {} | ||||||
|  |     }, | ||||||
|  |     "messages": { | ||||||
|  |       "name": "messages", | ||||||
|  |       "columns": { | ||||||
|  |         "id": { | ||||||
|  |           "name": "id", | ||||||
|  |           "type": "integer", | ||||||
|  |           "primaryKey": true, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "content": { | ||||||
|  |           "name": "content", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "chatId": { | ||||||
|  |           "name": "chatId", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "messageId": { | ||||||
|  |           "name": "messageId", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": true, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "type": { | ||||||
|  |           "name": "type", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": false, | ||||||
|  |           "autoincrement": false | ||||||
|  |         }, | ||||||
|  |         "metadata": { | ||||||
|  |           "name": "metadata", | ||||||
|  |           "type": "text", | ||||||
|  |           "primaryKey": false, | ||||||
|  |           "notNull": false, | ||||||
|  |           "autoincrement": false | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "indexes": {}, | ||||||
|  |       "foreignKeys": {}, | ||||||
|  |       "compositePrimaryKeys": {}, | ||||||
|  |       "uniqueConstraints": {}, | ||||||
|  |       "checkConstraints": {} | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "views": {}, | ||||||
|  |   "enums": {}, | ||||||
|  |   "_meta": { | ||||||
|  |     "schemas": {}, | ||||||
|  |     "tables": {}, | ||||||
|  |     "columns": {} | ||||||
|  |   }, | ||||||
|  |   "internal": { | ||||||
|  |     "indexes": {} | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								drizzle/meta/_journal.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | |||||||
|  | { | ||||||
|  |   "version": "7", | ||||||
|  |   "dialect": "sqlite", | ||||||
|  |   "entries": [ | ||||||
|  |     { | ||||||
|  |       "idx": 0, | ||||||
|  |       "version": "6", | ||||||
|  |       "when": 1748405503809, | ||||||
|  |       "tag": "0000_fuzzy_randall", | ||||||
|  |       "breakpoints": true | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								entrypoint.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | set -e | ||||||
|  |  | ||||||
|  | node migrate.js | ||||||
|  |  | ||||||
|  | exec node server.js | ||||||
							
								
								
									
										5
									
								
								next-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | |||||||
|  | /// <reference types="next" /> | ||||||
|  | /// <reference types="next/image-types/global" /> | ||||||
|  |  | ||||||
|  | // NOTE: This file should not be edited | ||||||
|  | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| /** @type {import('next').NextConfig} */ | /** @type {import('next').NextConfig} */ | ||||||
| const nextConfig = { | const nextConfig = { | ||||||
|  |   output: 'standalone', | ||||||
|   images: { |   images: { | ||||||
|     remotePatterns: [ |     remotePatterns: [ | ||||||
|       { |       { | ||||||
| @@ -7,6 +8,7 @@ const nextConfig = { | |||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
|  |   serverExternalPackages: ['pdf-parse'], | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default nextConfig; | export default nextConfig; | ||||||
							
								
								
									
										94
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -1,53 +1,69 @@ | |||||||
| { | { | ||||||
|   "name": "perplexica-backend", |   "name": "perplexica-frontend", | ||||||
|   "version": "1.10.0-rc3", |   "version": "1.11.0-rc2", | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "author": "ItzCrazyKns", |   "author": "ItzCrazyKns", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "npm run db:push && node dist/app.js", |     "dev": "next dev", | ||||||
|     "build": "tsc", |     "build": "npm run db:push && next build", | ||||||
|     "dev": "nodemon --ignore uploads/ src/app.ts ", |     "start": "next start", | ||||||
|     "db:push": "drizzle-kit push sqlite", |     "lint": "next lint", | ||||||
|     "format": "prettier . --check", |     "format:write": "prettier . --write", | ||||||
|     "format:write": "prettier . --write" |     "db:push": "drizzle-kit push" | ||||||
|   }, |  | ||||||
|   "devDependencies": { |  | ||||||
|     "@types/better-sqlite3": "^7.6.10", |  | ||||||
|     "@types/cors": "^2.8.17", |  | ||||||
|     "@types/express": "^4.17.21", |  | ||||||
|     "@types/html-to-text": "^9.0.4", |  | ||||||
|     "@types/multer": "^1.4.12", |  | ||||||
|     "@types/pdf-parse": "^1.1.4", |  | ||||||
|     "@types/readable-stream": "^4.0.11", |  | ||||||
|     "@types/ws": "^8.5.12", |  | ||||||
|     "drizzle-kit": "^0.22.7", |  | ||||||
|     "nodemon": "^3.1.0", |  | ||||||
|     "prettier": "^3.2.5", |  | ||||||
|     "ts-node": "^10.9.2", |  | ||||||
|     "typescript": "^5.4.3" |  | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@headlessui/react": "^2.2.0", | ||||||
|     "@iarna/toml": "^2.2.5", |     "@iarna/toml": "^2.2.5", | ||||||
|     "@langchain/anthropic": "^0.2.3", |     "@icons-pack/react-simple-icons": "^12.3.0", | ||||||
|     "@langchain/community": "^0.2.16", |     "@langchain/anthropic": "^0.3.24", | ||||||
|     "@langchain/openai": "^0.0.25", |     "@langchain/community": "^0.3.49", | ||||||
|     "@langchain/google-genai": "^0.0.23", |     "@langchain/core": "^0.3.66", | ||||||
|     "@xenova/transformers": "^2.17.1", |     "@langchain/google-genai": "^0.2.15", | ||||||
|     "axios": "^1.6.8", |     "@langchain/ollama": "^0.2.3", | ||||||
|     "better-sqlite3": "^11.0.0", |     "@langchain/openai": "^0.6.2", | ||||||
|  |     "@langchain/textsplitters": "^0.1.0", | ||||||
|  |     "@tailwindcss/typography": "^0.5.12", | ||||||
|  |     "@xenova/transformers": "^2.17.2", | ||||||
|  |     "axios": "^1.8.3", | ||||||
|  |     "better-sqlite3": "^11.9.1", | ||||||
|  |     "clsx": "^2.1.0", | ||||||
|     "compute-cosine-similarity": "^1.1.0", |     "compute-cosine-similarity": "^1.1.0", | ||||||
|     "compute-dot": "^1.1.0", |     "compute-dot": "^1.1.0", | ||||||
|     "cors": "^2.8.5", |     "drizzle-orm": "^0.40.1", | ||||||
|     "dotenv": "^16.4.5", |  | ||||||
|     "drizzle-orm": "^0.31.2", |  | ||||||
|     "express": "^4.19.2", |  | ||||||
|     "html-to-text": "^9.0.5", |     "html-to-text": "^9.0.5", | ||||||
|     "langchain": "^0.1.30", |     "jspdf": "^3.0.1", | ||||||
|     "mammoth": "^1.8.0", |     "langchain": "^0.3.30", | ||||||
|     "multer": "^1.4.5-lts.1", |     "lucide-react": "^0.363.0", | ||||||
|  |     "mammoth": "^1.9.1", | ||||||
|  |     "markdown-to-jsx": "^7.7.2", | ||||||
|  |     "next": "^15.2.2", | ||||||
|  |     "next-themes": "^0.3.0", | ||||||
|     "pdf-parse": "^1.1.1", |     "pdf-parse": "^1.1.1", | ||||||
|     "winston": "^3.13.0", |     "react": "^18", | ||||||
|     "ws": "^8.17.1", |     "react-dom": "^18", | ||||||
|  |     "react-text-to-speech": "^0.14.5", | ||||||
|  |     "react-textarea-autosize": "^8.5.3", | ||||||
|  |     "sonner": "^1.4.41", | ||||||
|  |     "tailwind-merge": "^2.2.2", | ||||||
|  |     "winston": "^3.17.0", | ||||||
|  |     "yet-another-react-lightbox": "^3.17.2", | ||||||
|     "zod": "^3.22.4" |     "zod": "^3.22.4" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@types/better-sqlite3": "^7.6.12", | ||||||
|  |     "@types/html-to-text": "^9.0.4", | ||||||
|  |     "@types/jspdf": "^2.0.0", | ||||||
|  |     "@types/node": "^20", | ||||||
|  |     "@types/pdf-parse": "^1.1.4", | ||||||
|  |     "@types/react": "^18", | ||||||
|  |     "@types/react-dom": "^18", | ||||||
|  |     "autoprefixer": "^10.0.1", | ||||||
|  |     "drizzle-kit": "^0.30.5", | ||||||
|  |     "eslint": "^8", | ||||||
|  |     "eslint-config-next": "14.1.4", | ||||||
|  |     "postcss": "^8", | ||||||
|  |     "prettier": "^3.2.5", | ||||||
|  |     "tailwindcss": "^3.3.0", | ||||||
|  |     "typescript": "^5" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/icon-100.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 916 B | 
							
								
								
									
										
											BIN
										
									
								
								public/icon-50.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 515 B | 
							
								
								
									
										
											BIN
										
									
								
								public/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 30 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/screenshots/p1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 183 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/screenshots/p1_small.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 130 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/screenshots/p2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 627 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/screenshots/p2_small.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 202 KiB | 
| Before Width: | Height: | Size: 629 B After Width: | Height: | Size: 629 B | 
							
								
								
									
										131
									
								
								public/weather-ico/clear-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,131 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.34167" y="-.34167" width="1.6833" height="1.85"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-sun-shiny { | ||||||
|  |         0% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           stroke-dasharray: 0.1px 10px; | ||||||
|  |           stroke-dashoffset: -1px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun-shiny line { | ||||||
|  |         -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |         -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |         -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |         animation-name: am-weather-sun-shiny; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 4.9 KiB | 
							
								
								
									
										159
									
								
								public/weather-ico/clear-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,159 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.3038" y="-.3318" width="1.6076" height="1.894"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g id="night" transform="translate(-4,-18)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .78534 36 20.022)" stroke-width="1.2616"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5" fill="#ffa500" stroke-miterlimit="10" | ||||||
|  |           stroke-width="1.4105" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" stroke-width="1.4105" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2.5232" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 6.7 KiB | 
							
								
								
									
										178
									
								
								public/weather-ico/cloudy-1-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,178 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.28472" width="1.403" height="1.6944"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-sun-shiny { | ||||||
|  |         0% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           stroke-dasharray: 0.1px 10px; | ||||||
|  |           stroke-dashoffset: -1px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun-shiny line { | ||||||
|  |         -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |         -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |         -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |         animation-name: am-weather-sun-shiny; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-2" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#c6deff" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 6.8 KiB | 
							
								
								
									
										206
									
								
								public/weather-ico/cloudy-1-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,206 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.19471" y="-.26087" width="1.3744" height="1.6884"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-2" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#c6deff" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 8.6 KiB | 
							
								
								
									
										244
									
								
								public/weather-ico/fog-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,244 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** FOG | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-fog-1 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           transform: translate(7px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-1 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-1; | ||||||
|  |         -moz-animation-name: am-weather-fog-1; | ||||||
|  |         -ms-animation-name: am-weather-fog-1; | ||||||
|  |         animation-name: am-weather-fog-1; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-2 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         21.05% { | ||||||
|  |           transform: translate(-6px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         78.95% { | ||||||
|  |           transform: translate(9px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-2 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-2; | ||||||
|  |         -moz-animation-name: am-weather-fog-2; | ||||||
|  |         -ms-animation-name: am-weather-fog-2; | ||||||
|  |         animation-name: am-weather-fog-2; | ||||||
|  |         -webkit-animation-duration: 20s; | ||||||
|  |         -moz-animation-duration: 20s; | ||||||
|  |         -ms-animation-duration: 20s; | ||||||
|  |         animation-duration: 20s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-3 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         25% { | ||||||
|  |           transform: translate(4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         75% { | ||||||
|  |           transform: translate(-4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-3 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-3; | ||||||
|  |         -moz-animation-name: am-weather-fog-3; | ||||||
|  |         -ms-animation-name: am-weather-fog-3; | ||||||
|  |         animation-name: am-weather-fog-3; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-4 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           transform: translate(-4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-4 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-4; | ||||||
|  |         -moz-animation-name: am-weather-fog-4; | ||||||
|  |         -ms-animation-name: am-weather-fog-4; | ||||||
|  |         animation-name: am-weather-fog-4; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" transform="translate(0,16)"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" />F | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffc04a" stroke="#ffc04a" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-fog" transform="translate(-10,20)" fill="none" stroke="#c6deff" stroke-linecap="round" | ||||||
|  |       stroke-width="2"> | ||||||
|  |       <line class="am-weather-fog-1" y1="0" y2="0" x1="1" x2="37" stroke-dasharray="3, 5, 17, 5, 7" /> | ||||||
|  |       <line class="am-weather-fog-2" y1="5" y2="5" x1="9" x2="33" stroke-dasharray="11, 7, 15" /> | ||||||
|  |       <line class="am-weather-fog-3" y1="10" y2="10" x1="5" x2="40" stroke-dasharray="11, 7, 3, 5, 9" /> | ||||||
|  |       <line class="am-weather-fog-4" y1="15" y2="15" x1="7" x2="42" stroke-dasharray="13, 5, 9, 5, 3" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 8.0 KiB | 
							
								
								
									
										309
									
								
								public/weather-ico/fog-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,309 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** FOG | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-fog-1 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           transform: translate(7px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-1 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-1; | ||||||
|  |         -moz-animation-name: am-weather-fog-1; | ||||||
|  |         -ms-animation-name: am-weather-fog-1; | ||||||
|  |         animation-name: am-weather-fog-1; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-2 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         21.05% { | ||||||
|  |           transform: translate(-6px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         78.95% { | ||||||
|  |           transform: translate(9px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-2 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-2; | ||||||
|  |         -moz-animation-name: am-weather-fog-2; | ||||||
|  |         -ms-animation-name: am-weather-fog-2; | ||||||
|  |         animation-name: am-weather-fog-2; | ||||||
|  |         -webkit-animation-duration: 20s; | ||||||
|  |         -moz-animation-duration: 20s; | ||||||
|  |         -ms-animation-duration: 20s; | ||||||
|  |         animation-duration: 20s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-3 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         25% { | ||||||
|  |           transform: translate(4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         75% { | ||||||
|  |           transform: translate(-4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-3 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-3; | ||||||
|  |         -moz-animation-name: am-weather-fog-3; | ||||||
|  |         -ms-animation-name: am-weather-fog-3; | ||||||
|  |         animation-name: am-weather-fog-3; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-fog-4 { | ||||||
|  |         0% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           transform: translate(-4px, 0px) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           transform: translate(0px, 0px) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-fog-4 { | ||||||
|  |         -webkit-animation-name: am-weather-fog-4; | ||||||
|  |         -moz-animation-name: am-weather-fog-4; | ||||||
|  |         -ms-animation-name: am-weather-fog-4; | ||||||
|  |         animation-name: am-weather-fog-4; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffc04a" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffc04a" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffc04a" stroke="#ffc04a" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-fog" transform="translate(-10,20)" fill="none" stroke="#c6deff" stroke-linecap="round" | ||||||
|  |       stroke-width="2"> | ||||||
|  |       <line class="am-weather-fog-1" y1="0" y2="0" x1="1" x2="37" stroke-dasharray="3, 5, 17, 5, 7" /> | ||||||
|  |       <line class="am-weather-fog-2" y1="5" y2="5" x1="9" x2="33" stroke-dasharray="11, 7, 15" /> | ||||||
|  |       <line class="am-weather-fog-3" y1="10" y2="10" x1="5" x2="40" stroke-dasharray="11, 7, 3, 5, 9" /> | ||||||
|  |       <line class="am-weather-fog-4" y1="15" y2="15" x1="7" x2="42" stroke-dasharray="13, 5, 9, 5, 3" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										204
									
								
								public/weather-ico/frost-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,204 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** FROST | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-frost { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         1% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         3% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         5% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         7% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         9% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         11% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         13% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         15% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         16% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-frost { | ||||||
|  |         -webkit-animation-name: am-weather-frost; | ||||||
|  |         -moz-animation-name: am-weather-frost; | ||||||
|  |         animation-name: am-weather-frost; | ||||||
|  |         -webkit-animation-duration: 1.11s; | ||||||
|  |         -moz-animation-duration: 1.11s; | ||||||
|  |         animation-duration: 1.11s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" transform="translate(0,16)"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" />F | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffc04a" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffc04a" stroke="#ffc04a" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g transform="translate(-16,4)"> | ||||||
|  |       <g class="am-weather-frost" stroke="#57a0ee" transform="translate(0,2)" fill="none" stroke-width="2" | ||||||
|  |         stroke-linecap="round" | ||||||
|  |         style="-moz-animation-duration:1.11s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-frost;-moz-animation-timing-function:linear;-webkit-animation-duration:1.11s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-frost;-webkit-animation-timing-function:linear"> | ||||||
|  |         <path d="M11,32H45" /> | ||||||
|  |         <path d="M15.5,37H40.5" /> | ||||||
|  |         <path d="M22.5,42H33.5" /> | ||||||
|  |       </g> | ||||||
|  |       <g> | ||||||
|  |         <path stroke="#57a0ee" transform="translate(0,0)" fill="none" stroke-width="2" stroke-linecap="round" | ||||||
|  |           d="M28,31V9M28,22l11,-3.67M34,20l2,-4M34,20l4,2M28,22l-11,-3.67M22,20l-2,-4M22,20l-4,2M28,14.27l3.01,-3.02M28,14.27l-3.01,-3.02" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 7.3 KiB | 
							
								
								
									
										269
									
								
								public/weather-ico/frost-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,269 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** FROST | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-frost { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         1% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         3% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         5% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         7% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         9% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         11% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         13% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         15% { | ||||||
|  |           -webkit-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(-0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(-0.3px, 0.0px); | ||||||
|  |           transform: translate(-0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         16% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-frost { | ||||||
|  |         -webkit-animation-name: am-weather-frost; | ||||||
|  |         -moz-animation-name: am-weather-frost; | ||||||
|  |         animation-name: am-weather-frost; | ||||||
|  |         -webkit-animation-duration: 1.11s; | ||||||
|  |         -moz-animation-duration: 1.11s; | ||||||
|  |         animation-duration: 1.11s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffc04a" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffc04a" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffc04a" stroke="#ffc04a" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g transform="translate(-16,4)"> | ||||||
|  |       <g class="am-weather-frost" stroke="#57a0ee" transform="translate(0,2)" fill="none" stroke-width="2" | ||||||
|  |         stroke-linecap="round" | ||||||
|  |         style="-moz-animation-duration:1.11s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-frost;-moz-animation-timing-function:linear;-webkit-animation-duration:1.11s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-frost;-webkit-animation-timing-function:linear"> | ||||||
|  |         <path d="M11,32H45" /> | ||||||
|  |         <path d="M15.5,37H40.5" /> | ||||||
|  |         <path d="M22.5,42H33.5" /> | ||||||
|  |       </g> | ||||||
|  |       <g> | ||||||
|  |         <path stroke="#57a0ee" transform="translate(0,0)" fill="none" stroke-width="2" stroke-linecap="round" | ||||||
|  |           d="M28,31V9M28,22l11,-3.67M34,20l2,-4M34,20l4,2M28,22l-11,-3.67M22,20l-2,-4M22,20l-4,2M28,14.27l3.01,-3.02M28,14.27l-3.01,-3.02" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										141
									
								
								public/weather-ico/rain-and-sleet-mix.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,141 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <!-- Mix of Rain and Sleet | Contributed by hsoJ95 on GitHub: https://github.com/hsoj95 --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.24684" y="-.22776" width="1.4937" height="1.5756"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-2 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-delay: 0.25s; | ||||||
|  |         -moz-animation-delay: 0.25s; | ||||||
|  |         -ms-animation-delay: 0.25s; | ||||||
|  |         animation-delay: 0.25s; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-3 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-3 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-3; | ||||||
|  |         -moz-animation-name: am-weather-cloud-3; | ||||||
|  |         animation-name: am-weather-cloud-3; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-3;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-3;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-sleet-2" transform="translate(-20,-10) rotate(10,-247.39,200.17)" fill="none" stroke="#91c0f8" | ||||||
|  |       stroke-linecap="round"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-5,1)" y2="8" stroke-dasharray="0.1, 7" stroke-width="2" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(5)" y2="8" stroke-dasharray="0.1, 7" stroke-width="2" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-rain-3" transform="translate(-20,-10) rotate(10,-245.89,217.31)" fill="none" stroke="#91c0f8" | ||||||
|  |       stroke-dasharray="4, 7" stroke-linecap="round" stroke-width="2"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-13,1)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-3,2)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-2" transform="translate(7,-1)" y2="8" | ||||||
|  |         style="-moz-animation-delay:0.25s;-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-delay:0.25s;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-delay:0.25s;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 7.6 KiB | 
							
								
								
									
										179
									
								
								public/weather-ico/rainy-1-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,179 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-rain-1" transform="translate(-20,-10) rotate(10,-238.68,233.96)"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-6,1)" y2="8" fill="none" stroke="#91c0f8" | ||||||
|  |         stroke-dasharray="4, 7" stroke-linecap="round" stroke-width="2" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 7.4 KiB | 
							
								
								
									
										243
									
								
								public/weather-ico/rainy-1-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,243 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.21122" width="1.403" height="1.4997"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weaher-rain-1" transform="translate(-20,-10) rotate(10,-238.68,233.96)"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-6,1)" y2="8" fill="none" stroke="#91c0f8" | ||||||
|  |         stroke-dasharray="4, 7" stroke-linecap="round" stroke-width="2" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										204
									
								
								public/weather-ico/rainy-2-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,204 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.20592" width="1.403" height="1.4872"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-2 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-delay: 0.25s; | ||||||
|  |         -moz-animation-delay: 0.25s; | ||||||
|  |         -ms-animation-delay: 0.25s; | ||||||
|  |         animation-delay: 0.25s; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" stroke="#ffa500" stroke-linecap="round" stroke-width="2" fifll="none" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g transform="translate(-20,-10) rotate(10,-245.89,217.31)" fill="none" stroke="#91c0f8" stroke-dasharray="4, 7" stroke-linecap="round" | ||||||
|  |       stroke-width="2"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-6,1)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-2" transform="translate(0,-1)" y2="8" | ||||||
|  |         style="-moz-animation-delay:0.25s;-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-delay:0.25s;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-delay:0.25s;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										256
									
								
								public/weather-ico/rainy-2-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,256 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-2 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-delay: 0.25s; | ||||||
|  |         -moz-animation-delay: 0.25s; | ||||||
|  |         -ms-animation-delay: 0.25s; | ||||||
|  |         animation-delay: 0.25s; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g class="layer" transform="translate(16,-2)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-rain-2" transform="translate(-20,-10) rotate(10,34,46)" fill="none" stroke="#91c0f8" | ||||||
|  |       stroke-dasharray="4, 7" stroke-linecap="round" stroke-width="2"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-6,1)" x1="34" x2="34" y1="46" y2="54" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-2" transform="translate(0,-1)" x1="34" x2="34" y1="46" y2="54" | ||||||
|  |         style="-moz-animation-delay:0.25s;-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-delay:0.25s;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-delay:0.25s;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										206
									
								
								public/weather-ico/rainy-3-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,206 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.24684" y="-.22892" width="1.4937" height="1.5576"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-2 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-delay: 0.25s; | ||||||
|  |         -moz-animation-delay: 0.25s; | ||||||
|  |         -ms-animation-delay: 0.25s; | ||||||
|  |         animation-delay: 0.25s; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" stroke="#ffa500" stroke-linecap="round" stroke-width="2" fifll="none" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g transform="translate(-20,-10) rotate(10,-247.39,200.17)" fill="none" stroke="#91c0f8" stroke-dasharray="4, 4" | ||||||
|  |       stroke-linecap="round" stroke-width="2"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-4,1)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-2" transform="translate(0,-1)" y2="8" | ||||||
|  |         style="-moz-animation-delay:0.25s;-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-delay:0.25s;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-delay:0.25s;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(4)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 9.3 KiB | 
							
								
								
									
										270
									
								
								public/weather-ico/rainy-3-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,270 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.24684" y="-.22892" width="1.4937" height="1.5576"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** RAIN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-rain { | ||||||
|  |         0% { | ||||||
|  |           stroke-dashoffset: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dashoffset: -100; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-1 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-rain-2 { | ||||||
|  |         -webkit-animation-name: am-weather-rain; | ||||||
|  |         -moz-animation-name: am-weather-rain; | ||||||
|  |         -ms-animation-name: am-weather-rain; | ||||||
|  |         animation-name: am-weather-rain; | ||||||
|  |         -webkit-animation-delay: 0.25s; | ||||||
|  |         -moz-animation-delay: 0.25s; | ||||||
|  |         -ms-animation-delay: 0.25s; | ||||||
|  |         animation-delay: 0.25s; | ||||||
|  |         -webkit-animation-duration: 8s; | ||||||
|  |         -moz-animation-duration: 8s; | ||||||
|  |         -ms-animation-duration: 8s; | ||||||
|  |         animation-duration: 8s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g transform="translate(-20,-10) rotate(10,-247.39,200.17)" fill="none" stroke="#91c0f8" stroke-dasharray="4, 4" | ||||||
|  |       stroke-linecap="round" stroke-width="2"> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(-4,1)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-2" transform="translate(0,-1)" y2="8" | ||||||
|  |         style="-moz-animation-delay:0.25s;-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-delay:0.25s;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-delay:0.25s;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |       <line class="am-weather-rain-1" transform="translate(4)" y2="8" | ||||||
|  |         style="-moz-animation-duration:8s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-rain;-moz-animation-timing-function:linear;-ms-animation-duration:8s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-rain;-ms-animation-timing-function:linear;-webkit-animation-duration:8s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-rain;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										374
									
								
								public/weather-ico/scattered-thunderstorms-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,374 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <!-- Scattered Thunderstorms | Contributed by hsoJ95 on GitHub: https://github.com/hsoj95 --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.1975" width="1.403" height="1.4766"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-3 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(-5px, 0px); | ||||||
|  |           -moz-transform: translate(-5px, 0px); | ||||||
|  |           -ms-transform: translate(-5px, 0px); | ||||||
|  |           transform: translate(-5px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(10px, 0px); | ||||||
|  |           -moz-transform: translate(10px, 0px); | ||||||
|  |           -ms-transform: translate(10px, 0px); | ||||||
|  |           transform: translate(10px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(-5px, 0px); | ||||||
|  |           -moz-transform: translate(-5px, 0px); | ||||||
|  |           -ms-transform: translate(-5px, 0px); | ||||||
|  |           transform: translate(-5px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-3 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-3; | ||||||
|  |         -moz-animation-name: am-weather-cloud-3; | ||||||
|  |         animation-name: am-weather-cloud-3; | ||||||
|  |         -webkit-animation-duration: 7s; | ||||||
|  |         -moz-animation-duration: 7s; | ||||||
|  |         animation-duration: 7s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-sun-shiny { | ||||||
|  |         0% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           stroke-dasharray: 0.1px 10px; | ||||||
|  |           stroke-dashoffset: -1px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun-shiny line { | ||||||
|  |         -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |         -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |         -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |         animation-name: am-weather-sun-shiny; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** STROKE | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-stroke { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         2% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         4% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         6% { | ||||||
|  |           -webkit-transform: translate(0.5px, 0.4px); | ||||||
|  |           -moz-transform: translate(0.5px, 0.4px); | ||||||
|  |           -ms-transform: translate(0.5px, 0.4px); | ||||||
|  |           transform: translate(0.5px, 0.4px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         8% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         10% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         12% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         14% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         16% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         18% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         20% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         22% { | ||||||
|  |           -webkit-transform: translate(1px, 0.0px); | ||||||
|  |           -moz-transform: translate(1px, 0.0px); | ||||||
|  |           -ms-transform: translate(1px, 0.0px); | ||||||
|  |           transform: translate(1px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         24% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         26% { | ||||||
|  |           -webkit-transform: translate(-1px, 0.0px); | ||||||
|  |           -moz-transform: translate(-1px, 0.0px); | ||||||
|  |           -ms-transform: translate(-1px, 0.0px); | ||||||
|  |           transform: translate(-1px, 0.0px); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         28% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         40% { | ||||||
|  |           fill: orange; | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         65% { | ||||||
|  |           fill: white; | ||||||
|  |           -webkit-transform: translate(-1px, 5.0px); | ||||||
|  |           -moz-transform: translate(-1px, 5.0px); | ||||||
|  |           -ms-transform: translate(-1px, 5.0px); | ||||||
|  |           transform: translate(-1px, 5.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         61% { | ||||||
|  |           fill: orange; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-stroke { | ||||||
|  |         -webkit-animation-name: am-weather-stroke; | ||||||
|  |         -moz-animation-name: am-weather-stroke; | ||||||
|  |         animation-name: am-weather-stroke; | ||||||
|  |         -webkit-animation-duration: 1.11s; | ||||||
|  |         -moz-animation-duration: 1.11s; | ||||||
|  |         animation-duration: 1.11s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g id="thunder" transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-lightning" transform="matrix(1.2,0,0,1.2,-4,28)"> | ||||||
|  |       <polygon class="am-weather-stroke" points="11.1 6.9 14.3 -2.9 20.5 -2.9 16.4 4.3 20.3 4.3 11.5 14.6 14.9 6.9" | ||||||
|  |         fill="#ffa500" stroke="#fff" | ||||||
|  |         style="-moz-animation-duration:1.11s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-stroke;-moz-animation-timing-function:linear;-webkit-animation-duration:1.11s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-stroke;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 13 KiB | 
							
								
								
									
										283
									
								
								public/weather-ico/scattered-thunderstorms-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,283 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <!-- Scattered Thunderstorms | Contributed by hsoJ95 on GitHub: https://github.com/hsoj95 --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.1975" width="1.403" height="1.4766"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-3 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(-5px, 0px); | ||||||
|  |           -moz-transform: translate(-5px, 0px); | ||||||
|  |           -ms-transform: translate(-5px, 0px); | ||||||
|  |           transform: translate(-5px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(10px, 0px); | ||||||
|  |           -moz-transform: translate(10px, 0px); | ||||||
|  |           -ms-transform: translate(10px, 0px); | ||||||
|  |           transform: translate(10px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(-5px, 0px); | ||||||
|  |           -moz-transform: translate(-5px, 0px); | ||||||
|  |           -ms-transform: translate(-5px, 0px); | ||||||
|  |           transform: translate(-5px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-3 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-3; | ||||||
|  |         -moz-animation-name: am-weather-cloud-3; | ||||||
|  |         animation-name: am-weather-cloud-3; | ||||||
|  |         -webkit-animation-duration: 7s; | ||||||
|  |         -moz-animation-duration: 7s; | ||||||
|  |         animation-duration: 7s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** STROKE | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-stroke { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         2% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         4% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         6% { | ||||||
|  |           -webkit-transform: translate(0.5px, 0.4px); | ||||||
|  |           -moz-transform: translate(0.5px, 0.4px); | ||||||
|  |           -ms-transform: translate(0.5px, 0.4px); | ||||||
|  |           transform: translate(0.5px, 0.4px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         8% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         10% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         12% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         14% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         16% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         18% { | ||||||
|  |           -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |           transform: translate(0.3px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         20% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         22% { | ||||||
|  |           -webkit-transform: translate(1px, 0.0px); | ||||||
|  |           -moz-transform: translate(1px, 0.0px); | ||||||
|  |           -ms-transform: translate(1px, 0.0px); | ||||||
|  |           transform: translate(1px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         24% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         26% { | ||||||
|  |           -webkit-transform: translate(-1px, 0.0px); | ||||||
|  |           -moz-transform: translate(-1px, 0.0px); | ||||||
|  |           -ms-transform: translate(-1px, 0.0px); | ||||||
|  |           transform: translate(-1px, 0.0px); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         28% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         40% { | ||||||
|  |           fill: orange; | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         65% { | ||||||
|  |           fill: white; | ||||||
|  |           -webkit-transform: translate(-1px, 5.0px); | ||||||
|  |           -moz-transform: translate(-1px, 5.0px); | ||||||
|  |           -ms-transform: translate(-1px, 5.0px); | ||||||
|  |           transform: translate(-1px, 5.0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         61% { | ||||||
|  |           fill: orange; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |           -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |           -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |           transform: translate(0.0px, 0.0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-stroke { | ||||||
|  |         -webkit-animation-name: am-weather-stroke; | ||||||
|  |         -moz-animation-name: am-weather-stroke; | ||||||
|  |         animation-name: am-weather-stroke; | ||||||
|  |         -webkit-animation-duration: 1.11s; | ||||||
|  |         -moz-animation-duration: 1.11s; | ||||||
|  |         animation-duration: 1.11s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g id="thunder" transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="3.3 1.5 4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="3.3 1.5 4 2.7 5.2 3.3 4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-lightning" transform="matrix(1.2,0,0,1.2,-4,28)"> | ||||||
|  |       <polygon class="am-weather-stroke" points="11.1 6.9 14.3 -2.9 20.5 -2.9 16.4 4.3 20.3 4.3 11.5 14.6 14.9 6.9" | ||||||
|  |         fill="#ffa500" stroke="#fff" | ||||||
|  |         style="-moz-animation-duration:1.11s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-stroke;-moz-animation-timing-function:linear;-webkit-animation-duration:1.11s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-stroke;-webkit-animation-timing-function:linear" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										307
									
								
								public/weather-ico/severe-thunderstorm.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,307 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <!-- Severe Thunderstorm | Contributed by hsoJ95 on GitHub: https://github.com/hsoj95 --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <defs> | ||||||
|  |         <filter id="blur" x="-.17571" y="-.19575" width="1.3379" height="1.4959"> | ||||||
|  |             <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |             <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |             <feComponentTransfer> | ||||||
|  |                 <feFuncA slope="0.05" type="linear" /> | ||||||
|  |             </feComponentTransfer> | ||||||
|  |             <feMerge> | ||||||
|  |                 <feMergeNode /> | ||||||
|  |                 <feMergeNode in="SourceGraphic" /> | ||||||
|  |             </feMerge> | ||||||
|  |         </filter> | ||||||
|  |         <style type="text/css"> | ||||||
|  |             <![CDATA[ | ||||||
|  |             /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |             @keyframes am-weather-cloud-3 { | ||||||
|  |                 0% { | ||||||
|  |                     -webkit-transform: translate(-5px, 0px); | ||||||
|  |                     -moz-transform: translate(-5px, 0px); | ||||||
|  |                     -ms-transform: translate(-5px, 0px); | ||||||
|  |                     transform: translate(-5px, 0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 50% { | ||||||
|  |                     -webkit-transform: translate(10px, 0px); | ||||||
|  |                     -moz-transform: translate(10px, 0px); | ||||||
|  |                     -ms-transform: translate(10px, 0px); | ||||||
|  |                     transform: translate(10px, 0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 100% { | ||||||
|  |                     -webkit-transform: translate(-5px, 0px); | ||||||
|  |                     -moz-transform: translate(-5px, 0px); | ||||||
|  |                     -ms-transform: translate(-5px, 0px); | ||||||
|  |                     transform: translate(-5px, 0px); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             .am-weather-cloud-3 { | ||||||
|  |                 -webkit-animation-name: am-weather-cloud-3; | ||||||
|  |                 -moz-animation-name: am-weather-cloud-3; | ||||||
|  |                 animation-name: am-weather-cloud-3; | ||||||
|  |                 -webkit-animation-duration: 7s; | ||||||
|  |                 -moz-animation-duration: 7s; | ||||||
|  |                 animation-duration: 7s; | ||||||
|  |                 -webkit-animation-timing-function: linear; | ||||||
|  |                 -moz-animation-timing-function: linear; | ||||||
|  |                 animation-timing-function: linear; | ||||||
|  |                 -webkit-animation-iteration-count: infinite; | ||||||
|  |                 -moz-animation-iteration-count: infinite; | ||||||
|  |                 animation-iteration-count: infinite; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @keyframes am-weather-cloud-1 { | ||||||
|  |                 0% { | ||||||
|  |                     -webkit-transform: translate(0px, 0px); | ||||||
|  |                     -moz-transform: translate(0px, 0px); | ||||||
|  |                     -ms-transform: translate(0px, 0px); | ||||||
|  |                     transform: translate(0px, 0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 50% { | ||||||
|  |                     -webkit-transform: translate(2px, 0px); | ||||||
|  |                     -moz-transform: translate(2px, 0px); | ||||||
|  |                     -ms-transform: translate(2px, 0px); | ||||||
|  |                     transform: translate(2px, 0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 100% { | ||||||
|  |                     -webkit-transform: translate(0px, 0px); | ||||||
|  |                     -moz-transform: translate(0px, 0px); | ||||||
|  |                     -ms-transform: translate(0px, 0px); | ||||||
|  |                     transform: translate(0px, 0px); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             .am-weather-cloud-1 { | ||||||
|  |                 -webkit-animation-name: am-weather-cloud-1; | ||||||
|  |                 -moz-animation-name: am-weather-cloud-1; | ||||||
|  |                 animation-name: am-weather-cloud-1; | ||||||
|  |                 -webkit-animation-duration: 3s; | ||||||
|  |                 -moz-animation-duration: 3s; | ||||||
|  |                 animation-duration: 3s; | ||||||
|  |                 -webkit-animation-timing-function: linear; | ||||||
|  |                 -moz-animation-timing-function: linear; | ||||||
|  |                 animation-timing-function: linear; | ||||||
|  |                 -webkit-animation-iteration-count: infinite; | ||||||
|  |                 -moz-animation-iteration-count: infinite; | ||||||
|  |                 animation-iteration-count: infinite; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             /* | ||||||
|  | ** STROKE | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |             @keyframes am-weather-stroke { | ||||||
|  |                 0% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 2% { | ||||||
|  |                     -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |                     transform: translate(0.3px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 4% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 6% { | ||||||
|  |                     -webkit-transform: translate(0.5px, 0.4px); | ||||||
|  |                     -moz-transform: translate(0.5px, 0.4px); | ||||||
|  |                     -ms-transform: translate(0.5px, 0.4px); | ||||||
|  |                     transform: translate(0.5px, 0.4px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 8% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 10% { | ||||||
|  |                     -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |                     transform: translate(0.3px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 12% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 14% { | ||||||
|  |                     -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |                     transform: translate(0.3px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 16% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 18% { | ||||||
|  |                     -webkit-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.3px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.3px, 0.0px); | ||||||
|  |                     transform: translate(0.3px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 20% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 22% { | ||||||
|  |                     -webkit-transform: translate(1px, 0.0px); | ||||||
|  |                     -moz-transform: translate(1px, 0.0px); | ||||||
|  |                     -ms-transform: translate(1px, 0.0px); | ||||||
|  |                     transform: translate(1px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 24% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 26% { | ||||||
|  |                     -webkit-transform: translate(-1px, 0.0px); | ||||||
|  |                     -moz-transform: translate(-1px, 0.0px); | ||||||
|  |                     -ms-transform: translate(-1px, 0.0px); | ||||||
|  |                     transform: translate(-1px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 28% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 40% { | ||||||
|  |                     fill: orange; | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 65% { | ||||||
|  |                     fill: white; | ||||||
|  |                     -webkit-transform: translate(-1px, 5.0px); | ||||||
|  |                     -moz-transform: translate(-1px, 5.0px); | ||||||
|  |                     -ms-transform: translate(-1px, 5.0px); | ||||||
|  |                     transform: translate(-1px, 5.0px); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 61% { | ||||||
|  |                     fill: orange; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 100% { | ||||||
|  |                     -webkit-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -moz-transform: translate(0.0px, 0.0px); | ||||||
|  |                     -ms-transform: translate(0.0px, 0.0px); | ||||||
|  |                     transform: translate(0.0px, 0.0px); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             .am-weather-stroke { | ||||||
|  |                 -webkit-animation-name: am-weather-stroke; | ||||||
|  |                 -moz-animation-name: am-weather-stroke; | ||||||
|  |                 animation-name: am-weather-stroke; | ||||||
|  |                 -webkit-animation-duration: 1.11s; | ||||||
|  |                 -moz-animation-duration: 1.11s; | ||||||
|  |                 animation-duration: 1.11s; | ||||||
|  |                 -webkit-animation-timing-function: linear; | ||||||
|  |                 -moz-animation-timing-function: linear; | ||||||
|  |                 animation-timing-function: linear; | ||||||
|  |                 -webkit-animation-iteration-count: infinite; | ||||||
|  |                 -moz-animation-iteration-count: infinite; | ||||||
|  |                 animation-iteration-count: infinite; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @keyframes error { | ||||||
|  |                 0% { | ||||||
|  |                     fill: #cc0000; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 50% { | ||||||
|  |                     fill: #ff0000; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 100% { | ||||||
|  |                     fill: #cc0000; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             #Shape { | ||||||
|  |                 -webkit-animation-name: error; | ||||||
|  |                 -moz-animation-name: error; | ||||||
|  |                 animation-name: error; | ||||||
|  |                 -webkit-animation-duration: 1s; | ||||||
|  |                 -moz-animation-duration: 1s; | ||||||
|  |                 animation-duration: 1s; | ||||||
|  |                 -webkit-animation-timing-function: linear; | ||||||
|  |                 -moz-animation-timing-function: linear; | ||||||
|  |                 animation-timing-function: linear; | ||||||
|  |                 -webkit-animation-iteration-count: infinite; | ||||||
|  |                 -moz-animation-iteration-count: infinite; | ||||||
|  |                 animation-iteration-count: infinite; | ||||||
|  |             } | ||||||
|  |             ]]> | ||||||
|  |         </style> | ||||||
|  |     </defs> | ||||||
|  |     <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |         <g class="am-weather-cloud-1" | ||||||
|  |             style="-moz-animation-duration:7s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-1;-moz-animation-timing-function:linear;-webkit-animation-duration:7s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-1;-webkit-animation-timing-function:linear"> | ||||||
|  |             <path transform="matrix(.6 0 0 .6 -10 -6)" | ||||||
|  |                 d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |                 fill="#666" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |         </g> | ||||||
|  |         <g class="am-weather-cloud-3"> | ||||||
|  |             <path transform="translate(-20,-11)" | ||||||
|  |                 d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |                 fill="#333" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="matrix(1.2,0,0,1.2,-4,28)"> | ||||||
|  |             <polygon class="am-weather-stroke" | ||||||
|  |                 points="11.1 6.9 14.3 -2.9 20.5 -2.9 16.4 4.3 20.3 4.3 11.5 14.6 14.9 6.9" fill="#ffa500" stroke="#fff" | ||||||
|  |                 style="-moz-animation-duration:1.11s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-stroke;-moz-animation-timing-function:linear;-webkit-animation-duration:1.11s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-stroke;-webkit-animation-timing-function:linear" /> | ||||||
|  |         </g> | ||||||
|  |         <g class="warning" transform="translate(20,30)"> | ||||||
|  |             <path | ||||||
|  |                 d="m7.7791 2.906-5.9912 10.117c-0.56283 0.95042-0.24862 2.1772 0.7018 2.74 0.30853 0.18271 0.66051 0.27911 1.0191 0.27911h11.982c1.1046 0 2-0.89543 2-2 0-0.35857-0.0964-0.71056-0.27911-1.0191l-5.9912-10.117c-0.56283-0.95042-1.7896-1.2646-2.74-0.7018-0.28918 0.17125-0.53055 0.41262-0.7018 0.7018z" | ||||||
|  |                 fill="#c00" /> | ||||||
|  |             <path d="m9.5 10.5v-5" stroke="#fff" stroke-linecap="round" stroke-width="1.5" /> | ||||||
|  |             <circle cx="9.5" cy="13" r="1" fill="#fff" /> | ||||||
|  |         </g> | ||||||
|  |     </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 13 KiB | 
							
								
								
									
										241
									
								
								public/weather-ico/snowy-1-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,241 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.23099" width="1.403" height="1.5634"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-sun-shiny { | ||||||
|  |         0% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           stroke-dasharray: 0.1px 10px; | ||||||
|  |           stroke-dashoffset: -1px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun-shiny line { | ||||||
|  |         -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |         -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |         -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |         animation-name: am-weather-sun-shiny; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-snow { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translateX(0) translateY(0); | ||||||
|  |           -moz-transform: translateX(0) translateY(0); | ||||||
|  |           -ms-transform: translateX(0) translateY(0); | ||||||
|  |           transform: translateX(0) translateY(0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         33.33% { | ||||||
|  |           -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           transform: translateX(-1.2px) translateY(2px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         66.66% { | ||||||
|  |           -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           transform: translateX(1.4px) translateY(4px); | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-1 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" transform="translate(0,16)" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-1" | ||||||
|  |       style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(12,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 9.6 KiB | 
							
								
								
									
										269
									
								
								public/weather-ico/snowy-1-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,269 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.23099" width="1.403" height="1.5634"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-snow { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translateX(0) translateY(0); | ||||||
|  |           -moz-transform: translateX(0) translateY(0); | ||||||
|  |           -ms-transform: translateX(0) translateY(0); | ||||||
|  |           transform: translateX(0) translateY(0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         33.33% { | ||||||
|  |           -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           transform: translateX(-1.2px) translateY(2px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         66.66% { | ||||||
|  |           -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           transform: translateX(1.4px) translateY(4px); | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-1 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-1" | ||||||
|  |       style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(12,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										273
									
								
								public/weather-ico/snowy-2-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,273 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.23099" width="1.403" height="1.5634"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-sun { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(360deg); | ||||||
|  |           -moz-transform: rotate(360deg); | ||||||
|  |           -ms-transform: rotate(360deg); | ||||||
|  |           transform: rotate(360deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun { | ||||||
|  |         -webkit-animation-name: am-weather-sun; | ||||||
|  |         -moz-animation-name: am-weather-sun; | ||||||
|  |         -ms-animation-name: am-weather-sun; | ||||||
|  |         animation-name: am-weather-sun; | ||||||
|  |         -webkit-animation-duration: 9s; | ||||||
|  |         -moz-animation-duration: 9s; | ||||||
|  |         -ms-animation-duration: 9s; | ||||||
|  |         animation-duration: 9s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-sun-shiny { | ||||||
|  |         0% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           stroke-dasharray: 0.1px 10px; | ||||||
|  |           stroke-dashoffset: -1px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           stroke-dasharray: 3px 10px; | ||||||
|  |           stroke-dashoffset: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-sun-shiny line { | ||||||
|  |         -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |         -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |         -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |         animation-name: am-weather-sun-shiny; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-snow { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translateX(0) translateY(0); | ||||||
|  |           -moz-transform: translateX(0) translateY(0); | ||||||
|  |           -ms-transform: translateX(0) translateY(0); | ||||||
|  |           transform: translateX(0) translateY(0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         33.33% { | ||||||
|  |           -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           transform: translateX(-1.2px) translateY(2px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         66.66% { | ||||||
|  |           -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           transform: translateX(1.4px) translateY(4px); | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-1 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-2 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-delay: 1.2s; | ||||||
|  |         -moz-animation-delay: 1.2s; | ||||||
|  |         -ms-animation-delay: 1.2s; | ||||||
|  |         animation-delay: 1.2s; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="translate(0,16)"> | ||||||
|  |       <g class="am-weather-sun" | ||||||
|  |         style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |         <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" stroke-width="2" /> | ||||||
|  |         <g transform="rotate(45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(135)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="scale(-1)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(225)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-90)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |         <g transform="rotate(-45)"> | ||||||
|  |           <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |             stroke-width="2" /> | ||||||
|  |         </g> | ||||||
|  |       </g> | ||||||
|  |       <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-1" | ||||||
|  |       style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(7,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-2" | ||||||
|  |       style="-moz-animation-delay:1.2s;-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-delay:1.2s;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-delay:1.2s;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(16,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										301
									
								
								public/weather-ico/snowy-2-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,301 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <defs> | ||||||
|  |     <filter id="blur" x="-.20655" y="-.23099" width="1.403" height="1.5634"> | ||||||
|  |       <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |       <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |       <feComponentTransfer> | ||||||
|  |         <feFuncA slope="0.05" type="linear" /> | ||||||
|  |       </feComponentTransfer> | ||||||
|  |       <feMerge> | ||||||
|  |         <feMergeNode /> | ||||||
|  |         <feMergeNode in="SourceGraphic" /> | ||||||
|  |       </feMerge> | ||||||
|  |     </filter> | ||||||
|  |     <style type="text/css"> | ||||||
|  |       <![CDATA[ | ||||||
|  |       /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-cloud-2 { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: translate(2px, 0px); | ||||||
|  |           -moz-transform: translate(2px, 0px); | ||||||
|  |           -ms-transform: translate(2px, 0px); | ||||||
|  |           transform: translate(2px, 0px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translate(0px, 0px); | ||||||
|  |           -moz-transform: translate(0px, 0px); | ||||||
|  |           -ms-transform: translate(0px, 0px); | ||||||
|  |           transform: translate(0px, 0px); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-cloud-2 { | ||||||
|  |         -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |         -moz-animation-name: am-weather-cloud-2; | ||||||
|  |         animation-name: am-weather-cloud-2; | ||||||
|  |         -webkit-animation-duration: 3s; | ||||||
|  |         -moz-animation-duration: 3s; | ||||||
|  |         animation-duration: 3s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-moon { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         50% { | ||||||
|  |           -webkit-transform: rotate(15deg); | ||||||
|  |           -moz-transform: rotate(15deg); | ||||||
|  |           -ms-transform: rotate(15deg); | ||||||
|  |           transform: rotate(15deg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: rotate(0deg); | ||||||
|  |           -moz-transform: rotate(0deg); | ||||||
|  |           -ms-transform: rotate(0deg); | ||||||
|  |           transform: rotate(0deg); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon { | ||||||
|  |         -webkit-animation-name: am-weather-moon; | ||||||
|  |         -moz-animation-name: am-weather-moon; | ||||||
|  |         -ms-animation-name: am-weather-moon; | ||||||
|  |         animation-name: am-weather-moon; | ||||||
|  |         -webkit-animation-duration: 6s; | ||||||
|  |         -moz-animation-duration: 6s; | ||||||
|  |         -ms-animation-duration: 6s; | ||||||
|  |         animation-duration: 6s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |         -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |         transform-origin: 12.5px 15.15px 0; | ||||||
|  |         /* TODO FF CENTER ISSUE */ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-1 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-1 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |         animation-name: am-weather-moon-star-1; | ||||||
|  |         -webkit-animation-delay: 3s; | ||||||
|  |         -moz-animation-delay: 3s; | ||||||
|  |         -ms-animation-delay: 3s; | ||||||
|  |         animation-delay: 3s; | ||||||
|  |         -webkit-animation-duration: 5s; | ||||||
|  |         -moz-animation-duration: 5s; | ||||||
|  |         -ms-animation-duration: 5s; | ||||||
|  |         animation-duration: 5s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       @keyframes am-weather-moon-star-2 { | ||||||
|  |         0% { | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-moon-star-2 { | ||||||
|  |         -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |         -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |         -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |         animation-name: am-weather-moon-star-2; | ||||||
|  |         -webkit-animation-delay: 5s; | ||||||
|  |         -moz-animation-delay: 5s; | ||||||
|  |         -ms-animation-delay: 5s; | ||||||
|  |         animation-delay: 5s; | ||||||
|  |         -webkit-animation-duration: 4s; | ||||||
|  |         -moz-animation-duration: 4s; | ||||||
|  |         -ms-animation-duration: 4s; | ||||||
|  |         animation-duration: 4s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: 1; | ||||||
|  |         -moz-animation-iteration-count: 1; | ||||||
|  |         -ms-animation-iteration-count: 1; | ||||||
|  |         animation-iteration-count: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |       @keyframes am-weather-snow { | ||||||
|  |         0% { | ||||||
|  |           -webkit-transform: translateX(0) translateY(0); | ||||||
|  |           -moz-transform: translateX(0) translateY(0); | ||||||
|  |           -ms-transform: translateX(0) translateY(0); | ||||||
|  |           transform: translateX(0) translateY(0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         33.33% { | ||||||
|  |           -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |           transform: translateX(-1.2px) translateY(2px); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         66.66% { | ||||||
|  |           -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |           transform: translateX(1.4px) translateY(4px); | ||||||
|  |           opacity: 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         100% { | ||||||
|  |           -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           transform: translateX(-1.6px) translateY(6px); | ||||||
|  |           opacity: 0; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-1 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .am-weather-snow-2 { | ||||||
|  |         -webkit-animation-name: am-weather-snow; | ||||||
|  |         -moz-animation-name: am-weather-snow; | ||||||
|  |         -ms-animation-name: am-weather-snow; | ||||||
|  |         animation-name: am-weather-snow; | ||||||
|  |         -webkit-animation-delay: 1.2s; | ||||||
|  |         -moz-animation-delay: 1.2s; | ||||||
|  |         -ms-animation-delay: 1.2s; | ||||||
|  |         animation-delay: 1.2s; | ||||||
|  |         -webkit-animation-duration: 2s; | ||||||
|  |         -moz-animation-duration: 2s; | ||||||
|  |         -ms-animation-duration: 2s; | ||||||
|  |         animation-duration: 2s; | ||||||
|  |         -webkit-animation-timing-function: linear; | ||||||
|  |         -moz-animation-timing-function: linear; | ||||||
|  |         -ms-animation-timing-function: linear; | ||||||
|  |         animation-timing-function: linear; | ||||||
|  |         -webkit-animation-iteration-count: infinite; | ||||||
|  |         -moz-animation-iteration-count: infinite; | ||||||
|  |         -ms-animation-iteration-count: infinite; | ||||||
|  |         animation-iteration-count: infinite; | ||||||
|  |       } | ||||||
|  |       ]]> | ||||||
|  |     </style> | ||||||
|  |   </defs> | ||||||
|  |   <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |     <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |       <g class="am-weather-moon-star-1" | ||||||
|  |         style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |           stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon-star-2" | ||||||
|  |         style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |         <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |           fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-moon" | ||||||
|  |         style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |         <path | ||||||
|  |           d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |           fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-cloud-3" | ||||||
|  |       style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |       <path transform="translate(-20,-11)" | ||||||
|  |         d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |         fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-1" | ||||||
|  |       style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(7,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |     <g class="am-weather-snow-2" | ||||||
|  |       style="-moz-animation-delay:1.2s;-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-delay:1.2s;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-delay:1.2s;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |       <g transform="translate(16,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |         <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |         <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |         <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 13 KiB | 
							
								
								
									
										334
									
								
								public/weather-ico/snowy-3-day.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,334 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |    <defs> | ||||||
|  |       <filter id="blur" x="-.24684" y="-.26897" width="1.4937" height="1.6759"> | ||||||
|  |          <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |          <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |          <feComponentTransfer> | ||||||
|  |             <feFuncA slope="0.05" type="linear" /> | ||||||
|  |          </feComponentTransfer> | ||||||
|  |          <feMerge> | ||||||
|  |             <feMergeNode /> | ||||||
|  |             <feMergeNode in="SourceGraphic" /> | ||||||
|  |          </feMerge> | ||||||
|  |       </filter> | ||||||
|  |       <style type="text/css"> | ||||||
|  |          <![CDATA[ | ||||||
|  |          /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-cloud-2 { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translate(0px, 0px); | ||||||
|  |                -moz-transform: translate(0px, 0px); | ||||||
|  |                -ms-transform: translate(0px, 0px); | ||||||
|  |                transform: translate(0px, 0px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             50% { | ||||||
|  |                -webkit-transform: translate(2px, 0px); | ||||||
|  |                -moz-transform: translate(2px, 0px); | ||||||
|  |                -ms-transform: translate(2px, 0px); | ||||||
|  |                transform: translate(2px, 0px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translate(0px, 0px); | ||||||
|  |                -moz-transform: translate(0px, 0px); | ||||||
|  |                -ms-transform: translate(0px, 0px); | ||||||
|  |                transform: translate(0px, 0px); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-cloud-2 { | ||||||
|  |             -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |             -moz-animation-name: am-weather-cloud-2; | ||||||
|  |             animation-name: am-weather-cloud-2; | ||||||
|  |             -webkit-animation-duration: 3s; | ||||||
|  |             -moz-animation-duration: 3s; | ||||||
|  |             animation-duration: 3s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          /* | ||||||
|  | ** SUN | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-sun { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: rotate(0deg); | ||||||
|  |                -moz-transform: rotate(0deg); | ||||||
|  |                -ms-transform: rotate(0deg); | ||||||
|  |                transform: rotate(0deg); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: rotate(360deg); | ||||||
|  |                -moz-transform: rotate(360deg); | ||||||
|  |                -ms-transform: rotate(360deg); | ||||||
|  |                transform: rotate(360deg); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-sun { | ||||||
|  |             -webkit-animation-name: am-weather-sun; | ||||||
|  |             -moz-animation-name: am-weather-sun; | ||||||
|  |             -ms-animation-name: am-weather-sun; | ||||||
|  |             animation-name: am-weather-sun; | ||||||
|  |             -webkit-animation-duration: 9s; | ||||||
|  |             -moz-animation-duration: 9s; | ||||||
|  |             -ms-animation-duration: 9s; | ||||||
|  |             animation-duration: 9s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          @keyframes am-weather-sun-shiny { | ||||||
|  |             0% { | ||||||
|  |                stroke-dasharray: 3px 10px; | ||||||
|  |                stroke-dashoffset: 0px; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             50% { | ||||||
|  |                stroke-dasharray: 0.1px 10px; | ||||||
|  |                stroke-dashoffset: -1px; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                stroke-dasharray: 3px 10px; | ||||||
|  |                stroke-dashoffset: 0px; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-sun-shiny line { | ||||||
|  |             -webkit-animation-name: am-weather-sun-shiny; | ||||||
|  |             -moz-animation-name: am-weather-sun-shiny; | ||||||
|  |             -ms-animation-name: am-weather-sun-shiny; | ||||||
|  |             animation-name: am-weather-sun-shiny; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-snow { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translateX(0) translateY(0); | ||||||
|  |                -moz-transform: translateX(0) translateY(0); | ||||||
|  |                -ms-transform: translateX(0) translateY(0); | ||||||
|  |                transform: translateX(0) translateY(0); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             33.33% { | ||||||
|  |                -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                transform: translateX(-1.2px) translateY(2px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             66.66% { | ||||||
|  |                -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                transform: translateX(1.4px) translateY(4px); | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          @keyframes am-weather-snow-reverse { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translateX(0) translateY(0); | ||||||
|  |                -moz-transform: translateX(0) translateY(0); | ||||||
|  |                -ms-transform: translateX(0) translateY(0); | ||||||
|  |                transform: translateX(0) translateY(0); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             33.33% { | ||||||
|  |                -webkit-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                -moz-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                -ms-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                transform: translateX(1.2px) translateY(2px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             66.66% { | ||||||
|  |                -webkit-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                -moz-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                -ms-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                -moz-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                -ms-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                transform: translateX(1.6px) translateY(6px); | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-1 { | ||||||
|  |             -webkit-animation-name: am-weather-snow; | ||||||
|  |             -moz-animation-name: am-weather-snow; | ||||||
|  |             -ms-animation-name: am-weather-snow; | ||||||
|  |             animation-name: am-weather-snow; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-2 { | ||||||
|  |             -webkit-animation-name: am-weather-snow; | ||||||
|  |             -moz-animation-name: am-weather-snow; | ||||||
|  |             -ms-animation-name: am-weather-snow; | ||||||
|  |             animation-name: am-weather-snow; | ||||||
|  |             -webkit-animation-delay: 1.2s; | ||||||
|  |             -moz-animation-delay: 1.2s; | ||||||
|  |             -ms-animation-delay: 1.2s; | ||||||
|  |             animation-delay: 1.2s; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-3 { | ||||||
|  |             -webkit-animation-name: am-weather-snow-reverse; | ||||||
|  |             -moz-animation-name: am-weather-snow-reverse; | ||||||
|  |             -ms-animation-name: am-weather-snow-reverse; | ||||||
|  |             animation-name: am-weather-snow-reverse; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |          ]]> | ||||||
|  |       </style> | ||||||
|  |    </defs> | ||||||
|  |    <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |       <g transform="translate(0,16)"> | ||||||
|  |          <g class="am-weather-sun" | ||||||
|  |             style="-moz-animation-duration:9s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-sun;-moz-animation-timing-function:linear;-ms-animation-duration:9s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-sun;-ms-animation-timing-function:linear;-webkit-animation-duration:9s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-sun;-webkit-animation-timing-function:linear"> | ||||||
|  |             <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                stroke-width="2" /> | ||||||
|  |             <g transform="rotate(45)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="rotate(90)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="rotate(135)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="scale(-1)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="rotate(225)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="rotate(-90)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |             <g transform="rotate(-45)"> | ||||||
|  |                <line transform="translate(0,9)" y2="3" fill="none" stroke="#ffa500" stroke-linecap="round" | ||||||
|  |                   stroke-width="2" /> | ||||||
|  |             </g> | ||||||
|  |          </g> | ||||||
|  |          <circle r="5" fill="#ffa500" stroke="#ffa500" stroke-width="2" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-cloud-2" | ||||||
|  |          style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |          <path transform="translate(-20,-11)" | ||||||
|  |             d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |             fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-1" | ||||||
|  |          style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(3,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-2" | ||||||
|  |          style="-moz-animation-delay:1.2s;-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-delay:1.2s;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-delay:1.2s;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(11,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-3" | ||||||
|  |          style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow-reverse;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow-reverse;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow-reverse;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(20,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |    </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										361
									
								
								public/weather-ico/snowy-3-night.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,361 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- (c) ammap.com | SVG weather icons --> | ||||||
|  | <svg width="56" height="48" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |    <defs> | ||||||
|  |       <filter id="blur" x="-.24684" y="-.26897" width="1.4937" height="1.6759"> | ||||||
|  |          <feGaussianBlur in="SourceAlpha" stdDeviation="3" /> | ||||||
|  |          <feOffset dx="0" dy="4" result="offsetblur" /> | ||||||
|  |          <feComponentTransfer> | ||||||
|  |             <feFuncA slope="0.05" type="linear" /> | ||||||
|  |          </feComponentTransfer> | ||||||
|  |          <feMerge> | ||||||
|  |             <feMergeNode /> | ||||||
|  |             <feMergeNode in="SourceGraphic" /> | ||||||
|  |          </feMerge> | ||||||
|  |       </filter> | ||||||
|  |       <style type="text/css"> | ||||||
|  |          <![CDATA[ | ||||||
|  |          /* | ||||||
|  | ** CLOUDS | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-cloud-2 { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translate(0px, 0px); | ||||||
|  |                -moz-transform: translate(0px, 0px); | ||||||
|  |                -ms-transform: translate(0px, 0px); | ||||||
|  |                transform: translate(0px, 0px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             50% { | ||||||
|  |                -webkit-transform: translate(2px, 0px); | ||||||
|  |                -moz-transform: translate(2px, 0px); | ||||||
|  |                -ms-transform: translate(2px, 0px); | ||||||
|  |                transform: translate(2px, 0px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translate(0px, 0px); | ||||||
|  |                -moz-transform: translate(0px, 0px); | ||||||
|  |                -ms-transform: translate(0px, 0px); | ||||||
|  |                transform: translate(0px, 0px); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-cloud-2 { | ||||||
|  |             -webkit-animation-name: am-weather-cloud-2; | ||||||
|  |             -moz-animation-name: am-weather-cloud-2; | ||||||
|  |             animation-name: am-weather-cloud-2; | ||||||
|  |             -webkit-animation-duration: 3s; | ||||||
|  |             -moz-animation-duration: 3s; | ||||||
|  |             animation-duration: 3s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          /* | ||||||
|  | ** MOON | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-moon { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: rotate(0deg); | ||||||
|  |                -moz-transform: rotate(0deg); | ||||||
|  |                -ms-transform: rotate(0deg); | ||||||
|  |                transform: rotate(0deg); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             50% { | ||||||
|  |                -webkit-transform: rotate(15deg); | ||||||
|  |                -moz-transform: rotate(15deg); | ||||||
|  |                -ms-transform: rotate(15deg); | ||||||
|  |                transform: rotate(15deg); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: rotate(0deg); | ||||||
|  |                -moz-transform: rotate(0deg); | ||||||
|  |                -ms-transform: rotate(0deg); | ||||||
|  |                transform: rotate(0deg); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-moon { | ||||||
|  |             -webkit-animation-name: am-weather-moon; | ||||||
|  |             -moz-animation-name: am-weather-moon; | ||||||
|  |             -ms-animation-name: am-weather-moon; | ||||||
|  |             animation-name: am-weather-moon; | ||||||
|  |             -webkit-animation-duration: 6s; | ||||||
|  |             -moz-animation-duration: 6s; | ||||||
|  |             -ms-animation-duration: 6s; | ||||||
|  |             animation-duration: 6s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |             -webkit-transform-origin: 12.5px 15.15px 0; | ||||||
|  |             /* TODO FF CENTER ISSUE */ | ||||||
|  |             -moz-transform-origin: 12.5px 15.15px 0; | ||||||
|  |             /* TODO FF CENTER ISSUE */ | ||||||
|  |             -ms-transform-origin: 12.5px 15.15px 0; | ||||||
|  |             /* TODO FF CENTER ISSUE */ | ||||||
|  |             transform-origin: 12.5px 15.15px 0; | ||||||
|  |             /* TODO FF CENTER ISSUE */ | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          @keyframes am-weather-moon-star-1 { | ||||||
|  |             0% { | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-moon-star-1 { | ||||||
|  |             -webkit-animation-name: am-weather-moon-star-1; | ||||||
|  |             -moz-animation-name: am-weather-moon-star-1; | ||||||
|  |             -ms-animation-name: am-weather-moon-star-1; | ||||||
|  |             animation-name: am-weather-moon-star-1; | ||||||
|  |             -webkit-animation-delay: 3s; | ||||||
|  |             -moz-animation-delay: 3s; | ||||||
|  |             -ms-animation-delay: 3s; | ||||||
|  |             animation-delay: 3s; | ||||||
|  |             -webkit-animation-duration: 5s; | ||||||
|  |             -moz-animation-duration: 5s; | ||||||
|  |             -ms-animation-duration: 5s; | ||||||
|  |             animation-duration: 5s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: 1; | ||||||
|  |             -moz-animation-iteration-count: 1; | ||||||
|  |             -ms-animation-iteration-count: 1; | ||||||
|  |             animation-iteration-count: 1; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          @keyframes am-weather-moon-star-2 { | ||||||
|  |             0% { | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-moon-star-2 { | ||||||
|  |             -webkit-animation-name: am-weather-moon-star-2; | ||||||
|  |             -moz-animation-name: am-weather-moon-star-2; | ||||||
|  |             -ms-animation-name: am-weather-moon-star-2; | ||||||
|  |             animation-name: am-weather-moon-star-2; | ||||||
|  |             -webkit-animation-delay: 5s; | ||||||
|  |             -moz-animation-delay: 5s; | ||||||
|  |             -ms-animation-delay: 5s; | ||||||
|  |             animation-delay: 5s; | ||||||
|  |             -webkit-animation-duration: 4s; | ||||||
|  |             -moz-animation-duration: 4s; | ||||||
|  |             -ms-animation-duration: 4s; | ||||||
|  |             animation-duration: 4s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: 1; | ||||||
|  |             -moz-animation-iteration-count: 1; | ||||||
|  |             -ms-animation-iteration-count: 1; | ||||||
|  |             animation-iteration-count: 1; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          /* | ||||||
|  | ** SNOW | ||||||
|  | */ | ||||||
|  |          @keyframes am-weather-snow { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translateX(0) translateY(0); | ||||||
|  |                -moz-transform: translateX(0) translateY(0); | ||||||
|  |                -ms-transform: translateX(0) translateY(0); | ||||||
|  |                transform: translateX(0) translateY(0); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             33.33% { | ||||||
|  |                -webkit-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                -moz-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                -ms-transform: translateX(-1.2px) translateY(2px); | ||||||
|  |                transform: translateX(-1.2px) translateY(2px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             66.66% { | ||||||
|  |                -webkit-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                -moz-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                -ms-transform: translateX(1.4px) translateY(4px); | ||||||
|  |                transform: translateX(1.4px) translateY(4px); | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                -moz-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                -ms-transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                transform: translateX(-1.6px) translateY(6px); | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          @keyframes am-weather-snow-reverse { | ||||||
|  |             0% { | ||||||
|  |                -webkit-transform: translateX(0) translateY(0); | ||||||
|  |                -moz-transform: translateX(0) translateY(0); | ||||||
|  |                -ms-transform: translateX(0) translateY(0); | ||||||
|  |                transform: translateX(0) translateY(0); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             33.33% { | ||||||
|  |                -webkit-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                -moz-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                -ms-transform: translateX(1.2px) translateY(2px); | ||||||
|  |                transform: translateX(1.2px) translateY(2px); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             66.66% { | ||||||
|  |                -webkit-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                -moz-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                -ms-transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                transform: translateX(-1.4px) translateY(4px); | ||||||
|  |                opacity: 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             100% { | ||||||
|  |                -webkit-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                -moz-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                -ms-transform: translateX(1.6px) translateY(6px); | ||||||
|  |                transform: translateX(1.6px) translateY(6px); | ||||||
|  |                opacity: 0; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-1 { | ||||||
|  |             -webkit-animation-name: am-weather-snow; | ||||||
|  |             -moz-animation-name: am-weather-snow; | ||||||
|  |             -ms-animation-name: am-weather-snow; | ||||||
|  |             animation-name: am-weather-snow; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-2 { | ||||||
|  |             -webkit-animation-name: am-weather-snow; | ||||||
|  |             -moz-animation-name: am-weather-snow; | ||||||
|  |             -ms-animation-name: am-weather-snow; | ||||||
|  |             animation-name: am-weather-snow; | ||||||
|  |             -webkit-animation-delay: 1.2s; | ||||||
|  |             -moz-animation-delay: 1.2s; | ||||||
|  |             -ms-animation-delay: 1.2s; | ||||||
|  |             animation-delay: 1.2s; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          .am-weather-snow-3 { | ||||||
|  |             -webkit-animation-name: am-weather-snow-reverse; | ||||||
|  |             -moz-animation-name: am-weather-snow-reverse; | ||||||
|  |             -ms-animation-name: am-weather-snow-reverse; | ||||||
|  |             animation-name: am-weather-snow-reverse; | ||||||
|  |             -webkit-animation-duration: 2s; | ||||||
|  |             -moz-animation-duration: 2s; | ||||||
|  |             -ms-animation-duration: 2s; | ||||||
|  |             animation-duration: 2s; | ||||||
|  |             -webkit-animation-timing-function: linear; | ||||||
|  |             -moz-animation-timing-function: linear; | ||||||
|  |             -ms-animation-timing-function: linear; | ||||||
|  |             animation-timing-function: linear; | ||||||
|  |             -webkit-animation-iteration-count: infinite; | ||||||
|  |             -moz-animation-iteration-count: infinite; | ||||||
|  |             -ms-animation-iteration-count: infinite; | ||||||
|  |             animation-iteration-count: infinite; | ||||||
|  |          } | ||||||
|  |          ]]> | ||||||
|  |       </style> | ||||||
|  |    </defs> | ||||||
|  |    <g transform="translate(16,-2)" filter="url(#blur)"> | ||||||
|  |       <g transform="matrix(.8 0 0 .8 16 4)"> | ||||||
|  |          <g class="am-weather-moon-star-1" | ||||||
|  |             style="-moz-animation-delay:3s;-moz-animation-duration:5s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-1;-moz-animation-timing-function:linear;-ms-animation-delay:3s;-ms-animation-duration:5s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-1;-ms-animation-timing-function:linear;-webkit-animation-delay:3s;-webkit-animation-duration:5s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-1;-webkit-animation-timing-function:linear"> | ||||||
|  |             <polygon points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" fill="#ffa500" | ||||||
|  |                stroke-miterlimit="10" /> | ||||||
|  |          </g> | ||||||
|  |          <g class="am-weather-moon-star-2" | ||||||
|  |             style="-moz-animation-delay:5s;-moz-animation-duration:4s;-moz-animation-iteration-count:1;-moz-animation-name:am-weather-moon-star-2;-moz-animation-timing-function:linear;-ms-animation-delay:5s;-ms-animation-duration:4s;-ms-animation-iteration-count:1;-ms-animation-name:am-weather-moon-star-2;-ms-animation-timing-function:linear;-webkit-animation-delay:5s;-webkit-animation-duration:4s;-webkit-animation-iteration-count:1;-webkit-animation-name:am-weather-moon-star-2;-webkit-animation-timing-function:linear"> | ||||||
|  |             <polygon transform="translate(20,10)" points="4 4 3.3 5.2 2.7 4 1.5 3.3 2.7 2.7 3.3 1.5 4 2.7 5.2 3.3" | ||||||
|  |                fill="#ffa500" stroke-miterlimit="10" /> | ||||||
|  |          </g> | ||||||
|  |          <g class="am-weather-moon" | ||||||
|  |             style="-moz-animation-duration:6s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-moon;-moz-animation-timing-function:linear;-moz-transform-origin:12.5px 15.15px 0;-ms-animation-duration:6s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-moon;-ms-animation-timing-function:linear;-ms-transform-origin:12.5px 15.15px 0;-webkit-animation-duration:6s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-moon;-webkit-animation-timing-function:linear;-webkit-transform-origin:12.5px 15.15px 0"> | ||||||
|  |             <path | ||||||
|  |                d="m14.5 13.2c0-3.7 2-6.9 5-8.7-1.5-0.9-3.2-1.3-5-1.3-5.5 0-10 4.5-10 10s4.5 10 10 10c1.8 0 3.5-0.5 5-1.3-3-1.7-5-5-5-8.7z" | ||||||
|  |                fill="#ffa500" stroke="#ffa500" stroke-linejoin="round" stroke-width="2" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-cloud-2" | ||||||
|  |          style="-moz-animation-duration:3s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-cloud-2;-moz-animation-timing-function:linear;-webkit-animation-duration:3s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-cloud-2;-webkit-animation-timing-function:linear"> | ||||||
|  |          <path transform="translate(-20,-11)" | ||||||
|  |             d="m47.7 35.4c0-4.6-3.7-8.2-8.2-8.2-1 0-1.9 0.2-2.8 0.5-0.3-3.4-3.1-6.2-6.6-6.2-3.7 0-6.7 3-6.7 6.7 0 0.8 0.2 1.6 0.4 2.3-0.3-0.1-0.7-0.1-1-0.1-3.7 0-6.7 3-6.7 6.7 0 3.6 2.9 6.6 6.5 6.7h17.2c4.4-0.5 7.9-4 7.9-8.4z" | ||||||
|  |             fill="#57a0ee" stroke="#fff" stroke-linejoin="round" stroke-width="1.2" /> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-1" | ||||||
|  |          style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(3,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-2" | ||||||
|  |          style="-moz-animation-delay:1.2s;-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow;-moz-animation-timing-function:linear;-ms-animation-delay:1.2s;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow;-ms-animation-timing-function:linear;-webkit-animation-delay:1.2s;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(11,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |       <g class="am-weather-snow-3" | ||||||
|  |          style="-moz-animation-duration:2s;-moz-animation-iteration-count:infinite;-moz-animation-name:am-weather-snow-reverse;-moz-animation-timing-function:linear;-ms-animation-duration:2s;-ms-animation-iteration-count:infinite;-ms-animation-name:am-weather-snow-reverse;-ms-animation-timing-function:linear;-webkit-animation-duration:2s;-webkit-animation-iteration-count:infinite;-webkit-animation-name:am-weather-snow-reverse;-webkit-animation-timing-function:linear"> | ||||||
|  |          <g transform="translate(20,28)" fill="none" stroke="#57a0ee" stroke-linecap="round"> | ||||||
|  |             <line transform="translate(0,9)" y1="-2.5" y2="2.5" stroke-width="1.2" /> | ||||||
|  |             <line transform="rotate(45,-10.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(90,-4.5,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |             <line transform="rotate(135,-1.864,4.5)" y1="-2.5" y2="2.5" /> | ||||||
|  |          </g> | ||||||
|  |       </g> | ||||||
|  |    </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 17 KiB | 
| @@ -1,10 +1,5 @@ | |||||||
| [GENERAL] | [GENERAL] | ||||||
| PORT = 3001 # Port to run the server on |  | ||||||
| SIMILARITY_MEASURE = "cosine" # "cosine" or "dot" | SIMILARITY_MEASURE = "cosine" # "cosine" or "dot" | ||||||
| CONFIG_PASSWORD = "lorem_ipsum" # Password to access config |  | ||||||
| DISCOVER_ENABLED = true |  | ||||||
| LIBRARY_ENABLED = true |  | ||||||
| COPILOT_ENABLED = true |  | ||||||
| KEEP_ALIVE = "5m" # How long to keep Ollama models loaded into memory. (Instead of using -1 use "-1m") | KEEP_ALIVE = "5m" # How long to keep Ollama models loaded into memory. (Instead of using -1 use "-1m") | ||||||
|  |  | ||||||
| [MODELS.OPENAI] | [MODELS.OPENAI] | ||||||
| @@ -27,5 +22,14 @@ MODEL_NAME = "" | |||||||
| [MODELS.OLLAMA] | [MODELS.OLLAMA] | ||||||
| API_URL = "" # Ollama API URL - http://host.docker.internal:11434 | API_URL = "" # Ollama API URL - http://host.docker.internal:11434 | ||||||
|  |  | ||||||
|  | [MODELS.DEEPSEEK] | ||||||
|  | API_KEY = "" | ||||||
|  |  | ||||||
|  | [MODELS.AIMLAPI] | ||||||
|  | API_KEY = "" # Required to use AI/ML API chat and embedding models | ||||||
|  |  | ||||||
|  | [MODELS.LM_STUDIO] | ||||||
|  | API_URL = "" # LM Studio API URL - http://host.docker.internal:1234 | ||||||
|  |  | ||||||
| [API_ENDPOINTS] | [API_ENDPOINTS] | ||||||
| SEARXNG = "http://localhost:32768" # SearxNG API URL | SEARXNG = "" # SearxNG API URL - http://localhost:32768 | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								src/app.ts
									
									
									
									
									
								
							
							
						
						| @@ -1,38 +0,0 @@ | |||||||
| import { startWebSocketServer } from './websocket'; |  | ||||||
| import express from 'express'; |  | ||||||
| import cors from 'cors'; |  | ||||||
| import http from 'http'; |  | ||||||
| import routes from './routes'; |  | ||||||
| import { getPort } from './config'; |  | ||||||
| import logger from './utils/logger'; |  | ||||||
|  |  | ||||||
| const port = getPort(); |  | ||||||
|  |  | ||||||
| const app = express(); |  | ||||||
| const server = http.createServer(app); |  | ||||||
|  |  | ||||||
| const corsOptions = { |  | ||||||
|   origin: '*', |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| app.use(cors(corsOptions)); |  | ||||||
| app.use(express.json()); |  | ||||||
|  |  | ||||||
| app.use('/api', routes); |  | ||||||
| app.get('/api', (_, res) => { |  | ||||||
|   res.status(200).json({ status: 'ok' }); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| server.listen(port, () => { |  | ||||||
|   logger.info(`Server is running on port ${port}`); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| startWebSocketServer(server); |  | ||||||
|  |  | ||||||
| process.on('uncaughtException', (err, origin) => { |  | ||||||
|   logger.error(`Uncaught Exception at ${origin}: ${err}`); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| process.on('unhandledRejection', (reason, promise) => { |  | ||||||
|   logger.error(`Unhandled Rejection at: ${promise}, reason: ${reason}`); |  | ||||||
| }); |  | ||||||
							
								
								
									
										306
									
								
								src/app/api/chat/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,306 @@ | |||||||
|  | import prompts from '@/lib/prompts'; | ||||||
|  | import MetaSearchAgent from '@/lib/search/metaSearchAgent'; | ||||||
|  | import crypto from 'crypto'; | ||||||
|  | import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; | ||||||
|  | import { EventEmitter } from 'stream'; | ||||||
|  | import { | ||||||
|  |   chatModelProviders, | ||||||
|  |   embeddingModelProviders, | ||||||
|  |   getAvailableChatModelProviders, | ||||||
|  |   getAvailableEmbeddingModelProviders, | ||||||
|  | } from '@/lib/providers'; | ||||||
|  | import db from '@/lib/db'; | ||||||
|  | import { chats, messages as messagesSchema } from '@/lib/db/schema'; | ||||||
|  | import { and, eq, gt } from 'drizzle-orm'; | ||||||
|  | import { getFileDetails } from '@/lib/utils/files'; | ||||||
|  | import { BaseChatModel } from '@langchain/core/language_models/chat_models'; | ||||||
|  | import { ChatOpenAI } from '@langchain/openai'; | ||||||
|  | import { | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { searchHandlers } from '@/lib/search'; | ||||||
|  |  | ||||||
|  | export const runtime = 'nodejs'; | ||||||
|  | export const dynamic = 'force-dynamic'; | ||||||
|  |  | ||||||
|  | type Message = { | ||||||
|  |   messageId: string; | ||||||
|  |   chatId: string; | ||||||
|  |   content: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type ChatModel = { | ||||||
|  |   provider: string; | ||||||
|  |   name: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type EmbeddingModel = { | ||||||
|  |   provider: string; | ||||||
|  |   name: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type Body = { | ||||||
|  |   message: Message; | ||||||
|  |   optimizationMode: 'speed' | 'balanced' | 'quality'; | ||||||
|  |   focusMode: string; | ||||||
|  |   history: Array<[string, string]>; | ||||||
|  |   files: Array<string>; | ||||||
|  |   chatModel: ChatModel; | ||||||
|  |   embeddingModel: EmbeddingModel; | ||||||
|  |   systemInstructions: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const handleEmitterEvents = async ( | ||||||
|  |   stream: EventEmitter, | ||||||
|  |   writer: WritableStreamDefaultWriter, | ||||||
|  |   encoder: TextEncoder, | ||||||
|  |   aiMessageId: string, | ||||||
|  |   chatId: string, | ||||||
|  | ) => { | ||||||
|  |   let recievedMessage = ''; | ||||||
|  |   let sources: any[] = []; | ||||||
|  |  | ||||||
|  |   stream.on('data', (data) => { | ||||||
|  |     const parsedData = JSON.parse(data); | ||||||
|  |     if (parsedData.type === 'response') { | ||||||
|  |       writer.write( | ||||||
|  |         encoder.encode( | ||||||
|  |           JSON.stringify({ | ||||||
|  |             type: 'message', | ||||||
|  |             data: parsedData.data, | ||||||
|  |             messageId: aiMessageId, | ||||||
|  |           }) + '\n', | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       recievedMessage += parsedData.data; | ||||||
|  |     } else if (parsedData.type === 'sources') { | ||||||
|  |       writer.write( | ||||||
|  |         encoder.encode( | ||||||
|  |           JSON.stringify({ | ||||||
|  |             type: 'sources', | ||||||
|  |             data: parsedData.data, | ||||||
|  |             messageId: aiMessageId, | ||||||
|  |           }) + '\n', | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       sources = parsedData.data; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   stream.on('end', () => { | ||||||
|  |     writer.write( | ||||||
|  |       encoder.encode( | ||||||
|  |         JSON.stringify({ | ||||||
|  |           type: 'messageEnd', | ||||||
|  |           messageId: aiMessageId, | ||||||
|  |         }) + '\n', | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |     writer.close(); | ||||||
|  |  | ||||||
|  |     db.insert(messagesSchema) | ||||||
|  |       .values({ | ||||||
|  |         content: recievedMessage, | ||||||
|  |         chatId: chatId, | ||||||
|  |         messageId: aiMessageId, | ||||||
|  |         role: 'assistant', | ||||||
|  |         metadata: JSON.stringify({ | ||||||
|  |           createdAt: new Date(), | ||||||
|  |           ...(sources && sources.length > 0 && { sources }), | ||||||
|  |         }), | ||||||
|  |       }) | ||||||
|  |       .execute(); | ||||||
|  |   }); | ||||||
|  |   stream.on('error', (data) => { | ||||||
|  |     const parsedData = JSON.parse(data); | ||||||
|  |     writer.write( | ||||||
|  |       encoder.encode( | ||||||
|  |         JSON.stringify({ | ||||||
|  |           type: 'error', | ||||||
|  |           data: parsedData.data, | ||||||
|  |         }), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |     writer.close(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const handleHistorySave = async ( | ||||||
|  |   message: Message, | ||||||
|  |   humanMessageId: string, | ||||||
|  |   focusMode: string, | ||||||
|  |   files: string[], | ||||||
|  | ) => { | ||||||
|  |   const chat = await db.query.chats.findFirst({ | ||||||
|  |     where: eq(chats.id, message.chatId), | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   if (!chat) { | ||||||
|  |     await db | ||||||
|  |       .insert(chats) | ||||||
|  |       .values({ | ||||||
|  |         id: message.chatId, | ||||||
|  |         title: message.content, | ||||||
|  |         createdAt: new Date().toString(), | ||||||
|  |         focusMode: focusMode, | ||||||
|  |         files: files.map(getFileDetails), | ||||||
|  |       }) | ||||||
|  |       .execute(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const messageExists = await db.query.messages.findFirst({ | ||||||
|  |     where: eq(messagesSchema.messageId, humanMessageId), | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   if (!messageExists) { | ||||||
|  |     await db | ||||||
|  |       .insert(messagesSchema) | ||||||
|  |       .values({ | ||||||
|  |         content: message.content, | ||||||
|  |         chatId: message.chatId, | ||||||
|  |         messageId: humanMessageId, | ||||||
|  |         role: 'user', | ||||||
|  |         metadata: JSON.stringify({ | ||||||
|  |           createdAt: new Date(), | ||||||
|  |         }), | ||||||
|  |       }) | ||||||
|  |       .execute(); | ||||||
|  |   } else { | ||||||
|  |     await db | ||||||
|  |       .delete(messagesSchema) | ||||||
|  |       .where( | ||||||
|  |         and( | ||||||
|  |           gt(messagesSchema.id, messageExists.id), | ||||||
|  |           eq(messagesSchema.chatId, message.chatId), | ||||||
|  |         ), | ||||||
|  |       ) | ||||||
|  |       .execute(); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body = (await req.json()) as Body; | ||||||
|  |     const { message } = body; | ||||||
|  |  | ||||||
|  |     if (message.content === '') { | ||||||
|  |       return Response.json( | ||||||
|  |         { | ||||||
|  |           message: 'Please provide a message to process', | ||||||
|  |         }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const [chatModelProviders, embeddingModelProviders] = await Promise.all([ | ||||||
|  |       getAvailableChatModelProviders(), | ||||||
|  |       getAvailableEmbeddingModelProviders(), | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     const chatModelProvider = | ||||||
|  |       chatModelProviders[ | ||||||
|  |         body.chatModel?.provider || Object.keys(chatModelProviders)[0] | ||||||
|  |       ]; | ||||||
|  |     const chatModel = | ||||||
|  |       chatModelProvider[ | ||||||
|  |         body.chatModel?.name || Object.keys(chatModelProvider)[0] | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |     const embeddingProvider = | ||||||
|  |       embeddingModelProviders[ | ||||||
|  |         body.embeddingModel?.provider || Object.keys(embeddingModelProviders)[0] | ||||||
|  |       ]; | ||||||
|  |     const embeddingModel = | ||||||
|  |       embeddingProvider[ | ||||||
|  |         body.embeddingModel?.name || Object.keys(embeddingProvider)[0] | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |     let llm: BaseChatModel | undefined; | ||||||
|  |     let embedding = embeddingModel.model; | ||||||
|  |  | ||||||
|  |     if (body.chatModel?.provider === 'custom_openai') { | ||||||
|  |       llm = new ChatOpenAI({ | ||||||
|  |         apiKey: getCustomOpenaiApiKey(), | ||||||
|  |         modelName: getCustomOpenaiModelName(), | ||||||
|  |         temperature: 0.7, | ||||||
|  |         configuration: { | ||||||
|  |           baseURL: getCustomOpenaiApiUrl(), | ||||||
|  |         }, | ||||||
|  |       }) as unknown as BaseChatModel; | ||||||
|  |     } else if (chatModelProvider && chatModel) { | ||||||
|  |       llm = chatModel.model; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!llm) { | ||||||
|  |       return Response.json({ error: 'Invalid chat model' }, { status: 400 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!embedding) { | ||||||
|  |       return Response.json( | ||||||
|  |         { error: 'Invalid embedding model' }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const humanMessageId = | ||||||
|  |       message.messageId ?? crypto.randomBytes(7).toString('hex'); | ||||||
|  |     const aiMessageId = crypto.randomBytes(7).toString('hex'); | ||||||
|  |  | ||||||
|  |     const history: BaseMessage[] = body.history.map((msg) => { | ||||||
|  |       if (msg[0] === 'human') { | ||||||
|  |         return new HumanMessage({ | ||||||
|  |           content: msg[1], | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         return new AIMessage({ | ||||||
|  |           content: msg[1], | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const handler = searchHandlers[body.focusMode]; | ||||||
|  |  | ||||||
|  |     if (!handler) { | ||||||
|  |       return Response.json( | ||||||
|  |         { | ||||||
|  |           message: 'Invalid focus mode', | ||||||
|  |         }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const stream = await handler.searchAndAnswer( | ||||||
|  |       message.content, | ||||||
|  |       history, | ||||||
|  |       llm, | ||||||
|  |       embedding, | ||||||
|  |       body.optimizationMode, | ||||||
|  |       body.files, | ||||||
|  |       body.systemInstructions, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const responseStream = new TransformStream(); | ||||||
|  |     const writer = responseStream.writable.getWriter(); | ||||||
|  |     const encoder = new TextEncoder(); | ||||||
|  |  | ||||||
|  |     handleEmitterEvents(stream, writer, encoder, aiMessageId, message.chatId); | ||||||
|  |     handleHistorySave(message, humanMessageId, body.focusMode, body.files); | ||||||
|  |  | ||||||
|  |     return new Response(responseStream.readable, { | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'text/event-stream', | ||||||
|  |         Connection: 'keep-alive', | ||||||
|  |         'Cache-Control': 'no-cache, no-transform', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('An error occurred while processing chat request:', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while processing chat request' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										69
									
								
								src/app/api/chats/[id]/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,69 @@ | |||||||
|  | import db from '@/lib/db'; | ||||||
|  | import { chats, messages } from '@/lib/db/schema'; | ||||||
|  | import { eq } from 'drizzle-orm'; | ||||||
|  |  | ||||||
|  | export const GET = async ( | ||||||
|  |   req: Request, | ||||||
|  |   { params }: { params: Promise<{ id: string }> }, | ||||||
|  | ) => { | ||||||
|  |   try { | ||||||
|  |     const { id } = await params; | ||||||
|  |  | ||||||
|  |     const chatExists = await db.query.chats.findFirst({ | ||||||
|  |       where: eq(chats.id, id), | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (!chatExists) { | ||||||
|  |       return Response.json({ message: 'Chat not found' }, { status: 404 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const chatMessages = await db.query.messages.findMany({ | ||||||
|  |       where: eq(messages.chatId, id), | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         chat: chatExists, | ||||||
|  |         messages: chatMessages, | ||||||
|  |       }, | ||||||
|  |       { status: 200 }, | ||||||
|  |     ); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('Error in getting chat by id: ', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error has occurred.' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const DELETE = async ( | ||||||
|  |   req: Request, | ||||||
|  |   { params }: { params: Promise<{ id: string }> }, | ||||||
|  | ) => { | ||||||
|  |   try { | ||||||
|  |     const { id } = await params; | ||||||
|  |  | ||||||
|  |     const chatExists = await db.query.chats.findFirst({ | ||||||
|  |       where: eq(chats.id, id), | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (!chatExists) { | ||||||
|  |       return Response.json({ message: 'Chat not found' }, { status: 404 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await db.delete(chats).where(eq(chats.id, id)).execute(); | ||||||
|  |     await db.delete(messages).where(eq(messages.chatId, id)).execute(); | ||||||
|  |  | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'Chat deleted successfully' }, | ||||||
|  |       { status: 200 }, | ||||||
|  |     ); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('Error in deleting chat by id: ', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error has occurred.' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										15
									
								
								src/app/api/chats/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | |||||||
|  | import db from '@/lib/db'; | ||||||
|  |  | ||||||
|  | export const GET = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     let chats = await db.query.chats.findMany(); | ||||||
|  |     chats = chats.reverse(); | ||||||
|  |     return Response.json({ chats: chats }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('Error in getting chats: ', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error has occurred.' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										124
									
								
								src/app/api/config/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,124 @@ | |||||||
|  | import { | ||||||
|  |   getAnthropicApiKey, | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  |   getGeminiApiKey, | ||||||
|  |   getGroqApiKey, | ||||||
|  |   getOllamaApiEndpoint, | ||||||
|  |   getOpenaiApiKey, | ||||||
|  |   getDeepseekApiKey, | ||||||
|  |   getAimlApiKey, | ||||||
|  |   getLMStudioApiEndpoint, | ||||||
|  |   updateConfig, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { | ||||||
|  |   getAvailableChatModelProviders, | ||||||
|  |   getAvailableEmbeddingModelProviders, | ||||||
|  | } from '@/lib/providers'; | ||||||
|  |  | ||||||
|  | export const GET = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const config: Record<string, any> = {}; | ||||||
|  |  | ||||||
|  |     const [chatModelProviders, embeddingModelProviders] = await Promise.all([ | ||||||
|  |       getAvailableChatModelProviders(), | ||||||
|  |       getAvailableEmbeddingModelProviders(), | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     config['chatModelProviders'] = {}; | ||||||
|  |     config['embeddingModelProviders'] = {}; | ||||||
|  |  | ||||||
|  |     for (const provider in chatModelProviders) { | ||||||
|  |       config['chatModelProviders'][provider] = Object.keys( | ||||||
|  |         chatModelProviders[provider], | ||||||
|  |       ).map((model) => { | ||||||
|  |         return { | ||||||
|  |           name: model, | ||||||
|  |           displayName: chatModelProviders[provider][model].displayName, | ||||||
|  |         }; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (const provider in embeddingModelProviders) { | ||||||
|  |       config['embeddingModelProviders'][provider] = Object.keys( | ||||||
|  |         embeddingModelProviders[provider], | ||||||
|  |       ).map((model) => { | ||||||
|  |         return { | ||||||
|  |           name: model, | ||||||
|  |           displayName: embeddingModelProviders[provider][model].displayName, | ||||||
|  |         }; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     config['openaiApiKey'] = getOpenaiApiKey(); | ||||||
|  |     config['ollamaApiUrl'] = getOllamaApiEndpoint(); | ||||||
|  |     config['lmStudioApiUrl'] = getLMStudioApiEndpoint(); | ||||||
|  |     config['anthropicApiKey'] = getAnthropicApiKey(); | ||||||
|  |     config['groqApiKey'] = getGroqApiKey(); | ||||||
|  |     config['geminiApiKey'] = getGeminiApiKey(); | ||||||
|  |     config['deepseekApiKey'] = getDeepseekApiKey(); | ||||||
|  |     config['aimlApiKey'] = getAimlApiKey(); | ||||||
|  |     config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl(); | ||||||
|  |     config['customOpenaiApiKey'] = getCustomOpenaiApiKey(); | ||||||
|  |     config['customOpenaiModelName'] = getCustomOpenaiModelName(); | ||||||
|  |  | ||||||
|  |     return Response.json({ ...config }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('An error occurred while getting config:', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while getting config' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const config = await req.json(); | ||||||
|  |  | ||||||
|  |     const updatedConfig = { | ||||||
|  |       MODELS: { | ||||||
|  |         OPENAI: { | ||||||
|  |           API_KEY: config.openaiApiKey, | ||||||
|  |         }, | ||||||
|  |         GROQ: { | ||||||
|  |           API_KEY: config.groqApiKey, | ||||||
|  |         }, | ||||||
|  |         ANTHROPIC: { | ||||||
|  |           API_KEY: config.anthropicApiKey, | ||||||
|  |         }, | ||||||
|  |         GEMINI: { | ||||||
|  |           API_KEY: config.geminiApiKey, | ||||||
|  |         }, | ||||||
|  |         OLLAMA: { | ||||||
|  |           API_URL: config.ollamaApiUrl, | ||||||
|  |         }, | ||||||
|  |         DEEPSEEK: { | ||||||
|  |           API_KEY: config.deepseekApiKey, | ||||||
|  |         }, | ||||||
|  |         AIMLAPI: { | ||||||
|  |           API_KEY: config.aimlApiKey, | ||||||
|  |         }, | ||||||
|  |         LM_STUDIO: { | ||||||
|  |           API_URL: config.lmStudioApiUrl, | ||||||
|  |         }, | ||||||
|  |         CUSTOM_OPENAI: { | ||||||
|  |           API_URL: config.customOpenaiApiUrl, | ||||||
|  |           API_KEY: config.customOpenaiApiKey, | ||||||
|  |           MODEL_NAME: config.customOpenaiModelName, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     updateConfig(updatedConfig); | ||||||
|  |  | ||||||
|  |     return Response.json({ message: 'Config updated' }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('An error occurred while updating config:', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while updating config' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										81
									
								
								src/app/api/discover/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,81 @@ | |||||||
|  | import { searchSearxng } from '@/lib/searxng'; | ||||||
|  |  | ||||||
|  | const articleWebsites = [ | ||||||
|  |   'yahoo.com', | ||||||
|  |   'www.exchangewire.com', | ||||||
|  |   'businessinsider.com', | ||||||
|  |   /* 'wired.com', | ||||||
|  |   'mashable.com', | ||||||
|  |   'theverge.com', | ||||||
|  |   'gizmodo.com', | ||||||
|  |   'cnet.com', | ||||||
|  |   'venturebeat.com', */ | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | const topics = ['AI', 'tech']; /* TODO: Add UI to customize this */ | ||||||
|  |  | ||||||
|  | export const GET = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const params = new URL(req.url).searchParams; | ||||||
|  |     const mode: 'normal' | 'preview' = | ||||||
|  |       (params.get('mode') as 'normal' | 'preview') || 'normal'; | ||||||
|  |  | ||||||
|  |     let data = []; | ||||||
|  |  | ||||||
|  |     if (mode === 'normal') { | ||||||
|  |       data = ( | ||||||
|  |         await Promise.all([ | ||||||
|  |           ...new Array(articleWebsites.length * topics.length) | ||||||
|  |             .fill(0) | ||||||
|  |             .map(async (_, i) => { | ||||||
|  |               return ( | ||||||
|  |                 await searchSearxng( | ||||||
|  |                   `site:${articleWebsites[i % articleWebsites.length]} ${ | ||||||
|  |                     topics[i % topics.length] | ||||||
|  |                   }`, | ||||||
|  |                   { | ||||||
|  |                     engines: ['bing news'], | ||||||
|  |                     pageno: 1, | ||||||
|  |                     language: 'en', | ||||||
|  |                   }, | ||||||
|  |                 ) | ||||||
|  |               ).results; | ||||||
|  |             }), | ||||||
|  |         ]) | ||||||
|  |       ) | ||||||
|  |         .map((result) => result) | ||||||
|  |         .flat() | ||||||
|  |         .sort(() => Math.random() - 0.5); | ||||||
|  |     } else { | ||||||
|  |       data = ( | ||||||
|  |         await searchSearxng( | ||||||
|  |           `site:${articleWebsites[Math.floor(Math.random() * articleWebsites.length)]} ${topics[Math.floor(Math.random() * topics.length)]}`, | ||||||
|  |           { | ||||||
|  |             engines: ['bing news'], | ||||||
|  |             pageno: 1, | ||||||
|  |             language: 'en', | ||||||
|  |           }, | ||||||
|  |         ) | ||||||
|  |       ).results; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         blogs: data, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         status: 200, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error(`An error occurred in discover route: ${err}`); | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         message: 'An error has occurred', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         status: 500, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										83
									
								
								src/app/api/images/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | |||||||
|  | import handleImageSearch from '@/lib/chains/imageSearchAgent'; | ||||||
|  | import { | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { getAvailableChatModelProviders } from '@/lib/providers'; | ||||||
|  | import { BaseChatModel } from '@langchain/core/language_models/chat_models'; | ||||||
|  | import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; | ||||||
|  | import { ChatOpenAI } from '@langchain/openai'; | ||||||
|  |  | ||||||
|  | interface ChatModel { | ||||||
|  |   provider: string; | ||||||
|  |   model: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface ImageSearchBody { | ||||||
|  |   query: string; | ||||||
|  |   chatHistory: any[]; | ||||||
|  |   chatModel?: ChatModel; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body: ImageSearchBody = await req.json(); | ||||||
|  |  | ||||||
|  |     const chatHistory = body.chatHistory | ||||||
|  |       .map((msg: any) => { | ||||||
|  |         if (msg.role === 'user') { | ||||||
|  |           return new HumanMessage(msg.content); | ||||||
|  |         } else if (msg.role === 'assistant') { | ||||||
|  |           return new AIMessage(msg.content); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .filter((msg) => msg !== undefined) as BaseMessage[]; | ||||||
|  |  | ||||||
|  |     const chatModelProviders = await getAvailableChatModelProviders(); | ||||||
|  |  | ||||||
|  |     const chatModelProvider = | ||||||
|  |       chatModelProviders[ | ||||||
|  |         body.chatModel?.provider || Object.keys(chatModelProviders)[0] | ||||||
|  |       ]; | ||||||
|  |     const chatModel = | ||||||
|  |       chatModelProvider[ | ||||||
|  |         body.chatModel?.model || Object.keys(chatModelProvider)[0] | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |     let llm: BaseChatModel | undefined; | ||||||
|  |  | ||||||
|  |     if (body.chatModel?.provider === 'custom_openai') { | ||||||
|  |       llm = new ChatOpenAI({ | ||||||
|  |         apiKey: getCustomOpenaiApiKey(), | ||||||
|  |         modelName: getCustomOpenaiModelName(), | ||||||
|  |         temperature: 0.7, | ||||||
|  |         configuration: { | ||||||
|  |           baseURL: getCustomOpenaiApiUrl(), | ||||||
|  |         }, | ||||||
|  |       }) as unknown as BaseChatModel; | ||||||
|  |     } else if (chatModelProvider && chatModel) { | ||||||
|  |       llm = chatModel.model; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!llm) { | ||||||
|  |       return Response.json({ error: 'Invalid chat model' }, { status: 400 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const images = await handleImageSearch( | ||||||
|  |       { | ||||||
|  |         chat_history: chatHistory, | ||||||
|  |         query: body.query, | ||||||
|  |       }, | ||||||
|  |       llm, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return Response.json({ images }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error(`An error occurred while searching images: ${err}`); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while searching images' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										47
									
								
								src/app/api/models/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,47 @@ | |||||||
|  | import { | ||||||
|  |   getAvailableChatModelProviders, | ||||||
|  |   getAvailableEmbeddingModelProviders, | ||||||
|  | } from '@/lib/providers'; | ||||||
|  |  | ||||||
|  | export const GET = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const [chatModelProviders, embeddingModelProviders] = await Promise.all([ | ||||||
|  |       getAvailableChatModelProviders(), | ||||||
|  |       getAvailableEmbeddingModelProviders(), | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     Object.keys(chatModelProviders).forEach((provider) => { | ||||||
|  |       Object.keys(chatModelProviders[provider]).forEach((model) => { | ||||||
|  |         delete (chatModelProviders[provider][model] as { model?: unknown }) | ||||||
|  |           .model; | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     Object.keys(embeddingModelProviders).forEach((provider) => { | ||||||
|  |       Object.keys(embeddingModelProviders[provider]).forEach((model) => { | ||||||
|  |         delete (embeddingModelProviders[provider][model] as { model?: unknown }) | ||||||
|  |           .model; | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         chatModelProviders, | ||||||
|  |         embeddingModelProviders, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         status: 200, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('An error occurred while fetching models', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         message: 'An error has occurred.', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         status: 500, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										270
									
								
								src/app/api/search/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,270 @@ | |||||||
|  | import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; | ||||||
|  | import type { Embeddings } from '@langchain/core/embeddings'; | ||||||
|  | import { ChatOpenAI } from '@langchain/openai'; | ||||||
|  | import { | ||||||
|  |   getAvailableChatModelProviders, | ||||||
|  |   getAvailableEmbeddingModelProviders, | ||||||
|  | } from '@/lib/providers'; | ||||||
|  | import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; | ||||||
|  | import { MetaSearchAgentType } from '@/lib/search/metaSearchAgent'; | ||||||
|  | import { | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { searchHandlers } from '@/lib/search'; | ||||||
|  |  | ||||||
|  | interface chatModel { | ||||||
|  |   provider: string; | ||||||
|  |   name: string; | ||||||
|  |   customOpenAIKey?: string; | ||||||
|  |   customOpenAIBaseURL?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface embeddingModel { | ||||||
|  |   provider: string; | ||||||
|  |   name: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface ChatRequestBody { | ||||||
|  |   optimizationMode: 'speed' | 'balanced'; | ||||||
|  |   focusMode: string; | ||||||
|  |   chatModel?: chatModel; | ||||||
|  |   embeddingModel?: embeddingModel; | ||||||
|  |   query: string; | ||||||
|  |   history: Array<[string, string]>; | ||||||
|  |   stream?: boolean; | ||||||
|  |   systemInstructions?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body: ChatRequestBody = await req.json(); | ||||||
|  |  | ||||||
|  |     if (!body.focusMode || !body.query) { | ||||||
|  |       return Response.json( | ||||||
|  |         { message: 'Missing focus mode or query' }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     body.history = body.history || []; | ||||||
|  |     body.optimizationMode = body.optimizationMode || 'balanced'; | ||||||
|  |     body.stream = body.stream || false; | ||||||
|  |  | ||||||
|  |     const history: BaseMessage[] = body.history.map((msg) => { | ||||||
|  |       return msg[0] === 'human' | ||||||
|  |         ? new HumanMessage({ content: msg[1] }) | ||||||
|  |         : new AIMessage({ content: msg[1] }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const [chatModelProviders, embeddingModelProviders] = await Promise.all([ | ||||||
|  |       getAvailableChatModelProviders(), | ||||||
|  |       getAvailableEmbeddingModelProviders(), | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     const chatModelProvider = | ||||||
|  |       body.chatModel?.provider || Object.keys(chatModelProviders)[0]; | ||||||
|  |     const chatModel = | ||||||
|  |       body.chatModel?.name || | ||||||
|  |       Object.keys(chatModelProviders[chatModelProvider])[0]; | ||||||
|  |  | ||||||
|  |     const embeddingModelProvider = | ||||||
|  |       body.embeddingModel?.provider || Object.keys(embeddingModelProviders)[0]; | ||||||
|  |     const embeddingModel = | ||||||
|  |       body.embeddingModel?.name || | ||||||
|  |       Object.keys(embeddingModelProviders[embeddingModelProvider])[0]; | ||||||
|  |  | ||||||
|  |     let llm: BaseChatModel | undefined; | ||||||
|  |     let embeddings: Embeddings | undefined; | ||||||
|  |  | ||||||
|  |     if (body.chatModel?.provider === 'custom_openai') { | ||||||
|  |       llm = new ChatOpenAI({ | ||||||
|  |         modelName: body.chatModel?.name || getCustomOpenaiModelName(), | ||||||
|  |         apiKey: | ||||||
|  |           body.chatModel?.customOpenAIKey || getCustomOpenaiApiKey(), | ||||||
|  |         temperature: 0.7, | ||||||
|  |         configuration: { | ||||||
|  |           baseURL: | ||||||
|  |             body.chatModel?.customOpenAIBaseURL || getCustomOpenaiApiUrl(), | ||||||
|  |         }, | ||||||
|  |       }) as unknown as BaseChatModel; | ||||||
|  |     } else if ( | ||||||
|  |       chatModelProviders[chatModelProvider] && | ||||||
|  |       chatModelProviders[chatModelProvider][chatModel] | ||||||
|  |     ) { | ||||||
|  |       llm = chatModelProviders[chatModelProvider][chatModel] | ||||||
|  |         .model as unknown as BaseChatModel | undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( | ||||||
|  |       embeddingModelProviders[embeddingModelProvider] && | ||||||
|  |       embeddingModelProviders[embeddingModelProvider][embeddingModel] | ||||||
|  |     ) { | ||||||
|  |       embeddings = embeddingModelProviders[embeddingModelProvider][ | ||||||
|  |         embeddingModel | ||||||
|  |       ].model as Embeddings | undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!llm || !embeddings) { | ||||||
|  |       return Response.json( | ||||||
|  |         { message: 'Invalid model selected' }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const searchHandler: MetaSearchAgentType = searchHandlers[body.focusMode]; | ||||||
|  |  | ||||||
|  |     if (!searchHandler) { | ||||||
|  |       return Response.json({ message: 'Invalid focus mode' }, { status: 400 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const emitter = await searchHandler.searchAndAnswer( | ||||||
|  |       body.query, | ||||||
|  |       history, | ||||||
|  |       llm, | ||||||
|  |       embeddings, | ||||||
|  |       body.optimizationMode, | ||||||
|  |       [], | ||||||
|  |       body.systemInstructions || '', | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     if (!body.stream) { | ||||||
|  |       return new Promise( | ||||||
|  |         ( | ||||||
|  |           resolve: (value: Response) => void, | ||||||
|  |           reject: (value: Response) => void, | ||||||
|  |         ) => { | ||||||
|  |           let message = ''; | ||||||
|  |           let sources: any[] = []; | ||||||
|  |  | ||||||
|  |           emitter.on('data', (data: string) => { | ||||||
|  |             try { | ||||||
|  |               const parsedData = JSON.parse(data); | ||||||
|  |               if (parsedData.type === 'response') { | ||||||
|  |                 message += parsedData.data; | ||||||
|  |               } else if (parsedData.type === 'sources') { | ||||||
|  |                 sources = parsedData.data; | ||||||
|  |               } | ||||||
|  |             } catch (error) { | ||||||
|  |               reject( | ||||||
|  |                 Response.json( | ||||||
|  |                   { message: 'Error parsing data' }, | ||||||
|  |                   { status: 500 }, | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             } | ||||||
|  |           }); | ||||||
|  |  | ||||||
|  |           emitter.on('end', () => { | ||||||
|  |             resolve(Response.json({ message, sources }, { status: 200 })); | ||||||
|  |           }); | ||||||
|  |  | ||||||
|  |           emitter.on('error', (error: any) => { | ||||||
|  |             reject( | ||||||
|  |               Response.json( | ||||||
|  |                 { message: 'Search error', error }, | ||||||
|  |                 { status: 500 }, | ||||||
|  |               ), | ||||||
|  |             ); | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const encoder = new TextEncoder(); | ||||||
|  |  | ||||||
|  |     const abortController = new AbortController(); | ||||||
|  |     const { signal } = abortController; | ||||||
|  |  | ||||||
|  |     const stream = new ReadableStream({ | ||||||
|  |       start(controller) { | ||||||
|  |         let sources: any[] = []; | ||||||
|  |  | ||||||
|  |         controller.enqueue( | ||||||
|  |           encoder.encode( | ||||||
|  |             JSON.stringify({ | ||||||
|  |               type: 'init', | ||||||
|  |               data: 'Stream connected', | ||||||
|  |             }) + '\n', | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         signal.addEventListener('abort', () => { | ||||||
|  |           emitter.removeAllListeners(); | ||||||
|  |  | ||||||
|  |           try { | ||||||
|  |             controller.close(); | ||||||
|  |           } catch (error) {} | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         emitter.on('data', (data: string) => { | ||||||
|  |           if (signal.aborted) return; | ||||||
|  |  | ||||||
|  |           try { | ||||||
|  |             const parsedData = JSON.parse(data); | ||||||
|  |  | ||||||
|  |             if (parsedData.type === 'response') { | ||||||
|  |               controller.enqueue( | ||||||
|  |                 encoder.encode( | ||||||
|  |                   JSON.stringify({ | ||||||
|  |                     type: 'response', | ||||||
|  |                     data: parsedData.data, | ||||||
|  |                   }) + '\n', | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             } else if (parsedData.type === 'sources') { | ||||||
|  |               sources = parsedData.data; | ||||||
|  |               controller.enqueue( | ||||||
|  |                 encoder.encode( | ||||||
|  |                   JSON.stringify({ | ||||||
|  |                     type: 'sources', | ||||||
|  |                     data: sources, | ||||||
|  |                   }) + '\n', | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             } | ||||||
|  |           } catch (error) { | ||||||
|  |             controller.error(error); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         emitter.on('end', () => { | ||||||
|  |           if (signal.aborted) return; | ||||||
|  |  | ||||||
|  |           controller.enqueue( | ||||||
|  |             encoder.encode( | ||||||
|  |               JSON.stringify({ | ||||||
|  |                 type: 'done', | ||||||
|  |               }) + '\n', | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |           controller.close(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         emitter.on('error', (error: any) => { | ||||||
|  |           if (signal.aborted) return; | ||||||
|  |  | ||||||
|  |           controller.error(error); | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |       cancel() { | ||||||
|  |         abortController.abort(); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return new Response(stream, { | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'text/event-stream', | ||||||
|  |         'Cache-Control': 'no-cache, no-transform', | ||||||
|  |         Connection: 'keep-alive', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   } catch (err: any) { | ||||||
|  |     console.error(`Error in getting search results: ${err.message}`); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error has occurred.' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										81
									
								
								src/app/api/suggestions/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,81 @@ | |||||||
|  | import generateSuggestions from '@/lib/chains/suggestionGeneratorAgent'; | ||||||
|  | import { | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { getAvailableChatModelProviders } from '@/lib/providers'; | ||||||
|  | import { BaseChatModel } from '@langchain/core/language_models/chat_models'; | ||||||
|  | import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; | ||||||
|  | import { ChatOpenAI } from '@langchain/openai'; | ||||||
|  |  | ||||||
|  | interface ChatModel { | ||||||
|  |   provider: string; | ||||||
|  |   model: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface SuggestionsGenerationBody { | ||||||
|  |   chatHistory: any[]; | ||||||
|  |   chatModel?: ChatModel; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body: SuggestionsGenerationBody = await req.json(); | ||||||
|  |  | ||||||
|  |     const chatHistory = body.chatHistory | ||||||
|  |       .map((msg: any) => { | ||||||
|  |         if (msg.role === 'user') { | ||||||
|  |           return new HumanMessage(msg.content); | ||||||
|  |         } else if (msg.role === 'assistant') { | ||||||
|  |           return new AIMessage(msg.content); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .filter((msg) => msg !== undefined) as BaseMessage[]; | ||||||
|  |  | ||||||
|  |     const chatModelProviders = await getAvailableChatModelProviders(); | ||||||
|  |  | ||||||
|  |     const chatModelProvider = | ||||||
|  |       chatModelProviders[ | ||||||
|  |         body.chatModel?.provider || Object.keys(chatModelProviders)[0] | ||||||
|  |       ]; | ||||||
|  |     const chatModel = | ||||||
|  |       chatModelProvider[ | ||||||
|  |         body.chatModel?.model || Object.keys(chatModelProvider)[0] | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |     let llm: BaseChatModel | undefined; | ||||||
|  |  | ||||||
|  |     if (body.chatModel?.provider === 'custom_openai') { | ||||||
|  |       llm = new ChatOpenAI({ | ||||||
|  |         apiKey: getCustomOpenaiApiKey(), | ||||||
|  |         modelName: getCustomOpenaiModelName(), | ||||||
|  |         temperature: 0.7, | ||||||
|  |         configuration: { | ||||||
|  |           baseURL: getCustomOpenaiApiUrl(), | ||||||
|  |         }, | ||||||
|  |       }) as unknown as BaseChatModel; | ||||||
|  |     } else if (chatModelProvider && chatModel) { | ||||||
|  |       llm = chatModel.model; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!llm) { | ||||||
|  |       return Response.json({ error: 'Invalid chat model' }, { status: 400 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const suggestions = await generateSuggestions( | ||||||
|  |       { | ||||||
|  |         chat_history: chatHistory, | ||||||
|  |       }, | ||||||
|  |       llm, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return Response.json({ suggestions }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error(`An error occurred while generating suggestions: ${err}`); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while generating suggestions' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										134
									
								
								src/app/api/uploads/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,134 @@ | |||||||
|  | import { NextResponse } from 'next/server'; | ||||||
|  | import fs from 'fs'; | ||||||
|  | import path from 'path'; | ||||||
|  | import crypto from 'crypto'; | ||||||
|  | import { getAvailableEmbeddingModelProviders } from '@/lib/providers'; | ||||||
|  | import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf'; | ||||||
|  | import { DocxLoader } from '@langchain/community/document_loaders/fs/docx'; | ||||||
|  | import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters'; | ||||||
|  | import { Document } from 'langchain/document'; | ||||||
|  |  | ||||||
|  | interface FileRes { | ||||||
|  |   fileName: string; | ||||||
|  |   fileExtension: string; | ||||||
|  |   fileId: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const uploadDir = path.join(process.cwd(), 'uploads'); | ||||||
|  |  | ||||||
|  | if (!fs.existsSync(uploadDir)) { | ||||||
|  |   fs.mkdirSync(uploadDir, { recursive: true }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const splitter = new RecursiveCharacterTextSplitter({ | ||||||
|  |   chunkSize: 500, | ||||||
|  |   chunkOverlap: 100, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export async function POST(req: Request) { | ||||||
|  |   try { | ||||||
|  |     const formData = await req.formData(); | ||||||
|  |  | ||||||
|  |     const files = formData.getAll('files') as File[]; | ||||||
|  |     const embedding_model = formData.get('embedding_model'); | ||||||
|  |     const embedding_model_provider = formData.get('embedding_model_provider'); | ||||||
|  |  | ||||||
|  |     if (!embedding_model || !embedding_model_provider) { | ||||||
|  |       return NextResponse.json( | ||||||
|  |         { message: 'Missing embedding model or provider' }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const embeddingModels = await getAvailableEmbeddingModelProviders(); | ||||||
|  |     const provider = | ||||||
|  |       embedding_model_provider ?? Object.keys(embeddingModels)[0]; | ||||||
|  |     const embeddingModel = | ||||||
|  |       embedding_model ?? Object.keys(embeddingModels[provider as string])[0]; | ||||||
|  |  | ||||||
|  |     let embeddingsModel = | ||||||
|  |       embeddingModels[provider as string]?.[embeddingModel as string]?.model; | ||||||
|  |     if (!embeddingsModel) { | ||||||
|  |       return NextResponse.json( | ||||||
|  |         { message: 'Invalid embedding model selected' }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const processedFiles: FileRes[] = []; | ||||||
|  |  | ||||||
|  |     await Promise.all( | ||||||
|  |       files.map(async (file: any) => { | ||||||
|  |         const fileExtension = file.name.split('.').pop(); | ||||||
|  |         if (!['pdf', 'docx', 'txt'].includes(fileExtension!)) { | ||||||
|  |           return NextResponse.json( | ||||||
|  |             { message: 'File type not supported' }, | ||||||
|  |             { status: 400 }, | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const uniqueFileName = `${crypto.randomBytes(16).toString('hex')}.${fileExtension}`; | ||||||
|  |         const filePath = path.join(uploadDir, uniqueFileName); | ||||||
|  |  | ||||||
|  |         const buffer = Buffer.from(await file.arrayBuffer()); | ||||||
|  |         fs.writeFileSync(filePath, new Uint8Array(buffer)); | ||||||
|  |  | ||||||
|  |         let docs: any[] = []; | ||||||
|  |         if (fileExtension === 'pdf') { | ||||||
|  |           const loader = new PDFLoader(filePath); | ||||||
|  |           docs = await loader.load(); | ||||||
|  |         } else if (fileExtension === 'docx') { | ||||||
|  |           const loader = new DocxLoader(filePath); | ||||||
|  |           docs = await loader.load(); | ||||||
|  |         } else if (fileExtension === 'txt') { | ||||||
|  |           const text = fs.readFileSync(filePath, 'utf-8'); | ||||||
|  |           docs = [ | ||||||
|  |             new Document({ pageContent: text, metadata: { title: file.name } }), | ||||||
|  |           ]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const splitted = await splitter.splitDocuments(docs); | ||||||
|  |  | ||||||
|  |         const extractedDataPath = filePath.replace(/\.\w+$/, '-extracted.json'); | ||||||
|  |         fs.writeFileSync( | ||||||
|  |           extractedDataPath, | ||||||
|  |           JSON.stringify({ | ||||||
|  |             title: file.name, | ||||||
|  |             contents: splitted.map((doc) => doc.pageContent), | ||||||
|  |           }), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         const embeddings = await embeddingsModel.embedDocuments( | ||||||
|  |           splitted.map((doc) => doc.pageContent), | ||||||
|  |         ); | ||||||
|  |         const embeddingsDataPath = filePath.replace( | ||||||
|  |           /\.\w+$/, | ||||||
|  |           '-embeddings.json', | ||||||
|  |         ); | ||||||
|  |         fs.writeFileSync( | ||||||
|  |           embeddingsDataPath, | ||||||
|  |           JSON.stringify({ | ||||||
|  |             title: file.name, | ||||||
|  |             embeddings, | ||||||
|  |           }), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         processedFiles.push({ | ||||||
|  |           fileName: file.name, | ||||||
|  |           fileExtension: fileExtension, | ||||||
|  |           fileId: uniqueFileName.replace(/\.\w+$/, ''), | ||||||
|  |         }); | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return NextResponse.json({ | ||||||
|  |       files: processedFiles, | ||||||
|  |     }); | ||||||
|  |   } catch (error) { | ||||||
|  |     console.error('Error uploading file:', error); | ||||||
|  |     return NextResponse.json( | ||||||
|  |       { message: 'An error has occurred.' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								src/app/api/videos/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | |||||||
|  | import handleVideoSearch from '@/lib/chains/videoSearchAgent'; | ||||||
|  | import { | ||||||
|  |   getCustomOpenaiApiKey, | ||||||
|  |   getCustomOpenaiApiUrl, | ||||||
|  |   getCustomOpenaiModelName, | ||||||
|  | } from '@/lib/config'; | ||||||
|  | import { getAvailableChatModelProviders } from '@/lib/providers'; | ||||||
|  | import { BaseChatModel } from '@langchain/core/language_models/chat_models'; | ||||||
|  | import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; | ||||||
|  | import { ChatOpenAI } from '@langchain/openai'; | ||||||
|  |  | ||||||
|  | interface ChatModel { | ||||||
|  |   provider: string; | ||||||
|  |   model: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface VideoSearchBody { | ||||||
|  |   query: string; | ||||||
|  |   chatHistory: any[]; | ||||||
|  |   chatModel?: ChatModel; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body: VideoSearchBody = await req.json(); | ||||||
|  |  | ||||||
|  |     const chatHistory = body.chatHistory | ||||||
|  |       .map((msg: any) => { | ||||||
|  |         if (msg.role === 'user') { | ||||||
|  |           return new HumanMessage(msg.content); | ||||||
|  |         } else if (msg.role === 'assistant') { | ||||||
|  |           return new AIMessage(msg.content); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .filter((msg) => msg !== undefined) as BaseMessage[]; | ||||||
|  |  | ||||||
|  |     const chatModelProviders = await getAvailableChatModelProviders(); | ||||||
|  |  | ||||||
|  |     const chatModelProvider = | ||||||
|  |       chatModelProviders[ | ||||||
|  |         body.chatModel?.provider || Object.keys(chatModelProviders)[0] | ||||||
|  |       ]; | ||||||
|  |     const chatModel = | ||||||
|  |       chatModelProvider[ | ||||||
|  |         body.chatModel?.model || Object.keys(chatModelProvider)[0] | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |     let llm: BaseChatModel | undefined; | ||||||
|  |  | ||||||
|  |     if (body.chatModel?.provider === 'custom_openai') { | ||||||
|  |       llm = new ChatOpenAI({ | ||||||
|  |         apiKey: getCustomOpenaiApiKey(), | ||||||
|  |         modelName: getCustomOpenaiModelName(), | ||||||
|  |         temperature: 0.7, | ||||||
|  |         configuration: { | ||||||
|  |           baseURL: getCustomOpenaiApiUrl(), | ||||||
|  |         }, | ||||||
|  |       }) as unknown as BaseChatModel; | ||||||
|  |     } else if (chatModelProvider && chatModel) { | ||||||
|  |       llm = chatModel.model; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!llm) { | ||||||
|  |       return Response.json({ error: 'Invalid chat model' }, { status: 400 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const videos = await handleVideoSearch( | ||||||
|  |       { | ||||||
|  |         chat_history: chatHistory, | ||||||
|  |         query: body.query, | ||||||
|  |       }, | ||||||
|  |       llm, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return Response.json({ videos }, { status: 200 }); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error(`An error occurred while searching videos: ${err}`); | ||||||
|  |     return Response.json( | ||||||
|  |       { message: 'An error occurred while searching videos' }, | ||||||
|  |       { status: 500 }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										167
									
								
								src/app/api/weather/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,167 @@ | |||||||
|  | export const POST = async (req: Request) => { | ||||||
|  |   try { | ||||||
|  |     const body: { lat: number; lng: number; temperatureUnit: 'C' | 'F' } = | ||||||
|  |       await req.json(); | ||||||
|  |  | ||||||
|  |     if (!body.lat || !body.lng) { | ||||||
|  |       return Response.json( | ||||||
|  |         { | ||||||
|  |           message: 'Invalid request.', | ||||||
|  |         }, | ||||||
|  |         { status: 400 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const res = await fetch( | ||||||
|  |       `https://api.open-meteo.com/v1/forecast?latitude=${body.lat}&longitude=${body.lng}¤t=weather_code,temperature_2m,is_day,relative_humidity_2m,wind_speed_10m&timezone=auto${body.temperatureUnit === 'C' ? '' : '&temperature_unit=fahrenheit'}`, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const data = await res.json(); | ||||||
|  |  | ||||||
|  |     if (data.error) { | ||||||
|  |       console.error(`Error fetching weather data: ${data.reason}`); | ||||||
|  |       return Response.json( | ||||||
|  |         { | ||||||
|  |           message: 'An error has occurred.', | ||||||
|  |         }, | ||||||
|  |         { status: 500 }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const weather: { | ||||||
|  |       temperature: number; | ||||||
|  |       condition: string; | ||||||
|  |       humidity: number; | ||||||
|  |       windSpeed: number; | ||||||
|  |       icon: string; | ||||||
|  |       temperatureUnit: 'C' | 'F'; | ||||||
|  |     } = { | ||||||
|  |       temperature: data.current.temperature_2m, | ||||||
|  |       condition: '', | ||||||
|  |       humidity: data.current.relative_humidity_2m, | ||||||
|  |       windSpeed: data.current.wind_speed_10m, | ||||||
|  |       icon: '', | ||||||
|  |       temperatureUnit: body.temperatureUnit, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const code = data.current.weather_code; | ||||||
|  |     const isDay = data.current.is_day === 1; | ||||||
|  |     const dayOrNight = isDay ? 'day' : 'night'; | ||||||
|  |  | ||||||
|  |     switch (code) { | ||||||
|  |       case 0: | ||||||
|  |         weather.icon = `clear-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Clear'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 1: | ||||||
|  |         weather.condition = 'Mainly Clear'; | ||||||
|  |       case 2: | ||||||
|  |         weather.condition = 'Partly Cloudy'; | ||||||
|  |       case 3: | ||||||
|  |         weather.icon = `cloudy-1-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Cloudy'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 45: | ||||||
|  |         weather.condition = 'Fog'; | ||||||
|  |       case 48: | ||||||
|  |         weather.icon = `fog-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Fog'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 51: | ||||||
|  |         weather.condition = 'Light Drizzle'; | ||||||
|  |       case 53: | ||||||
|  |         weather.condition = 'Moderate Drizzle'; | ||||||
|  |       case 55: | ||||||
|  |         weather.icon = `rainy-1-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Dense Drizzle'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 56: | ||||||
|  |         weather.condition = 'Light Freezing Drizzle'; | ||||||
|  |       case 57: | ||||||
|  |         weather.icon = `frost-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Dense Freezing Drizzle'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 61: | ||||||
|  |         weather.condition = 'Slight Rain'; | ||||||
|  |       case 63: | ||||||
|  |         weather.condition = 'Moderate Rain'; | ||||||
|  |       case 65: | ||||||
|  |         weather.condition = 'Heavy Rain'; | ||||||
|  |         weather.icon = `rainy-2-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 66: | ||||||
|  |         weather.condition = 'Light Freezing Rain'; | ||||||
|  |       case 67: | ||||||
|  |         weather.condition = 'Heavy Freezing Rain'; | ||||||
|  |         weather.icon = 'rain-and-sleet-mix'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 71: | ||||||
|  |         weather.condition = 'Slight Snow Fall'; | ||||||
|  |       case 73: | ||||||
|  |         weather.condition = 'Moderate Snow Fall'; | ||||||
|  |       case 75: | ||||||
|  |         weather.condition = 'Heavy Snow Fall'; | ||||||
|  |         weather.icon = `snowy-2-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 77: | ||||||
|  |         weather.condition = 'Snow'; | ||||||
|  |         weather.icon = `snowy-1-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 80: | ||||||
|  |         weather.condition = 'Slight Rain Showers'; | ||||||
|  |       case 81: | ||||||
|  |         weather.condition = 'Moderate Rain Showers'; | ||||||
|  |       case 82: | ||||||
|  |         weather.condition = 'Heavy Rain Showers'; | ||||||
|  |         weather.icon = `rainy-3-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 85: | ||||||
|  |         weather.condition = 'Slight Snow Showers'; | ||||||
|  |       case 86: | ||||||
|  |         weather.condition = 'Moderate Snow Showers'; | ||||||
|  |       case 87: | ||||||
|  |         weather.condition = 'Heavy Snow Showers'; | ||||||
|  |         weather.icon = `snowy-3-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 95: | ||||||
|  |         weather.condition = 'Thunderstorm'; | ||||||
|  |         weather.icon = `scattered-thunderstorms-${dayOrNight}`; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       case 96: | ||||||
|  |         weather.condition = 'Thunderstorm with Slight Hail'; | ||||||
|  |       case 99: | ||||||
|  |         weather.condition = 'Thunderstorm with Heavy Hail'; | ||||||
|  |         weather.icon = 'severe-thunderstorm'; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |       default: | ||||||
|  |         weather.icon = `clear-${dayOrNight}`; | ||||||
|  |         weather.condition = 'Clear'; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Response.json(weather); | ||||||
|  |   } catch (err) { | ||||||
|  |     console.error('An error occurred while getting home widgets', err); | ||||||
|  |     return Response.json( | ||||||
|  |       { | ||||||
|  |         message: 'An error has occurred.', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         status: 500, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										9
									
								
								src/app/c/[chatId]/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | import ChatWindow from '@/components/ChatWindow'; | ||||||
|  | import React from 'react'; | ||||||
|  |  | ||||||
|  | const Page = ({ params }: { params: Promise<{ chatId: string }> }) => { | ||||||
|  |   const { chatId } = React.use(params); | ||||||
|  |   return <ChatWindow id={chatId} />; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default Page; | ||||||
| @@ -19,7 +19,7 @@ const Page = () => { | |||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const fetchData = async () => { |     const fetchData = async () => { | ||||||
|       try { |       try { | ||||||
|         const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/discover`, { |         const res = await fetch(`/api/discover`, { | ||||||
|           method: 'GET', |           method: 'GET', | ||||||
|           headers: { |           headers: { | ||||||
|             'Content-Type': 'application/json', |             'Content-Type': 'application/json', | ||||||
| Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB | 
| @@ -11,3 +11,11 @@ | |||||||
|     display: none; |     display: none; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | @media screen and (-webkit-min-device-pixel-ratio: 0) { | ||||||
|  |   select, | ||||||
|  |   textarea, | ||||||
|  |   input { | ||||||
|  |     font-size: 16px !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								src/app/library/layout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | |||||||
|  | import { Metadata } from 'next'; | ||||||
|  | import React from 'react'; | ||||||
|  |  | ||||||
|  | export const metadata: Metadata = { | ||||||
|  |   title: 'Library - Perplexica', | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const Layout = ({ children }: { children: React.ReactNode }) => { | ||||||
|  |   return <div>{children}</div>; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default Layout; | ||||||
| @@ -1,9 +1,8 @@ | |||||||
| 'use client'; | 'use client'; | ||||||
| 
 | 
 | ||||||
| import DeleteChat from '@/components/DeleteChat'; | import DeleteChat from '@/components/DeleteChat'; | ||||||
| import { formatTimeDifference } from '@/lib/utils'; | import { cn, formatTimeDifference } from '@/lib/utils'; | ||||||
| import { cn } from '@/lib/utils'; | import { BookOpenText, ClockIcon, Delete, ScanEye } from 'lucide-react'; | ||||||
| import { BookOpenText, ClockIcon } from 'lucide-react'; |  | ||||||
| import Link from 'next/link'; | import Link from 'next/link'; | ||||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||||
| 
 | 
 | ||||||
| @@ -22,7 +21,7 @@ const Page = () => { | |||||||
|     const fetchChats = async () => { |     const fetchChats = async () => { | ||||||
|       setLoading(true); |       setLoading(true); | ||||||
| 
 | 
 | ||||||
|       const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chats`, { |       const res = await fetch(`/api/chats`, { | ||||||
|         method: 'GET', |         method: 'GET', | ||||||
|         headers: { |         headers: { | ||||||
|           'Content-Type': 'application/json', |           'Content-Type': 'application/json', | ||||||
							
								
								
									
										54
									
								
								src/app/manifest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,54 @@ | |||||||
|  | import type { MetadataRoute } from 'next'; | ||||||
|  |  | ||||||
|  | export default function manifest(): MetadataRoute.Manifest { | ||||||
|  |   return { | ||||||
|  |     name: 'Perplexica - Chat with the internet', | ||||||
|  |     short_name: 'Perplexica', | ||||||
|  |     description: | ||||||
|  |       'Perplexica is an AI powered chatbot that is connected to the internet.', | ||||||
|  |     start_url: '/', | ||||||
|  |     display: 'standalone', | ||||||
|  |     background_color: '#0a0a0a', | ||||||
|  |     theme_color: '#0a0a0a', | ||||||
|  |     screenshots: [ | ||||||
|  |       { | ||||||
|  |         src: '/screenshots/p1.png', | ||||||
|  |         form_factor: 'wide', | ||||||
|  |         sizes: '2560x1600', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         src: '/screenshots/p2.png', | ||||||
|  |         form_factor: 'wide', | ||||||
|  |         sizes: '2560x1600', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         src: '/screenshots/p1_small.png', | ||||||
|  |         form_factor: 'narrow', | ||||||
|  |         sizes: '828x1792', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         src: '/screenshots/p2_small.png', | ||||||
|  |         form_factor: 'narrow', | ||||||
|  |         sizes: '828x1792', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |     icons: [ | ||||||
|  |       { | ||||||
|  |         src: '/icon-50.png', | ||||||
|  |         sizes: '50x50', | ||||||
|  |         type: 'image/png' as const, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         src: '/icon-100.png', | ||||||
|  |         sizes: '100x100', | ||||||
|  |         type: 'image/png', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         src: '/icon.png', | ||||||
|  |         sizes: '440x440', | ||||||
|  |         type: 'image/png', | ||||||
|  |         purpose: 'any', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @@ -7,6 +7,7 @@ import { Switch } from '@headlessui/react'; | |||||||
| import ThemeSwitcher from '@/components/theme/Switcher'; | import ThemeSwitcher from '@/components/theme/Switcher'; | ||||||
| import { ImagesIcon, VideoIcon } from 'lucide-react'; | import { ImagesIcon, VideoIcon } from 'lucide-react'; | ||||||
| import Link from 'next/link'; | import Link from 'next/link'; | ||||||
|  | import { PROVIDER_METADATA } from '@/lib/providers'; | ||||||
| 
 | 
 | ||||||
| interface SettingsType { | interface SettingsType { | ||||||
|   chatModelProviders: { |   chatModelProviders: { | ||||||
| @@ -20,6 +21,9 @@ interface SettingsType { | |||||||
|   anthropicApiKey: string; |   anthropicApiKey: string; | ||||||
|   geminiApiKey: string; |   geminiApiKey: string; | ||||||
|   ollamaApiUrl: string; |   ollamaApiUrl: string; | ||||||
|  |   lmStudioApiUrl: string; | ||||||
|  |   deepseekApiKey: string; | ||||||
|  |   aimlApiKey: string; | ||||||
|   customOpenaiApiKey: string; |   customOpenaiApiKey: string; | ||||||
|   customOpenaiApiUrl: string; |   customOpenaiApiUrl: string; | ||||||
|   customOpenaiModelName: string; |   customOpenaiModelName: string; | ||||||
| @@ -54,6 +58,38 @@ const Input = ({ className, isSaving, onSave, ...restProps }: InputProps) => { | |||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | interface TextareaProps extends React.InputHTMLAttributes<HTMLTextAreaElement> { | ||||||
|  |   isSaving?: boolean; | ||||||
|  |   onSave?: (value: string) => void; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Textarea = ({ | ||||||
|  |   className, | ||||||
|  |   isSaving, | ||||||
|  |   onSave, | ||||||
|  |   ...restProps | ||||||
|  | }: TextareaProps) => { | ||||||
|  |   return ( | ||||||
|  |     <div className="relative"> | ||||||
|  |       <textarea | ||||||
|  |         placeholder="Any special instructions for the LLM" | ||||||
|  |         className="placeholder:text-sm text-sm w-full flex items-center justify-between p-3 bg-light-secondary dark:bg-dark-secondary rounded-lg hover:bg-light-200 dark:hover:bg-dark-200 transition-colors" | ||||||
|  |         rows={4} | ||||||
|  |         onBlur={(e) => onSave?.(e.target.value)} | ||||||
|  |         {...restProps} | ||||||
|  |       /> | ||||||
|  |       {isSaving && ( | ||||||
|  |         <div className="absolute right-3 top-3"> | ||||||
|  |           <Loader2 | ||||||
|  |             size={16} | ||||||
|  |             className="animate-spin text-black/70 dark:text-white/70" | ||||||
|  |           /> | ||||||
|  |         </div> | ||||||
|  |       )} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const Select = ({ | const Select = ({ | ||||||
|   className, |   className, | ||||||
|   options, |   options, | ||||||
| @@ -108,95 +144,18 @@ const Page = () => { | |||||||
|   const [selectedEmbeddingModel, setSelectedEmbeddingModel] = useState< |   const [selectedEmbeddingModel, setSelectedEmbeddingModel] = useState< | ||||||
|     string | null |     string | null | ||||||
|   >(null); |   >(null); | ||||||
|   const [isLoading, setIsLoading] = useState(false); |   const [isLoading, setIsLoading] = useState(true); | ||||||
|   const [automaticImageSearch, setAutomaticImageSearch] = useState(false); |   const [automaticImageSearch, setAutomaticImageSearch] = useState(false); | ||||||
|   const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false); |   const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false); | ||||||
|  |   const [systemInstructions, setSystemInstructions] = useState<string>(''); | ||||||
|  |   const [temperatureUnit, setTemperatureUnit] = useState<'C' | 'F'>('C'); | ||||||
|   const [savingStates, setSavingStates] = useState<Record<string, boolean>>({}); |   const [savingStates, setSavingStates] = useState<Record<string, boolean>>({}); | ||||||
| 
 | 
 | ||||||
|   const [password, setPassword] = useState(''); |  | ||||||
|   const [passwordSubmitted, setPasswordSubmitted] = useState(false); |  | ||||||
|   const [isPasswordValid, setIsPasswordValid] = useState(true); |  | ||||||
| 
 |  | ||||||
|   const handlePasswordSubmit = async () => { |  | ||||||
|     setIsLoading(true); |  | ||||||
|     setPasswordSubmitted(true); |  | ||||||
| 
 |  | ||||||
|     const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, { |  | ||||||
|       headers: { |  | ||||||
|         'Content-Type': 'application/json', |  | ||||||
|         Authorization: `Bearer ${password}`, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     if (res.status === 401) { |  | ||||||
|       setIsPasswordValid(false); |  | ||||||
|       setPasswordSubmitted(false); |  | ||||||
|       setIsLoading(false); |  | ||||||
|       return; |  | ||||||
|     } else { |  | ||||||
|       setIsPasswordValid(true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const data = (await res.json()) as SettingsType; |  | ||||||
|     setConfig(data); |  | ||||||
| 
 |  | ||||||
|     const chatModelProvidersKeys = Object.keys(data.chatModelProviders || {}); |  | ||||||
|     const embeddingModelProvidersKeys = Object.keys( |  | ||||||
|       data.embeddingModelProviders || {}, |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     const defaultChatModelProvider = |  | ||||||
|       chatModelProvidersKeys.length > 0 ? chatModelProvidersKeys[0] : ''; |  | ||||||
|     const defaultEmbeddingModelProvider = |  | ||||||
|       embeddingModelProvidersKeys.length > 0 |  | ||||||
|         ? embeddingModelProvidersKeys[0] |  | ||||||
|         : ''; |  | ||||||
| 
 |  | ||||||
|     const chatModelProvider = |  | ||||||
|       localStorage.getItem('chatModelProvider') || |  | ||||||
|       defaultChatModelProvider || |  | ||||||
|       ''; |  | ||||||
|     const chatModel = |  | ||||||
|       localStorage.getItem('chatModel') || |  | ||||||
|       (data.chatModelProviders && |  | ||||||
|       data.chatModelProviders[chatModelProvider]?.length > 0 |  | ||||||
|         ? data.chatModelProviders[chatModelProvider][0].name |  | ||||||
|         : undefined) || |  | ||||||
|       ''; |  | ||||||
|     const embeddingModelProvider = |  | ||||||
|       localStorage.getItem('embeddingModelProvider') || |  | ||||||
|       defaultEmbeddingModelProvider || |  | ||||||
|       ''; |  | ||||||
|     const embeddingModel = |  | ||||||
|       localStorage.getItem('embeddingModel') || |  | ||||||
|       (data.embeddingModelProviders && |  | ||||||
|         data.embeddingModelProviders[embeddingModelProvider]?.[0].name) || |  | ||||||
|       ''; |  | ||||||
| 
 |  | ||||||
|     setSelectedChatModelProvider(chatModelProvider); |  | ||||||
|     setSelectedChatModel(chatModel); |  | ||||||
|     setSelectedEmbeddingModelProvider(embeddingModelProvider); |  | ||||||
|     setSelectedEmbeddingModel(embeddingModel); |  | ||||||
|     setChatModels(data.chatModelProviders || {}); |  | ||||||
|     setEmbeddingModels(data.embeddingModelProviders || {}); |  | ||||||
| 
 |  | ||||||
|     setAutomaticImageSearch( |  | ||||||
|       localStorage.getItem('autoImageSearch') === 'true', |  | ||||||
|     ); |  | ||||||
|     setAutomaticVideoSearch( |  | ||||||
|       localStorage.getItem('autoVideoSearch') === 'true', |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     setIsLoading(false); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const fetchConfig = async () => { |     const fetchConfig = async () => { | ||||||
|       setIsLoading(true); |       const res = await fetch(`/api/config`, { | ||||||
|       const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, { |  | ||||||
|         headers: { |         headers: { | ||||||
|           'Content-Type': 'application/json', |           'Content-Type': 'application/json', | ||||||
|           Authorization: `Bearer ${password}`, |  | ||||||
|         }, |         }, | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
| @@ -250,6 +209,10 @@ const Page = () => { | |||||||
|         localStorage.getItem('autoVideoSearch') === 'true', |         localStorage.getItem('autoVideoSearch') === 'true', | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|  |       setSystemInstructions(localStorage.getItem('systemInstructions')!); | ||||||
|  | 
 | ||||||
|  |       setTemperatureUnit(localStorage.getItem('temperatureUnit')! as 'C' | 'F'); | ||||||
|  | 
 | ||||||
|       setIsLoading(false); |       setIsLoading(false); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @@ -265,21 +228,13 @@ const Page = () => { | |||||||
|         [key]: value, |         [key]: value, | ||||||
|       } as SettingsType; |       } as SettingsType; | ||||||
| 
 | 
 | ||||||
|       const response = await fetch( |       const response = await fetch(`/api/config`, { | ||||||
|         `${process.env.NEXT_PUBLIC_API_URL}/config`, |  | ||||||
|         { |  | ||||||
|         method: 'POST', |         method: 'POST', | ||||||
|         headers: { |         headers: { | ||||||
|           'Content-Type': 'application/json', |           'Content-Type': 'application/json', | ||||||
|             Authorization: `Bearer ${password}`, |  | ||||||
|         }, |         }, | ||||||
|         body: JSON.stringify(updatedConfig), |         body: JSON.stringify(updatedConfig), | ||||||
|         }, |       }); | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       if (response.status === 401) { |  | ||||||
|         throw new Error('Unauthorized'); |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       if (!response.ok) { |       if (!response.ok) { | ||||||
|         throw new Error('Failed to update config'); |         throw new Error('Failed to update config'); | ||||||
| @@ -291,10 +246,9 @@ const Page = () => { | |||||||
|         key.toLowerCase().includes('api') || |         key.toLowerCase().includes('api') || | ||||||
|         key.toLowerCase().includes('url') |         key.toLowerCase().includes('url') | ||||||
|       ) { |       ) { | ||||||
|         const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, { |         const res = await fetch(`/api/config`, { | ||||||
|           headers: { |           headers: { | ||||||
|             'Content-Type': 'application/json', |             'Content-Type': 'application/json', | ||||||
|             Authorization: `Bearer ${password}`, |  | ||||||
|           }, |           }, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @@ -415,6 +369,10 @@ const Page = () => { | |||||||
|         localStorage.setItem('embeddingModelProvider', value); |         localStorage.setItem('embeddingModelProvider', value); | ||||||
|       } else if (key === 'embeddingModel') { |       } else if (key === 'embeddingModel') { | ||||||
|         localStorage.setItem('embeddingModel', value); |         localStorage.setItem('embeddingModel', value); | ||||||
|  |       } else if (key === 'systemInstructions') { | ||||||
|  |         localStorage.setItem('systemInstructions', value); | ||||||
|  |       } else if (key === 'temperatureUnit') { | ||||||
|  |         localStorage.setItem('temperatureUnit', value.toString()); | ||||||
|       } |       } | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       console.error('Failed to save:', err); |       console.error('Failed to save:', err); | ||||||
| @@ -460,43 +418,38 @@ const Page = () => { | |||||||
|             /> |             /> | ||||||
|           </svg> |           </svg> | ||||||
|         </div> |         </div> | ||||||
|       ) : !passwordSubmitted ? ( |  | ||||||
|         <div className="flex flex-col max-w-md mx-auto mt-10 p-6 bg-light-secondary dark:bg-dark-secondary border border-light-200 dark:border-dark-200 rounded-2xl"> |  | ||||||
|           <h2 className="text-sm text-black/80 dark:text-white/80"> |  | ||||||
|           Enter the password to access the settings |  | ||||||
|           </h2> |  | ||||||
|           <div className="flex flex-col"> |  | ||||||
|             <Input |  | ||||||
|               type="password" |  | ||||||
|               placeholder="Password" |  | ||||||
|               className="mt-4" |  | ||||||
|               disabled={isLoading} |  | ||||||
|               onChange={(e) => setPassword(e.target.value)} |  | ||||||
|             /> |  | ||||||
|           </div> |  | ||||||
|           {!isPasswordValid && ( |  | ||||||
|             <p className="text-xs text-red-500 mt-2"> |  | ||||||
|               Password is incorrect |  | ||||||
|             </p> |  | ||||||
|           )} |  | ||||||
|           <button |  | ||||||
|             onClick={handlePasswordSubmit} |  | ||||||
|             disabled={isLoading} |  | ||||||
|             className="bg-[#24A0ED] flex flex-row items-center text-xs mt-4 text-white disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#ececec21] rounded-full px-4 py-2" |  | ||||||
|           > |  | ||||||
|             Submit |  | ||||||
|           </button> |  | ||||||
|         </div> |  | ||||||
|       ) : ( |       ) : ( | ||||||
|         config && ( |         config && ( | ||||||
|           <div className="flex flex-col space-y-6 pb-28 lg:pb-8"> |           <div className="flex flex-col space-y-6 pb-28 lg:pb-8"> | ||||||
|             <SettingsSection title="Appearance"> |             <SettingsSection title="Preferences"> | ||||||
|               <div className="flex flex-col space-y-1"> |               <div className="flex flex-col space-y-1"> | ||||||
|                 <p className="text-black/70 dark:text-white/70 text-sm"> |                 <p className="text-black/70 dark:text-white/70 text-sm"> | ||||||
|                   Theme |                   Theme | ||||||
|                 </p> |                 </p> | ||||||
|                 <ThemeSwitcher /> |                 <ThemeSwitcher /> | ||||||
|               </div> |               </div> | ||||||
|  |               <div className="flex flex-col space-y-1"> | ||||||
|  |                 <p className="text-black/70 dark:text-white/70 text-sm"> | ||||||
|  |                   Temperature Unit | ||||||
|  |                 </p> | ||||||
|  |                 <Select | ||||||
|  |                   value={temperatureUnit ?? undefined} | ||||||
|  |                   onChange={(e) => { | ||||||
|  |                     setTemperatureUnit(e.target.value as 'C' | 'F'); | ||||||
|  |                     saveConfig('temperatureUnit', e.target.value); | ||||||
|  |                   }} | ||||||
|  |                   options={[ | ||||||
|  |                     { | ||||||
|  |                       label: 'Celsius', | ||||||
|  |                       value: 'C', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                       label: 'Fahrenheit', | ||||||
|  |                       value: 'F', | ||||||
|  |                     }, | ||||||
|  |                   ]} | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|             </SettingsSection> |             </SettingsSection> | ||||||
| 
 | 
 | ||||||
|             <SettingsSection title="Automatic Search"> |             <SettingsSection title="Automatic Search"> | ||||||
| @@ -587,6 +540,19 @@ const Page = () => { | |||||||
|               </div> |               </div> | ||||||
|             </SettingsSection> |             </SettingsSection> | ||||||
| 
 | 
 | ||||||
|  |             <SettingsSection title="System Instructions"> | ||||||
|  |               <div className="flex flex-col space-y-4"> | ||||||
|  |                 <Textarea | ||||||
|  |                   value={systemInstructions ?? undefined} | ||||||
|  |                   isSaving={savingStates['systemInstructions']} | ||||||
|  |                   onChange={(e) => { | ||||||
|  |                     setSystemInstructions(e.target.value); | ||||||
|  |                   }} | ||||||
|  |                   onSave={(value) => saveConfig('systemInstructions', value)} | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </SettingsSection> | ||||||
|  | 
 | ||||||
|             <SettingsSection title="Model Settings"> |             <SettingsSection title="Model Settings"> | ||||||
|               {config.chatModelProviders && ( |               {config.chatModelProviders && ( | ||||||
|                 <div className="flex flex-col space-y-4"> |                 <div className="flex flex-col space-y-4"> | ||||||
| @@ -611,6 +577,7 @@ const Page = () => { | |||||||
|                         (provider) => ({ |                         (provider) => ({ | ||||||
|                           value: provider, |                           value: provider, | ||||||
|                           label: |                           label: | ||||||
|  |                             (PROVIDER_METADATA as any)[provider]?.displayName || | ||||||
|                             provider.charAt(0).toUpperCase() + |                             provider.charAt(0).toUpperCase() + | ||||||
|                               provider.slice(1), |                               provider.slice(1), | ||||||
|                         }), |                         }), | ||||||
| @@ -753,6 +720,7 @@ const Page = () => { | |||||||
|                         (provider) => ({ |                         (provider) => ({ | ||||||
|                           value: provider, |                           value: provider, | ||||||
|                           label: |                           label: | ||||||
|  |                             (PROVIDER_METADATA as any)[provider]?.displayName || | ||||||
|                             provider.charAt(0).toUpperCase() + |                             provider.charAt(0).toUpperCase() + | ||||||
|                               provider.slice(1), |                               provider.slice(1), | ||||||
|                         }), |                         }), | ||||||
| @@ -902,6 +870,63 @@ const Page = () => { | |||||||
|                     onSave={(value) => saveConfig('geminiApiKey', value)} |                     onSave={(value) => saveConfig('geminiApiKey', value)} | ||||||
|                   /> |                   /> | ||||||
|                 </div> |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <div className="flex flex-col space-y-1"> | ||||||
|  |                   <p className="text-black/70 dark:text-white/70 text-sm"> | ||||||
|  |                     Deepseek API Key | ||||||
|  |                   </p> | ||||||
|  |                   <Input | ||||||
|  |                     type="text" | ||||||
|  |                     placeholder="Deepseek API Key" | ||||||
|  |                     value={config.deepseekApiKey} | ||||||
|  |                     isSaving={savingStates['deepseekApiKey']} | ||||||
|  |                     onChange={(e) => { | ||||||
|  |                       setConfig((prev) => ({ | ||||||
|  |                         ...prev!, | ||||||
|  |                         deepseekApiKey: e.target.value, | ||||||
|  |                       })); | ||||||
|  |                     }} | ||||||
|  |                     onSave={(value) => saveConfig('deepseekApiKey', value)} | ||||||
|  |                   /> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <div className="flex flex-col space-y-1"> | ||||||
|  |                   <p className="text-black/70 dark:text-white/70 text-sm"> | ||||||
|  |                     AI/ML API Key | ||||||
|  |                   </p> | ||||||
|  |                   <Input | ||||||
|  |                     type="text" | ||||||
|  |                     placeholder="AI/ML API Key" | ||||||
|  |                     value={config.aimlApiKey} | ||||||
|  |                     isSaving={savingStates['aimlApiKey']} | ||||||
|  |                     onChange={(e) => { | ||||||
|  |                       setConfig((prev) => ({ | ||||||
|  |                         ...prev!, | ||||||
|  |                         aimlApiKey: e.target.value, | ||||||
|  |                       })); | ||||||
|  |                     }} | ||||||
|  |                     onSave={(value) => saveConfig('aimlApiKey', value)} | ||||||
|  |                   /> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <div className="flex flex-col space-y-1"> | ||||||
|  |                   <p className="text-black/70 dark:text-white/70 text-sm"> | ||||||
|  |                     LM Studio API URL | ||||||
|  |                   </p> | ||||||
|  |                   <Input | ||||||
|  |                     type="text" | ||||||
|  |                     placeholder="LM Studio API URL" | ||||||
|  |                     value={config.lmStudioApiUrl} | ||||||
|  |                     isSaving={savingStates['lmStudioApiUrl']} | ||||||
|  |                     onChange={(e) => { | ||||||
|  |                       setConfig((prev) => ({ | ||||||
|  |                         ...prev!, | ||||||
|  |                         lmStudioApiUrl: e.target.value, | ||||||
|  |                       })); | ||||||
|  |                     }} | ||||||
|  |                     onSave={(value) => saveConfig('lmStudioApiUrl', value)} | ||||||
|  |                   /> | ||||||
|  |                 </div> | ||||||
|               </div> |               </div> | ||||||
|             </SettingsSection> |             </SettingsSection> | ||||||
|           </div> |           </div> | ||||||
| @@ -48,11 +48,17 @@ const Chat = ({ | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  |     const scroll = () => { | ||||||
|       messageEnd.current?.scrollIntoView({ behavior: 'smooth' }); |       messageEnd.current?.scrollIntoView({ behavior: 'smooth' }); | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     if (messages.length === 1) { |     if (messages.length === 1) { | ||||||
|       document.title = `${messages[0].content.substring(0, 30)} - Perplexica`; |       document.title = `${messages[0].content.substring(0, 30)} - Perplexica`; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if (messages[messages.length - 1]?.role == 'user') { | ||||||
|  |       scroll(); | ||||||
|  |     } | ||||||
|   }, [messages]); |   }, [messages]); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
| @@ -29,36 +29,27 @@ export interface File { | |||||||
|   fileId: string; |   fileId: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const useSocket = ( | interface ChatModelProvider { | ||||||
|   url: string, |   name: string; | ||||||
|   setIsWSReady: (ready: boolean) => void, |   provider: string; | ||||||
|   setError: (error: boolean) => void, | } | ||||||
|  | 
 | ||||||
|  | interface EmbeddingModelProvider { | ||||||
|  |   name: string; | ||||||
|  |   provider: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const checkConfig = async ( | ||||||
|  |   setChatModelProvider: (provider: ChatModelProvider) => void, | ||||||
|  |   setEmbeddingModelProvider: (provider: EmbeddingModelProvider) => void, | ||||||
|  |   setIsConfigReady: (ready: boolean) => void, | ||||||
|  |   setHasError: (hasError: boolean) => void, | ||||||
| ) => { | ) => { | ||||||
|   const wsRef = useRef<WebSocket | null>(null); |  | ||||||
|   const reconnectTimeoutRef = useRef<NodeJS.Timeout>(); |  | ||||||
|   const retryCountRef = useRef(0); |  | ||||||
|   const isCleaningUpRef = useRef(false); |  | ||||||
|   const MAX_RETRIES = 3; |  | ||||||
|   const INITIAL_BACKOFF = 1000; // 1 second
 |  | ||||||
|   const isConnectionErrorRef = useRef(false); |  | ||||||
| 
 |  | ||||||
|   const getBackoffDelay = (retryCount: number) => { |  | ||||||
|     return Math.min(INITIAL_BACKOFF * Math.pow(2, retryCount), 10000); // Cap at 10 seconds
 |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     const connectWs = async () => { |  | ||||||
|       if (wsRef.current?.readyState === WebSocket.OPEN) { |  | ||||||
|         wsRef.current.close(); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|   try { |   try { | ||||||
|     let chatModel = localStorage.getItem('chatModel'); |     let chatModel = localStorage.getItem('chatModel'); | ||||||
|     let chatModelProvider = localStorage.getItem('chatModelProvider'); |     let chatModelProvider = localStorage.getItem('chatModelProvider'); | ||||||
|     let embeddingModel = localStorage.getItem('embeddingModel'); |     let embeddingModel = localStorage.getItem('embeddingModel'); | ||||||
|         let embeddingModelProvider = localStorage.getItem( |     let embeddingModelProvider = localStorage.getItem('embeddingModelProvider'); | ||||||
|           'embeddingModelProvider', |  | ||||||
|         ); |  | ||||||
| 
 | 
 | ||||||
|     const autoImageSearch = localStorage.getItem('autoImageSearch'); |     const autoImageSearch = localStorage.getItem('autoImageSearch'); | ||||||
|     const autoVideoSearch = localStorage.getItem('autoVideoSearch'); |     const autoVideoSearch = localStorage.getItem('autoVideoSearch'); | ||||||
| @@ -71,14 +62,11 @@ const useSocket = ( | |||||||
|       localStorage.setItem('autoVideoSearch', 'false'); |       localStorage.setItem('autoVideoSearch', 'false'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         const providers = await fetch( |     const providers = await fetch(`/api/models`, { | ||||||
|           `${process.env.NEXT_PUBLIC_API_URL}/models`, |  | ||||||
|           { |  | ||||||
|       headers: { |       headers: { | ||||||
|         'Content-Type': 'application/json', |         'Content-Type': 'application/json', | ||||||
|       }, |       }, | ||||||
|           }, |     }).then(async (res) => { | ||||||
|         ).then(async (res) => { |  | ||||||
|       if (!res.ok) |       if (!res.ok) | ||||||
|         throw new Error( |         throw new Error( | ||||||
|           `Failed to fetch models: ${res.status} ${res.statusText}`, |           `Failed to fetch models: ${res.status} ${res.statusText}`, | ||||||
| @@ -94,17 +82,29 @@ const useSocket = ( | |||||||
|     ) { |     ) { | ||||||
|       if (!chatModel || !chatModelProvider) { |       if (!chatModel || !chatModelProvider) { | ||||||
|         const chatModelProviders = providers.chatModelProviders; |         const chatModelProviders = providers.chatModelProviders; | ||||||
|  |         const chatModelProvidersKeys = Object.keys(chatModelProviders); | ||||||
| 
 | 
 | ||||||
|  |         if (!chatModelProviders || chatModelProvidersKeys.length === 0) { | ||||||
|  |           return toast.error('No chat models available'); | ||||||
|  |         } else { | ||||||
|           chatModelProvider = |           chatModelProvider = | ||||||
|               chatModelProvider || Object.keys(chatModelProviders)[0]; |             chatModelProvidersKeys.find( | ||||||
| 
 |               (provider) => | ||||||
|             chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]; |                 Object.keys(chatModelProviders[provider]).length > 0, | ||||||
|  |             ) || chatModelProvidersKeys[0]; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if ( |         if ( | ||||||
|               !chatModelProviders || |           chatModelProvider === 'custom_openai' && | ||||||
|               Object.keys(chatModelProviders).length === 0 |           Object.keys(chatModelProviders[chatModelProvider]).length === 0 | ||||||
|             ) |         ) { | ||||||
|               return toast.error('No chat models available'); |           toast.error( | ||||||
|  |             "Looks like you haven't configured any chat model providers. Please configure them from the settings page or the config file.", | ||||||
|  |           ); | ||||||
|  |           return setHasError(true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!embeddingModel || !embeddingModelProvider) { |       if (!embeddingModel || !embeddingModelProvider) { | ||||||
| @@ -125,17 +125,15 @@ const useSocket = ( | |||||||
|       localStorage.setItem('chatModel', chatModel!); |       localStorage.setItem('chatModel', chatModel!); | ||||||
|       localStorage.setItem('chatModelProvider', chatModelProvider); |       localStorage.setItem('chatModelProvider', chatModelProvider); | ||||||
|       localStorage.setItem('embeddingModel', embeddingModel!); |       localStorage.setItem('embeddingModel', embeddingModel!); | ||||||
|           localStorage.setItem( |       localStorage.setItem('embeddingModelProvider', embeddingModelProvider); | ||||||
|             'embeddingModelProvider', |  | ||||||
|             embeddingModelProvider, |  | ||||||
|           ); |  | ||||||
|     } else { |     } else { | ||||||
|       const chatModelProviders = providers.chatModelProviders; |       const chatModelProviders = providers.chatModelProviders; | ||||||
|       const embeddingModelProviders = providers.embeddingModelProviders; |       const embeddingModelProviders = providers.embeddingModelProviders; | ||||||
| 
 | 
 | ||||||
|       if ( |       if ( | ||||||
|         Object.keys(chatModelProviders).length > 0 && |         Object.keys(chatModelProviders).length > 0 && | ||||||
|             !chatModelProviders[chatModelProvider] |         (!chatModelProviders[chatModelProvider] || | ||||||
|  |           Object.keys(chatModelProviders[chatModelProvider]).length === 0) | ||||||
|       ) { |       ) { | ||||||
|         const chatModelProvidersKeys = Object.keys(chatModelProviders); |         const chatModelProvidersKeys = Object.keys(chatModelProviders); | ||||||
|         chatModelProvider = |         chatModelProvider = | ||||||
| @@ -150,6 +148,16 @@ const useSocket = ( | |||||||
|         chatModelProvider && |         chatModelProvider && | ||||||
|         !chatModelProviders[chatModelProvider][chatModel] |         !chatModelProviders[chatModelProvider][chatModel] | ||||||
|       ) { |       ) { | ||||||
|  |         if ( | ||||||
|  |           chatModelProvider === 'custom_openai' && | ||||||
|  |           Object.keys(chatModelProviders[chatModelProvider]).length === 0 | ||||||
|  |         ) { | ||||||
|  |           toast.error( | ||||||
|  |             "Looks like you haven't configured any chat model providers. Please configure them from the settings page or the config file.", | ||||||
|  |           ); | ||||||
|  |           return setHasError(true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         chatModel = Object.keys( |         chatModel = Object.keys( | ||||||
|           chatModelProviders[ |           chatModelProviders[ | ||||||
|             Object.keys(chatModelProviders[chatModelProvider]).length > 0 |             Object.keys(chatModelProviders[chatModelProvider]).length > 0 | ||||||
| @@ -157,6 +165,7 @@ const useSocket = ( | |||||||
|               : Object.keys(chatModelProviders)[0] |               : Object.keys(chatModelProviders)[0] | ||||||
|           ], |           ], | ||||||
|         )[0]; |         )[0]; | ||||||
|  | 
 | ||||||
|         localStorage.setItem('chatModel', chatModel); |         localStorage.setItem('chatModel', chatModel); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @@ -165,10 +174,7 @@ const useSocket = ( | |||||||
|         !embeddingModelProviders[embeddingModelProvider] |         !embeddingModelProviders[embeddingModelProvider] | ||||||
|       ) { |       ) { | ||||||
|         embeddingModelProvider = Object.keys(embeddingModelProviders)[0]; |         embeddingModelProvider = Object.keys(embeddingModelProviders)[0]; | ||||||
|             localStorage.setItem( |         localStorage.setItem('embeddingModelProvider', embeddingModelProvider); | ||||||
|               'embeddingModelProvider', |  | ||||||
|               embeddingModelProvider, |  | ||||||
|             ); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if ( |       if ( | ||||||
| @@ -182,127 +188,22 @@ const useSocket = ( | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         const wsURL = new URL(url); |     setChatModelProvider({ | ||||||
|         const searchParams = new URLSearchParams({}); |       name: chatModel!, | ||||||
| 
 |       provider: chatModelProvider, | ||||||
|         searchParams.append('chatModel', chatModel!); |  | ||||||
|         searchParams.append('chatModelProvider', chatModelProvider); |  | ||||||
| 
 |  | ||||||
|         if (chatModelProvider === 'custom_openai') { |  | ||||||
|           searchParams.append( |  | ||||||
|             'openAIApiKey', |  | ||||||
|             localStorage.getItem('openAIApiKey')!, |  | ||||||
|           ); |  | ||||||
|           searchParams.append( |  | ||||||
|             'openAIBaseURL', |  | ||||||
|             localStorage.getItem('openAIBaseURL')!, |  | ||||||
|           ); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         searchParams.append('embeddingModel', embeddingModel!); |  | ||||||
|         searchParams.append('embeddingModelProvider', embeddingModelProvider); |  | ||||||
| 
 |  | ||||||
|         wsURL.search = searchParams.toString(); |  | ||||||
| 
 |  | ||||||
|         const ws = new WebSocket(wsURL.toString()); |  | ||||||
|         wsRef.current = ws; |  | ||||||
| 
 |  | ||||||
|         const timeoutId = setTimeout(() => { |  | ||||||
|           if (ws.readyState !== 1) { |  | ||||||
|             toast.error( |  | ||||||
|               'Failed to connect to the server. Please try again later.', |  | ||||||
|             ); |  | ||||||
|           } |  | ||||||
|         }, 10000); |  | ||||||
| 
 |  | ||||||
|         ws.addEventListener('message', (e) => { |  | ||||||
|           const data = JSON.parse(e.data); |  | ||||||
|           if (data.type === 'signal' && data.data === 'open') { |  | ||||||
|             const interval = setInterval(() => { |  | ||||||
|               if (ws.readyState === 1) { |  | ||||||
|                 setIsWSReady(true); |  | ||||||
|                 setError(false); |  | ||||||
|                 if (retryCountRef.current > 0) { |  | ||||||
|                   toast.success('Connection restored.'); |  | ||||||
|                 } |  | ||||||
|                 retryCountRef.current = 0; |  | ||||||
|                 clearInterval(interval); |  | ||||||
|               } |  | ||||||
|             }, 5); |  | ||||||
|             clearTimeout(timeoutId); |  | ||||||
|             console.debug(new Date(), 'ws:connected'); |  | ||||||
|           } |  | ||||||
|           if (data.type === 'error') { |  | ||||||
|             isConnectionErrorRef.current = true; |  | ||||||
|             setError(true); |  | ||||||
|             toast.error(data.data); |  | ||||||
|           } |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|         ws.onerror = () => { |     setEmbeddingModelProvider({ | ||||||
|           clearTimeout(timeoutId); |       name: embeddingModel!, | ||||||
|           setIsWSReady(false); |       provider: embeddingModelProvider, | ||||||
|           toast.error('WebSocket connection error.'); |     }); | ||||||
|         }; |  | ||||||
| 
 | 
 | ||||||
|         ws.onclose = () => { |     setIsConfigReady(true); | ||||||
|           clearTimeout(timeoutId); |   } catch (err) { | ||||||
|           setIsWSReady(false); |     console.error('An error occurred while checking the configuration:', err); | ||||||
|           console.debug(new Date(), 'ws:disconnected'); |     setIsConfigReady(false); | ||||||
|           if (!isCleaningUpRef.current && !isConnectionErrorRef.current) { |     setHasError(true); | ||||||
|             toast.error('Connection lost. Attempting to reconnect...'); |  | ||||||
|             attemptReconnect(); |  | ||||||
|   } |   } | ||||||
|         }; |  | ||||||
|       } catch (error) { |  | ||||||
|         console.debug(new Date(), 'ws:error', error); |  | ||||||
|         setIsWSReady(false); |  | ||||||
|         attemptReconnect(); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const attemptReconnect = () => { |  | ||||||
|       retryCountRef.current += 1; |  | ||||||
| 
 |  | ||||||
|       if (retryCountRef.current > MAX_RETRIES) { |  | ||||||
|         console.debug(new Date(), 'ws:max_retries'); |  | ||||||
|         setError(true); |  | ||||||
|         toast.error( |  | ||||||
|           'Unable to connect to server after multiple attempts. Please refresh the page to try again.', |  | ||||||
|         ); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const backoffDelay = getBackoffDelay(retryCountRef.current); |  | ||||||
|       console.debug( |  | ||||||
|         new Date(), |  | ||||||
|         `ws:retry attempt=${retryCountRef.current}/${MAX_RETRIES} delay=${backoffDelay}ms`, |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       if (reconnectTimeoutRef.current) { |  | ||||||
|         clearTimeout(reconnectTimeoutRef.current); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       reconnectTimeoutRef.current = setTimeout(() => { |  | ||||||
|         connectWs(); |  | ||||||
|       }, backoffDelay); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     connectWs(); |  | ||||||
| 
 |  | ||||||
|     return () => { |  | ||||||
|       if (reconnectTimeoutRef.current) { |  | ||||||
|         clearTimeout(reconnectTimeoutRef.current); |  | ||||||
|       } |  | ||||||
|       if (wsRef.current?.readyState === WebSocket.OPEN) { |  | ||||||
|         wsRef.current.close(); |  | ||||||
|         isCleaningUpRef.current = true; |  | ||||||
|         console.debug(new Date(), 'ws:cleanup'); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|   }, [url, setIsWSReady, setError]); |  | ||||||
| 
 |  | ||||||
|   return wsRef.current; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const loadMessages = async ( | const loadMessages = async ( | ||||||
| @@ -315,15 +216,12 @@ const loadMessages = async ( | |||||||
|   setFiles: (files: File[]) => void, |   setFiles: (files: File[]) => void, | ||||||
|   setFileIds: (fileIds: string[]) => void, |   setFileIds: (fileIds: string[]) => void, | ||||||
| ) => { | ) => { | ||||||
|   const res = await fetch( |   const res = await fetch(`/api/chats/${chatId}`, { | ||||||
|     `${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, |  | ||||||
|     { |  | ||||||
|     method: 'GET', |     method: 'GET', | ||||||
|     headers: { |     headers: { | ||||||
|       'Content-Type': 'application/json', |       'Content-Type': 'application/json', | ||||||
|     }, |     }, | ||||||
|     }, |   }); | ||||||
|   ); |  | ||||||
| 
 | 
 | ||||||
|   if (res.status === 404) { |   if (res.status === 404) { | ||||||
|     setNotFound(true); |     setNotFound(true); | ||||||
| @@ -373,15 +271,32 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|   const [chatId, setChatId] = useState<string | undefined>(id); |   const [chatId, setChatId] = useState<string | undefined>(id); | ||||||
|   const [newChatCreated, setNewChatCreated] = useState(false); |   const [newChatCreated, setNewChatCreated] = useState(false); | ||||||
| 
 | 
 | ||||||
|  |   const [chatModelProvider, setChatModelProvider] = useState<ChatModelProvider>( | ||||||
|  |     { | ||||||
|  |       name: '', | ||||||
|  |       provider: '', | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const [embeddingModelProvider, setEmbeddingModelProvider] = | ||||||
|  |     useState<EmbeddingModelProvider>({ | ||||||
|  |       name: '', | ||||||
|  |       provider: '', | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |   const [isConfigReady, setIsConfigReady] = useState(false); | ||||||
|   const [hasError, setHasError] = useState(false); |   const [hasError, setHasError] = useState(false); | ||||||
|   const [isReady, setIsReady] = useState(false); |   const [isReady, setIsReady] = useState(false); | ||||||
| 
 | 
 | ||||||
|   const [isWSReady, setIsWSReady] = useState(false); |   useEffect(() => { | ||||||
|   const ws = useSocket( |     checkConfig( | ||||||
|     process.env.NEXT_PUBLIC_WS_URL!, |       setChatModelProvider, | ||||||
|     setIsWSReady, |       setEmbeddingModelProvider, | ||||||
|  |       setIsConfigReady, | ||||||
|       setHasError, |       setHasError, | ||||||
|     ); |     ); | ||||||
|  |     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||||
|  |   }, []); | ||||||
| 
 | 
 | ||||||
|   const [loading, setLoading] = useState(false); |   const [loading, setLoading] = useState(false); | ||||||
|   const [messageAppeared, setMessageAppeared] = useState(false); |   const [messageAppeared, setMessageAppeared] = useState(false); | ||||||
| @@ -399,8 +314,6 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
| 
 | 
 | ||||||
|   const [notFound, setNotFound] = useState(false); |   const [notFound, setNotFound] = useState(false); | ||||||
| 
 | 
 | ||||||
|   const [isSettingsOpen, setIsSettingsOpen] = useState(false); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if ( |     if ( | ||||||
|       chatId && |       chatId && | ||||||
| @@ -426,16 +339,6 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 |     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||||
|   }, []); |   }, []); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |  | ||||||
|     return () => { |  | ||||||
|       if (ws?.readyState === 1) { |  | ||||||
|         ws.close(); |  | ||||||
|         console.debug(new Date(), 'ws:cleanup'); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 |  | ||||||
|   }, []); |  | ||||||
| 
 |  | ||||||
|   const messagesRef = useRef<Message[]>([]); |   const messagesRef = useRef<Message[]>([]); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
| @@ -443,18 +346,18 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|   }, [messages]); |   }, [messages]); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (isMessagesLoaded && isWSReady) { |     if (isMessagesLoaded && isConfigReady) { | ||||||
|       setIsReady(true); |       setIsReady(true); | ||||||
|       console.debug(new Date(), 'app:ready'); |       console.debug(new Date(), 'app:ready'); | ||||||
|     } else { |     } else { | ||||||
|       setIsReady(false); |       setIsReady(false); | ||||||
|     } |     } | ||||||
|   }, [isMessagesLoaded, isWSReady]); |   }, [isMessagesLoaded, isConfigReady]); | ||||||
| 
 | 
 | ||||||
|   const sendMessage = async (message: string, messageId?: string) => { |   const sendMessage = async (message: string, messageId?: string) => { | ||||||
|     if (loading) return; |     if (loading) return; | ||||||
|     if (!ws || ws.readyState !== WebSocket.OPEN) { |     if (!isConfigReady) { | ||||||
|       toast.error('Cannot send message while disconnected'); |       toast.error('Cannot send message before the configuration is ready'); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -467,21 +370,6 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
| 
 | 
 | ||||||
|     messageId = messageId ?? crypto.randomBytes(7).toString('hex'); |     messageId = messageId ?? crypto.randomBytes(7).toString('hex'); | ||||||
| 
 | 
 | ||||||
|     ws.send( |  | ||||||
|       JSON.stringify({ |  | ||||||
|         type: 'message', |  | ||||||
|         message: { |  | ||||||
|           messageId: messageId, |  | ||||||
|           chatId: chatId!, |  | ||||||
|           content: message, |  | ||||||
|         }, |  | ||||||
|         files: fileIds, |  | ||||||
|         focusMode: focusMode, |  | ||||||
|         optimizationMode: optimizationMode, |  | ||||||
|         history: [...chatHistory, ['human', message]], |  | ||||||
|       }), |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     setMessages((prevMessages) => [ |     setMessages((prevMessages) => [ | ||||||
|       ...prevMessages, |       ...prevMessages, | ||||||
|       { |       { | ||||||
| @@ -493,9 +381,7 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|       }, |       }, | ||||||
|     ]); |     ]); | ||||||
| 
 | 
 | ||||||
|     const messageHandler = async (e: MessageEvent) => { |     const messageHandler = async (data: any) => { | ||||||
|       const data = JSON.parse(e.data); |  | ||||||
| 
 |  | ||||||
|       if (data.type === 'error') { |       if (data.type === 'error') { | ||||||
|         toast.error(data.data); |         toast.error(data.data); | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
| @@ -558,11 +444,25 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|           ['assistant', recievedMessage], |           ['assistant', recievedMessage], | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         ws?.removeEventListener('message', messageHandler); |  | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
| 
 | 
 | ||||||
|         const lastMsg = messagesRef.current[messagesRef.current.length - 1]; |         const lastMsg = messagesRef.current[messagesRef.current.length - 1]; | ||||||
| 
 | 
 | ||||||
|  |         const autoImageSearch = localStorage.getItem('autoImageSearch'); | ||||||
|  |         const autoVideoSearch = localStorage.getItem('autoVideoSearch'); | ||||||
|  | 
 | ||||||
|  |         if (autoImageSearch === 'true') { | ||||||
|  |           document | ||||||
|  |             .getElementById(`search-images-${lastMsg.messageId}`) | ||||||
|  |             ?.click(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (autoVideoSearch === 'true') { | ||||||
|  |           document | ||||||
|  |             .getElementById(`search-videos-${lastMsg.messageId}`) | ||||||
|  |             ?.click(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if ( |         if ( | ||||||
|           lastMsg.role === 'assistant' && |           lastMsg.role === 'assistant' && | ||||||
|           lastMsg.sources && |           lastMsg.sources && | ||||||
| @@ -579,21 +479,63 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|             }), |             }), | ||||||
|           ); |           ); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         const autoImageSearch = localStorage.getItem('autoImageSearch'); |  | ||||||
|         const autoVideoSearch = localStorage.getItem('autoVideoSearch'); |  | ||||||
| 
 |  | ||||||
|         if (autoImageSearch === 'true') { |  | ||||||
|           document.getElementById('search-images')?.click(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (autoVideoSearch === 'true') { |  | ||||||
|           document.getElementById('search-videos')?.click(); |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     ws?.addEventListener('message', messageHandler); |     const res = await fetch('/api/chat', { | ||||||
|  |       method: 'POST', | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'application/json', | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify({ | ||||||
|  |         content: message, | ||||||
|  |         message: { | ||||||
|  |           messageId: messageId, | ||||||
|  |           chatId: chatId!, | ||||||
|  |           content: message, | ||||||
|  |         }, | ||||||
|  |         chatId: chatId!, | ||||||
|  |         files: fileIds, | ||||||
|  |         focusMode: focusMode, | ||||||
|  |         optimizationMode: optimizationMode, | ||||||
|  |         history: chatHistory, | ||||||
|  |         chatModel: { | ||||||
|  |           name: chatModelProvider.name, | ||||||
|  |           provider: chatModelProvider.provider, | ||||||
|  |         }, | ||||||
|  |         embeddingModel: { | ||||||
|  |           name: embeddingModelProvider.name, | ||||||
|  |           provider: embeddingModelProvider.provider, | ||||||
|  |         }, | ||||||
|  |         systemInstructions: localStorage.getItem('systemInstructions'), | ||||||
|  |       }), | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (!res.body) throw new Error('No response body'); | ||||||
|  | 
 | ||||||
|  |     const reader = res.body?.getReader(); | ||||||
|  |     const decoder = new TextDecoder('utf-8'); | ||||||
|  | 
 | ||||||
|  |     let partialChunk = ''; | ||||||
|  | 
 | ||||||
|  |     while (true) { | ||||||
|  |       const { value, done } = await reader.read(); | ||||||
|  |       if (done) break; | ||||||
|  | 
 | ||||||
|  |       partialChunk += decoder.decode(value, { stream: true }); | ||||||
|  | 
 | ||||||
|  |       try { | ||||||
|  |         const messages = partialChunk.split('\n'); | ||||||
|  |         for (const msg of messages) { | ||||||
|  |           if (!msg.trim()) continue; | ||||||
|  |           const json = JSON.parse(msg); | ||||||
|  |           messageHandler(json); | ||||||
|  |         } | ||||||
|  |         partialChunk = ''; | ||||||
|  |       } catch (error) { | ||||||
|  |         console.warn('Incomplete JSON, waiting for next chunk...'); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const rewrite = (messageId: string) => { |   const rewrite = (messageId: string) => { | ||||||
| @@ -614,11 +556,11 @@ const ChatWindow = ({ id }: { id?: string }) => { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (isReady && initialMessage && ws?.readyState === 1) { |     if (isReady && initialMessage && isConfigReady) { | ||||||
|       sendMessage(initialMessage); |       sendMessage(initialMessage); | ||||||
|     } |     } | ||||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 |     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||||
|   }, [ws?.readyState, isReady, initialMessage, isWSReady]); |   }, [isConfigReady, isReady, initialMessage]); | ||||||
| 
 | 
 | ||||||
|   if (hasError) { |   if (hasError) { | ||||||
|     return ( |     return ( | ||||||
| @@ -29,15 +29,12 @@ const DeleteChat = ({ | |||||||
|   const handleDelete = async () => { |   const handleDelete = async () => { | ||||||
|     setLoading(true); |     setLoading(true); | ||||||
|     try { |     try { | ||||||
|       const res = await fetch( |       const res = await fetch(`/api/chats/${chatId}`, { | ||||||
|         `${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, |  | ||||||
|         { |  | ||||||
|         method: 'DELETE', |         method: 'DELETE', | ||||||
|         headers: { |         headers: { | ||||||
|           'Content-Type': 'application/json', |           'Content-Type': 'application/json', | ||||||
|         }, |         }, | ||||||
|         }, |       }); | ||||||
|       ); |  | ||||||
| 
 | 
 | ||||||
|       if (res.status != 200) { |       if (res.status != 200) { | ||||||
|         throw new Error('Failed to delete chat'); |         throw new Error('Failed to delete chat'); | ||||||
							
								
								
									
										66
									
								
								src/components/EmptyChat.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,66 @@ | |||||||
|  | import { Settings } from 'lucide-react'; | ||||||
|  | import EmptyChatMessageInput from './EmptyChatMessageInput'; | ||||||
|  | import { File } from './ChatWindow'; | ||||||
|  | import Link from 'next/link'; | ||||||
|  | import WeatherWidget from './WeatherWidget'; | ||||||
|  | import NewsArticleWidget from './NewsArticleWidget'; | ||||||
|  |  | ||||||
|  | const EmptyChat = ({ | ||||||
|  |   sendMessage, | ||||||
|  |   focusMode, | ||||||
|  |   setFocusMode, | ||||||
|  |   optimizationMode, | ||||||
|  |   setOptimizationMode, | ||||||
|  |   fileIds, | ||||||
|  |   setFileIds, | ||||||
|  |   files, | ||||||
|  |   setFiles, | ||||||
|  | }: { | ||||||
|  |   sendMessage: (message: string) => void; | ||||||
|  |   focusMode: string; | ||||||
|  |   setFocusMode: (mode: string) => void; | ||||||
|  |   optimizationMode: string; | ||||||
|  |   setOptimizationMode: (mode: string) => void; | ||||||
|  |   fileIds: string[]; | ||||||
|  |   setFileIds: (fileIds: string[]) => void; | ||||||
|  |   files: File[]; | ||||||
|  |   setFiles: (files: File[]) => void; | ||||||
|  | }) => { | ||||||
|  |   return ( | ||||||
|  |     <div className="relative"> | ||||||
|  |       <div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5"> | ||||||
|  |         <Link href="/settings"> | ||||||
|  |           <Settings className="cursor-pointer lg:hidden" /> | ||||||
|  |         </Link> | ||||||
|  |       </div> | ||||||
|  |       <div className="flex flex-col items-center justify-center min-h-screen max-w-screen-sm mx-auto p-2 space-y-4"> | ||||||
|  |         <div className="flex flex-col items-center justify-center w-full space-y-8"> | ||||||
|  |           <h2 className="text-black/70 dark:text-white/70 text-3xl font-medium -mt-8"> | ||||||
|  |             Research begins here. | ||||||
|  |           </h2> | ||||||
|  |           <EmptyChatMessageInput | ||||||
|  |             sendMessage={sendMessage} | ||||||
|  |             focusMode={focusMode} | ||||||
|  |             setFocusMode={setFocusMode} | ||||||
|  |             optimizationMode={optimizationMode} | ||||||
|  |             setOptimizationMode={setOptimizationMode} | ||||||
|  |             fileIds={fileIds} | ||||||
|  |             setFileIds={setFileIds} | ||||||
|  |             files={files} | ||||||
|  |             setFiles={setFiles} | ||||||
|  |           /> | ||||||
|  |         </div> | ||||||
|  |         <div className="flex flex-col w-full gap-4 mt-2 sm:flex-row sm:justify-center"> | ||||||
|  |           <div className="flex-1 w-full"> | ||||||
|  |             <WeatherWidget /> | ||||||
|  |           </div> | ||||||
|  |           <div className="flex-1 w-full"> | ||||||
|  |             <NewsArticleWidget /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default EmptyChat; | ||||||
| @@ -12,13 +12,18 @@ import { | |||||||
|   Layers3, |   Layers3, | ||||||
|   Plus, |   Plus, | ||||||
| } from 'lucide-react'; | } from 'lucide-react'; | ||||||
| import Markdown from 'markdown-to-jsx'; | import Markdown, { MarkdownToJSX } from 'markdown-to-jsx'; | ||||||
| import Copy from './MessageActions/Copy'; | import Copy from './MessageActions/Copy'; | ||||||
| import Rewrite from './MessageActions/Rewrite'; | import Rewrite from './MessageActions/Rewrite'; | ||||||
| import MessageSources from './MessageSources'; | import MessageSources from './MessageSources'; | ||||||
| import SearchImages from './SearchImages'; | import SearchImages from './SearchImages'; | ||||||
| import SearchVideos from './SearchVideos'; | import SearchVideos from './SearchVideos'; | ||||||
| import { useSpeech } from 'react-text-to-speech'; | import { useSpeech } from 'react-text-to-speech'; | ||||||
|  | import ThinkBox from './ThinkBox'; | ||||||
|  | 
 | ||||||
|  | const ThinkTagProcessor = ({ children }: { children: React.ReactNode }) => { | ||||||
|  |   return <ThinkBox content={children as string} />; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const MessageBox = ({ | const MessageBox = ({ | ||||||
|   message, |   message, | ||||||
| @@ -43,32 +48,83 @@ const MessageBox = ({ | |||||||
|   const [speechMessage, setSpeechMessage] = useState(message.content); |   const [speechMessage, setSpeechMessage] = useState(message.content); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  |     const citationRegex = /\[([^\]]+)\]/g; | ||||||
|     const regex = /\[(\d+)\]/g; |     const regex = /\[(\d+)\]/g; | ||||||
|  |     let processedMessage = message.content; | ||||||
|  | 
 | ||||||
|  |     if (message.role === 'assistant' && message.content.includes('<think>')) { | ||||||
|  |       const openThinkTag = processedMessage.match(/<think>/g)?.length || 0; | ||||||
|  |       const closeThinkTag = processedMessage.match(/<\/think>/g)?.length || 0; | ||||||
|  | 
 | ||||||
|  |       if (openThinkTag > closeThinkTag) { | ||||||
|  |         processedMessage += '</think> <a> </a>'; // The extra <a> </a> is to prevent the the think component from looking bad
 | ||||||
|  |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if ( |     if ( | ||||||
|       message.role === 'assistant' && |       message.role === 'assistant' && | ||||||
|       message?.sources && |       message?.sources && | ||||||
|       message.sources.length > 0 |       message.sources.length > 0 | ||||||
|     ) { |     ) { | ||||||
|       return setParsedMessage( |       setParsedMessage( | ||||||
|         message.content.replace( |         processedMessage.replace( | ||||||
|           regex, |           citationRegex, | ||||||
|           (_, number) => |           (_, capturedContent: string) => { | ||||||
|             `<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${number}</a>`, |             const numbers = capturedContent | ||||||
|  |               .split(',') | ||||||
|  |               .map((numStr) => numStr.trim()); | ||||||
|  | 
 | ||||||
|  |             const linksHtml = numbers | ||||||
|  |               .map((numStr) => { | ||||||
|  |                 const number = parseInt(numStr); | ||||||
|  | 
 | ||||||
|  |                 if (isNaN(number) || number <= 0) { | ||||||
|  |                   return `[${numStr}]`; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 const source = message.sources?.[number - 1]; | ||||||
|  |                 const url = source?.metadata?.url; | ||||||
|  | 
 | ||||||
|  |                 if (url) { | ||||||
|  |                   return `<a href="${url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${numStr}</a>`; | ||||||
|  |                 } else { | ||||||
|  |                   return `[${numStr}]`; | ||||||
|  |                 } | ||||||
|  |               }) | ||||||
|  |               .join(''); | ||||||
|  | 
 | ||||||
|  |             return linksHtml; | ||||||
|  |           }, | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|  |       setSpeechMessage(message.content.replace(regex, '')); | ||||||
|  |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     setSpeechMessage(message.content.replace(regex, '')); |     setSpeechMessage(message.content.replace(regex, '')); | ||||||
|     setParsedMessage(message.content); |     setParsedMessage(processedMessage); | ||||||
|   }, [message.content, message.sources, message.role]); |   }, [message.content, message.sources, message.role]); | ||||||
| 
 | 
 | ||||||
|   const { speechStatus, start, stop } = useSpeech({ text: speechMessage }); |   const { speechStatus, start, stop } = useSpeech({ text: speechMessage }); | ||||||
| 
 | 
 | ||||||
|  |   const markdownOverrides: MarkdownToJSX.Options = { | ||||||
|  |     overrides: { | ||||||
|  |       think: { | ||||||
|  |         component: ThinkTagProcessor, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       {message.role === 'user' && ( |       {message.role === 'user' && ( | ||||||
|         <div className={cn('w-full', messageIndex === 0 ? 'pt-16' : 'pt-8', 'break-words')}> |         <div | ||||||
|  |           className={cn( | ||||||
|  |             'w-full', | ||||||
|  |             messageIndex === 0 ? 'pt-16' : 'pt-8', | ||||||
|  |             'break-words', | ||||||
|  |           )} | ||||||
|  |         > | ||||||
|           <h2 className="text-black dark:text-white font-medium text-3xl lg:w-9/12"> |           <h2 className="text-black dark:text-white font-medium text-3xl lg:w-9/12"> | ||||||
|             {message.content} |             {message.content} | ||||||
|           </h2> |           </h2> | ||||||
| @@ -105,11 +161,13 @@ const MessageBox = ({ | |||||||
|                   Answer |                   Answer | ||||||
|                 </h3> |                 </h3> | ||||||
|               </div> |               </div> | ||||||
|  | 
 | ||||||
|               <Markdown |               <Markdown | ||||||
|                 className={cn( |                 className={cn( | ||||||
|                   'prose prose-h1:mb-3 prose-h2:mb-2 prose-h2:mt-6 prose-h2:font-[800] prose-h3:mt-4 prose-h3:mb-1.5 prose-h3:font-[600] dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 font-[400]', |                   'prose prose-h1:mb-3 prose-h2:mb-2 prose-h2:mt-6 prose-h2:font-[800] prose-h3:mt-4 prose-h3:mb-1.5 prose-h3:font-[600] dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 font-[400]', | ||||||
|                   'max-w-none break-words text-black dark:text-white', |                   'max-w-none break-words text-black dark:text-white', | ||||||
|                 )} |                 )} | ||||||
|  |                 options={markdownOverrides} | ||||||
|               > |               > | ||||||
|                 {parsedMessage} |                 {parsedMessage} | ||||||
|               </Markdown> |               </Markdown> | ||||||
| @@ -187,10 +245,12 @@ const MessageBox = ({ | |||||||
|             <SearchImages |             <SearchImages | ||||||
|               query={history[messageIndex - 1].content} |               query={history[messageIndex - 1].content} | ||||||
|               chatHistory={history.slice(0, messageIndex - 1)} |               chatHistory={history.slice(0, messageIndex - 1)} | ||||||
|  |               messageId={message.messageId} | ||||||
|             /> |             /> | ||||||
|             <SearchVideos |             <SearchVideos | ||||||
|               chatHistory={history.slice(0, messageIndex - 1)} |               chatHistory={history.slice(0, messageIndex - 1)} | ||||||
|               query={history[messageIndex - 1].content} |               query={history[messageIndex - 1].content} | ||||||
|  |               messageId={message.messageId} | ||||||
|             /> |             /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| @@ -41,7 +41,7 @@ const Attach = ({ | |||||||
|     data.append('embedding_model_provider', embeddingModelProvider!); |     data.append('embedding_model_provider', embeddingModelProvider!); | ||||||
|     data.append('embedding_model', embeddingModel!); |     data.append('embedding_model', embeddingModel!); | ||||||
| 
 | 
 | ||||||
|     const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/uploads`, { |     const res = await fetch(`/api/uploads`, { | ||||||
|       method: 'POST', |       method: 'POST', | ||||||
|       body: data, |       body: data, | ||||||
|     }); |     }); | ||||||
| @@ -39,7 +39,7 @@ const AttachSmall = ({ | |||||||
|     data.append('embedding_model_provider', embeddingModelProvider!); |     data.append('embedding_model_provider', embeddingModelProvider!); | ||||||
|     data.append('embedding_model', embeddingModel!); |     data.append('embedding_model', embeddingModel!); | ||||||
| 
 | 
 | ||||||
|     const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/uploads`, { |     const res = await fetch(`/api/uploads`, { | ||||||
|       method: 'POST', |       method: 'POST', | ||||||
|       body: data, |       body: data, | ||||||
|     }); |     }); | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||||
| import { Switch } from '@headlessui/react'; | import { Switch } from '@headlessui/react'; | ||||||
| import { useEffect } from 'react'; |  | ||||||
| 
 | 
 | ||||||
| const CopilotToggle = ({ | const CopilotToggle = ({ | ||||||
|   copilotEnabled, |   copilotEnabled, | ||||||
| @@ -9,33 +8,11 @@ const CopilotToggle = ({ | |||||||
|   copilotEnabled: boolean; |   copilotEnabled: boolean; | ||||||
|   setCopilotEnabled: (enabled: boolean) => void; |   setCopilotEnabled: (enabled: boolean) => void; | ||||||
| }) => { | }) => { | ||||||
|   const fetchAndSetCopilotEnabled = async () => { |  | ||||||
|     const res = await fetch( |  | ||||||
|       `${process.env.NEXT_PUBLIC_API_URL}/config/preferences`, |  | ||||||
|       { |  | ||||||
|         method: 'GET', |  | ||||||
|         headers: { |  | ||||||
|           'Content-Type': 'application/json', |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     const preferences = await res.json(); |  | ||||||
| 
 |  | ||||||
|     setCopilotEnabled(preferences.isCopilotEnabled); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     fetchAndSetCopilotEnabled(); |  | ||||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 |  | ||||||
|   }, []); |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="group flex flex-row items-center space-x-1 active:scale-95 duration-200 transition cursor-pointer"> |     <div className="group flex flex-row items-center space-x-1 active:scale-95 duration-200 transition cursor-pointer"> | ||||||
|       <Switch |       <Switch | ||||||
|         checked={copilotEnabled} |         checked={copilotEnabled} | ||||||
|         onChange={setCopilotEnabled} |         onChange={setCopilotEnabled} | ||||||
|         disabled={true} |  | ||||||
|         className="bg-light-secondary dark:bg-dark-secondary border border-light-200/70 dark:border-dark-200 relative inline-flex h-5 w-10 sm:h-6 sm:w-11 items-center rounded-full" |         className="bg-light-secondary dark:bg-dark-secondary border border-light-200/70 dark:border-dark-200 relative inline-flex h-5 w-10 sm:h-6 sm:w-11 items-center rounded-full" | ||||||
|       > |       > | ||||||
|         <span className="sr-only">Copilot</span> |         <span className="sr-only">Copilot</span> | ||||||
| @@ -45,25 +45,13 @@ const focusModes = [ | |||||||
|     key: 'youtubeSearch', |     key: 'youtubeSearch', | ||||||
|     title: 'Youtube', |     title: 'Youtube', | ||||||
|     description: 'Search and watch videos', |     description: 'Search and watch videos', | ||||||
|     icon: ( |     icon: <SiYoutube className="h-5 w-auto mr-0.5" />, | ||||||
|       <SiYoutube |  | ||||||
|         className="h-5 w-auto mr-0.5" |  | ||||||
|         onPointerEnterCapture={undefined} |  | ||||||
|         onPointerLeaveCapture={undefined} |  | ||||||
|       /> |  | ||||||
|     ), |  | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     key: 'redditSearch', |     key: 'redditSearch', | ||||||
|     title: 'Reddit', |     title: 'Reddit', | ||||||
|     description: 'Search for discussions and opinions', |     description: 'Search for discussions and opinions', | ||||||
|     icon: ( |     icon: <SiReddit className="h-5 w-auto mr-0.5" />, | ||||||
|       <SiReddit |  | ||||||
|         className="h-5 w-auto mr-0.5" |  | ||||||
|         onPointerEnterCapture={undefined} |  | ||||||
|         onPointerLeaveCapture={undefined} |  | ||||||
|       /> |  | ||||||
|     ), |  | ||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @@ -69,11 +69,15 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { | |||||||
|           <div className="flex flex-row items-center space-x-1"> |           <div className="flex flex-row items-center space-x-1"> | ||||||
|             {sources.slice(3, 6).map((source, i) => { |             {sources.slice(3, 6).map((source, i) => { | ||||||
|               return source.metadata.url === 'File' ? ( |               return source.metadata.url === 'File' ? ( | ||||||
|                 <div className="bg-dark-200 hover:bg-dark-100 transition duration-200 flex items-center justify-center w-6 h-6 rounded-full"> |                 <div | ||||||
|  |                   key={i} | ||||||
|  |                   className="bg-dark-200 hover:bg-dark-100 transition duration-200 flex items-center justify-center w-6 h-6 rounded-full" | ||||||
|  |                 > | ||||||
|                   <File size={12} className="text-white/70" /> |                   <File size={12} className="text-white/70" /> | ||||||
|                 </div> |                 </div> | ||||||
|               ) : ( |               ) : ( | ||||||
|                 <img |                 <img | ||||||
|  |                   key={i} | ||||||
|                   src={`https://s2.googleusercontent.com/s2/favicons?domain_url=${source.metadata.url}`} |                   src={`https://s2.googleusercontent.com/s2/favicons?domain_url=${source.metadata.url}`} | ||||||
|                   width={16} |                   width={16} | ||||||
|                   height={16} |                   height={16} | ||||||
							
								
								
									
										215
									
								
								src/components/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,215 @@ | |||||||
|  | import { Clock, Edit, Share, Trash, FileText, FileDown } from 'lucide-react'; | ||||||
|  | import { Message } from './ChatWindow'; | ||||||
|  | import { useEffect, useState, Fragment } from 'react'; | ||||||
|  | import { formatTimeDifference } from '@/lib/utils'; | ||||||
|  | import DeleteChat from './DeleteChat'; | ||||||
|  | import { | ||||||
|  |   Popover, | ||||||
|  |   PopoverButton, | ||||||
|  |   PopoverPanel, | ||||||
|  |   Transition, | ||||||
|  | } from '@headlessui/react'; | ||||||
|  | import jsPDF from 'jspdf'; | ||||||
|  |  | ||||||
|  | const downloadFile = (filename: string, content: string, type: string) => { | ||||||
|  |   const blob = new Blob([content], { type }); | ||||||
|  |   const url = URL.createObjectURL(blob); | ||||||
|  |   const a = document.createElement('a'); | ||||||
|  |   a.href = url; | ||||||
|  |   a.download = filename; | ||||||
|  |   document.body.appendChild(a); | ||||||
|  |   a.click(); | ||||||
|  |   setTimeout(() => { | ||||||
|  |     document.body.removeChild(a); | ||||||
|  |     URL.revokeObjectURL(url); | ||||||
|  |   }, 0); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const exportAsMarkdown = (messages: Message[], title: string) => { | ||||||
|  |   const date = new Date(messages[0]?.createdAt || Date.now()).toLocaleString(); | ||||||
|  |   let md = `# 💬 Chat Export: ${title}\n\n`; | ||||||
|  |   md += `*Exported on: ${date}*\n\n---\n`; | ||||||
|  |   messages.forEach((msg, idx) => { | ||||||
|  |     md += `\n---\n`; | ||||||
|  |     md += `**${msg.role === 'user' ? '🧑 User' : '🤖 Assistant'}**   | ||||||
|  | `; | ||||||
|  |     md += `*${new Date(msg.createdAt).toLocaleString()}*\n\n`; | ||||||
|  |     md += `> ${msg.content.replace(/\n/g, '\n> ')}\n`; | ||||||
|  |     if (msg.sources && msg.sources.length > 0) { | ||||||
|  |       md += `\n**Citations:**\n`; | ||||||
|  |       msg.sources.forEach((src: any, i: number) => { | ||||||
|  |         const url = src.metadata?.url || ''; | ||||||
|  |         md += `- [${i + 1}] [${url}](${url})\n`; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   md += '\n---\n'; | ||||||
|  |   downloadFile(`${title || 'chat'}.md`, md, 'text/markdown'); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const exportAsPDF = (messages: Message[], title: string) => { | ||||||
|  |   const doc = new jsPDF(); | ||||||
|  |   const date = new Date(messages[0]?.createdAt || Date.now()).toLocaleString(); | ||||||
|  |   let y = 15; | ||||||
|  |   const pageHeight = doc.internal.pageSize.height; | ||||||
|  |   doc.setFontSize(18); | ||||||
|  |   doc.text(`Chat Export: ${title}`, 10, y); | ||||||
|  |   y += 8; | ||||||
|  |   doc.setFontSize(11); | ||||||
|  |   doc.setTextColor(100); | ||||||
|  |   doc.text(`Exported on: ${date}`, 10, y); | ||||||
|  |   y += 8; | ||||||
|  |   doc.setDrawColor(200); | ||||||
|  |   doc.line(10, y, 200, y); | ||||||
|  |   y += 6; | ||||||
|  |   doc.setTextColor(30); | ||||||
|  |   messages.forEach((msg, idx) => { | ||||||
|  |     if (y > pageHeight - 30) { | ||||||
|  |       doc.addPage(); | ||||||
|  |       y = 15; | ||||||
|  |     } | ||||||
|  |     doc.setFont('helvetica', 'bold'); | ||||||
|  |     doc.text(`${msg.role === 'user' ? 'User' : 'Assistant'}`, 10, y); | ||||||
|  |     doc.setFont('helvetica', 'normal'); | ||||||
|  |     doc.setFontSize(10); | ||||||
|  |     doc.setTextColor(120); | ||||||
|  |     doc.text(`${new Date(msg.createdAt).toLocaleString()}`, 40, y); | ||||||
|  |     y += 6; | ||||||
|  |     doc.setTextColor(30); | ||||||
|  |     doc.setFontSize(12); | ||||||
|  |     const lines = doc.splitTextToSize(msg.content, 180); | ||||||
|  |     for (let i = 0; i < lines.length; i++) { | ||||||
|  |       if (y > pageHeight - 20) { | ||||||
|  |         doc.addPage(); | ||||||
|  |         y = 15; | ||||||
|  |       } | ||||||
|  |       doc.text(lines[i], 12, y); | ||||||
|  |       y += 6; | ||||||
|  |     } | ||||||
|  |     if (msg.sources && msg.sources.length > 0) { | ||||||
|  |       doc.setFontSize(11); | ||||||
|  |       doc.setTextColor(80); | ||||||
|  |       if (y > pageHeight - 20) { | ||||||
|  |         doc.addPage(); | ||||||
|  |         y = 15; | ||||||
|  |       } | ||||||
|  |       doc.text('Citations:', 12, y); | ||||||
|  |       y += 5; | ||||||
|  |       msg.sources.forEach((src: any, i: number) => { | ||||||
|  |         const url = src.metadata?.url || ''; | ||||||
|  |         if (y > pageHeight - 15) { | ||||||
|  |           doc.addPage(); | ||||||
|  |           y = 15; | ||||||
|  |         } | ||||||
|  |         doc.text(`- [${i + 1}] ${url}`, 15, y); | ||||||
|  |         y += 5; | ||||||
|  |       }); | ||||||
|  |       doc.setTextColor(30); | ||||||
|  |     } | ||||||
|  |     y += 6; | ||||||
|  |     doc.setDrawColor(230); | ||||||
|  |     if (y > pageHeight - 10) { | ||||||
|  |       doc.addPage(); | ||||||
|  |       y = 15; | ||||||
|  |     } | ||||||
|  |     doc.line(10, y, 200, y); | ||||||
|  |     y += 4; | ||||||
|  |   }); | ||||||
|  |   doc.save(`${title || 'chat'}.pdf`); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const Navbar = ({ | ||||||
|  |   chatId, | ||||||
|  |   messages, | ||||||
|  | }: { | ||||||
|  |   messages: Message[]; | ||||||
|  |   chatId: string; | ||||||
|  | }) => { | ||||||
|  |   const [title, setTitle] = useState<string>(''); | ||||||
|  |   const [timeAgo, setTimeAgo] = useState<string>(''); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (messages.length > 0) { | ||||||
|  |       const newTitle = | ||||||
|  |         messages[0].content.length > 20 | ||||||
|  |           ? `${messages[0].content.substring(0, 20).trim()}...` | ||||||
|  |           : messages[0].content; | ||||||
|  |       setTitle(newTitle); | ||||||
|  |       const newTimeAgo = formatTimeDifference( | ||||||
|  |         new Date(), | ||||||
|  |         messages[0].createdAt, | ||||||
|  |       ); | ||||||
|  |       setTimeAgo(newTimeAgo); | ||||||
|  |     } | ||||||
|  |   }, [messages]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     const intervalId = setInterval(() => { | ||||||
|  |       if (messages.length > 0) { | ||||||
|  |         const newTimeAgo = formatTimeDifference( | ||||||
|  |           new Date(), | ||||||
|  |           messages[0].createdAt, | ||||||
|  |         ); | ||||||
|  |         setTimeAgo(newTimeAgo); | ||||||
|  |       } | ||||||
|  |     }, 1000); | ||||||
|  |  | ||||||
|  |     return () => clearInterval(intervalId); | ||||||
|  |     // eslint-disable-next-line react-hooks/exhaustive-deps | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="fixed z-40 top-0 left-0 right-0 px-4 lg:pl-[104px] lg:pr-6 lg:px-8 flex flex-row items-center justify-between w-full py-4 text-sm text-black dark:text-white/70 border-b bg-light-primary dark:bg-dark-primary border-light-100 dark:border-dark-200"> | ||||||
|  |       <a | ||||||
|  |         href="/" | ||||||
|  |         className="active:scale-95 transition duration-100 cursor-pointer lg:hidden" | ||||||
|  |       > | ||||||
|  |         <Edit size={17} /> | ||||||
|  |       </a> | ||||||
|  |       <div className="hidden lg:flex flex-row items-center justify-center space-x-2"> | ||||||
|  |         <Clock size={17} /> | ||||||
|  |         <p className="text-xs">{timeAgo} ago</p> | ||||||
|  |       </div> | ||||||
|  |       <p className="hidden lg:flex">{title}</p> | ||||||
|  |  | ||||||
|  |       <div className="flex flex-row items-center space-x-4"> | ||||||
|  |         <Popover className="relative"> | ||||||
|  |           <PopoverButton className="active:scale-95 transition duration-100 cursor-pointer p-2 rounded-full hover:bg-light-secondary dark:hover:bg-dark-secondary"> | ||||||
|  |             <Share size={17} /> | ||||||
|  |           </PopoverButton> | ||||||
|  |           <Transition | ||||||
|  |             as={Fragment} | ||||||
|  |             enter="transition ease-out duration-100" | ||||||
|  |             enterFrom="opacity-0 translate-y-1" | ||||||
|  |             enterTo="opacity-100 translate-y-0" | ||||||
|  |             leave="transition ease-in duration-75" | ||||||
|  |             leaveFrom="opacity-100 translate-y-0" | ||||||
|  |             leaveTo="opacity-0 translate-y-1" | ||||||
|  |           > | ||||||
|  |             <PopoverPanel className="absolute right-0 mt-2 w-64 rounded-xl shadow-xl bg-light-primary dark:bg-dark-primary border border-light-200 dark:border-dark-200 z-50"> | ||||||
|  |               <div className="flex flex-col py-3 px-3 gap-2"> | ||||||
|  |                 <button | ||||||
|  |                   className="flex items-center gap-2 px-4 py-2 text-left hover:bg-light-secondary dark:hover:bg-dark-secondary transition-colors text-black dark:text-white rounded-lg font-medium" | ||||||
|  |                   onClick={() => exportAsMarkdown(messages, title || '')} | ||||||
|  |                 > | ||||||
|  |                   <FileText size={17} className="text-[#24A0ED]" /> | ||||||
|  |                   Export as Markdown | ||||||
|  |                 </button> | ||||||
|  |                 <button | ||||||
|  |                   className="flex items-center gap-2 px-4 py-2 text-left hover:bg-light-secondary dark:hover:bg-dark-secondary transition-colors text-black dark:text-white rounded-lg font-medium" | ||||||
|  |                   onClick={() => exportAsPDF(messages, title || '')} | ||||||
|  |                 > | ||||||
|  |                   <FileDown size={17} className="text-[#24A0ED]" /> | ||||||
|  |                   Export as PDF | ||||||
|  |                 </button> | ||||||
|  |               </div> | ||||||
|  |             </PopoverPanel> | ||||||
|  |           </Transition> | ||||||
|  |         </Popover> | ||||||
|  |         <DeleteChat redirect chatId={chatId} chats={[]} setChats={() => {}} /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default Navbar; | ||||||
							
								
								
									
										71
									
								
								src/components/NewsArticleWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,71 @@ | |||||||
|  | import { useEffect, useState } from 'react'; | ||||||
|  |  | ||||||
|  | interface Article { | ||||||
|  |   title: string; | ||||||
|  |   content: string; | ||||||
|  |   url: string; | ||||||
|  |   thumbnail: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const NewsArticleWidget = () => { | ||||||
|  |   const [article, setArticle] = useState<Article | null>(null); | ||||||
|  |   const [loading, setLoading] = useState(true); | ||||||
|  |   const [error, setError] = useState(false); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     fetch('/api/discover?mode=preview') | ||||||
|  |       .then((res) => res.json()) | ||||||
|  |       .then((data) => { | ||||||
|  |         const articles = (data.blogs || []).filter((a: Article) => a.thumbnail); | ||||||
|  |         setArticle(articles[Math.floor(Math.random() * articles.length)]); | ||||||
|  |         setLoading(false); | ||||||
|  |       }) | ||||||
|  |       .catch(() => { | ||||||
|  |         setError(true); | ||||||
|  |         setLoading(false); | ||||||
|  |       }); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="bg-light-secondary dark:bg-dark-secondary rounded-xl border border-light-200 dark:border-dark-200 shadow-sm flex flex-row items-center w-full h-24 min-h-[96px] max-h-[96px] px-3 py-2 gap-3 overflow-hidden"> | ||||||
|  |       {loading ? ( | ||||||
|  |         <> | ||||||
|  |           <div className="animate-pulse flex flex-row items-center w-full h-full"> | ||||||
|  |             <div className="rounded-lg w-16 min-w-16 max-w-16 h-16 min-h-16 max-h-16 bg-light-200 dark:bg-dark-200 mr-3" /> | ||||||
|  |             <div className="flex flex-col justify-center flex-1 h-full w-0 gap-2"> | ||||||
|  |               <div className="h-4 w-3/4 rounded bg-light-200 dark:bg-dark-200" /> | ||||||
|  |               <div className="h-3 w-1/2 rounded bg-light-200 dark:bg-dark-200" /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </> | ||||||
|  |       ) : error ? ( | ||||||
|  |         <div className="w-full text-xs text-red-400">Could not load news.</div> | ||||||
|  |       ) : article ? ( | ||||||
|  |         <a | ||||||
|  |           href={`/?q=Summary: ${article.url}`} | ||||||
|  |           className="flex flex-row items-center w-full h-full group" | ||||||
|  |         > | ||||||
|  |           <img | ||||||
|  |             className="object-cover rounded-lg w-16 min-w-16 max-w-16 h-16 min-h-16 max-h-16 border border-light-200 dark:border-dark-200 bg-light-200 dark:bg-dark-200 group-hover:opacity-90 transition" | ||||||
|  |             src={ | ||||||
|  |               new URL(article.thumbnail).origin + | ||||||
|  |               new URL(article.thumbnail).pathname + | ||||||
|  |               `?id=${new URL(article.thumbnail).searchParams.get('id')}` | ||||||
|  |             } | ||||||
|  |             alt={article.title} | ||||||
|  |           /> | ||||||
|  |           <div className="flex flex-col justify-center flex-1 h-full pl-3 w-0"> | ||||||
|  |             <div className="font-bold text-xs text-black dark:text-white leading-tight truncate overflow-hidden whitespace-nowrap"> | ||||||
|  |               {article.title} | ||||||
|  |             </div> | ||||||
|  |             <p className="text-black/70 dark:text-white/70 text-xs leading-snug truncate overflow-hidden whitespace-nowrap"> | ||||||
|  |               {article.content} | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |         </a> | ||||||
|  |       ) : null} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default NewsArticleWidget; | ||||||
| @@ -14,9 +14,11 @@ type Image = { | |||||||
| const SearchImages = ({ | const SearchImages = ({ | ||||||
|   query, |   query, | ||||||
|   chatHistory, |   chatHistory, | ||||||
|  |   messageId, | ||||||
| }: { | }: { | ||||||
|   query: string; |   query: string; | ||||||
|   chatHistory: Message[]; |   chatHistory: Message[]; | ||||||
|  |   messageId: string; | ||||||
| }) => { | }) => { | ||||||
|   const [images, setImages] = useState<Image[] | null>(null); |   const [images, setImages] = useState<Image[] | null>(null); | ||||||
|   const [loading, setLoading] = useState(false); |   const [loading, setLoading] = useState(false); | ||||||
| @@ -27,7 +29,7 @@ const SearchImages = ({ | |||||||
|     <> |     <> | ||||||
|       {!loading && images === null && ( |       {!loading && images === null && ( | ||||||
|         <button |         <button | ||||||
|           id="search-images" |           id={`search-images-${messageId}`} | ||||||
|           onClick={async () => { |           onClick={async () => { | ||||||
|             setLoading(true); |             setLoading(true); | ||||||
| 
 | 
 | ||||||
| @@ -37,9 +39,7 @@ const SearchImages = ({ | |||||||
|             const customOpenAIBaseURL = localStorage.getItem('openAIBaseURL'); |             const customOpenAIBaseURL = localStorage.getItem('openAIBaseURL'); | ||||||
|             const customOpenAIKey = localStorage.getItem('openAIApiKey'); |             const customOpenAIKey = localStorage.getItem('openAIApiKey'); | ||||||
| 
 | 
 | ||||||
|             const res = await fetch( |             const res = await fetch(`/api/images`, { | ||||||
|               `${process.env.NEXT_PUBLIC_API_URL}/images`, |  | ||||||
|               { |  | ||||||
|               method: 'POST', |               method: 'POST', | ||||||
|               headers: { |               headers: { | ||||||
|                 'Content-Type': 'application/json', |                 'Content-Type': 'application/json', | ||||||
| @@ -56,8 +56,7 @@ const SearchImages = ({ | |||||||
|                   }), |                   }), | ||||||
|                 }, |                 }, | ||||||
|               }), |               }), | ||||||
|               }, |             }); | ||||||
|             ); |  | ||||||
| 
 | 
 | ||||||
|             const data = await res.json(); |             const data = await res.json(); | ||||||
| 
 | 
 | ||||||