├── static ├── css │ ├── graphing.css │ ├── matrix.css │ ├── calculator.css │ ├── converter.css │ └── styles.css ├── images │ ├── logo.png │ ├── logo-icon.ico │ └── actualimage.png └── js │ ├── calculator.js │ ├── converter.js │ ├── matrix.js │ ├── graphing.js │ └── script.js ├── pyfiles ├── __init__.py ├── lovegraph.py └── geminicall.py ├── requirements.txt ├── .gitignore ├── vercel.json ├── templates ├── index.html ├── draw.html ├── converter.html ├── calculator.html ├── matrix.html └── graphing.html ├── README.md └── app.py /static/css/graphing.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/matrix.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/calculator.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/converter.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyfiles/__init__.py: -------------------------------------------------------------------------------- 1 | # This file makes pyfiles a Python package 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | plotly 3 | python-dotenv 4 | requests 5 | numpy 6 | gunicorn 7 | uvicorn -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anubhob435/trackbees-calc/HEAD/static/images/logo.png -------------------------------------------------------------------------------- /static/images/logo-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anubhob435/trackbees-calc/HEAD/static/images/logo-icon.ico -------------------------------------------------------------------------------- /static/images/actualimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anubhob435/trackbees-calc/HEAD/static/images/actualimage.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .env 3 | .vscode/ 4 | venv/ 5 | *.png 6 | !logo.png 7 | !actualimage.png 8 | requirements_backup.txt -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "app.py", 6 | "use": "@vercel/python" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "/app.py" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /static/js/calculator.js: -------------------------------------------------------------------------------- 1 | let display = document.getElementById('display'); 2 | 3 | function appendToDisplay(value) { 4 | display.value += value; 5 | } 6 | 7 | function clearDisplay() { 8 | display.value = ''; 9 | } 10 | 11 | function calculate() { 12 | try { 13 | display.value = eval(display.value); 14 | } catch (error) { 15 | display.value = 'Error'; 16 | } 17 | } 18 | 19 | // Add keyboard support 20 | document.addEventListener('keydown', (event) => { 21 | if (event.key === 'Enter') { 22 | calculate(); 23 | } else if (event.key === 'Escape') { 24 | clearDisplay(); 25 | } else if (/[\d\+\-\*\/\(\)\.]/.test(event.key)) { 26 | appendToDisplay(event.key); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /pyfiles/lovegraph.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import plotly.graph_objects as go 3 | 4 | def create_animated_heart(): 5 | # Create base coordinates 6 | x = np.linspace(-2, 2, 1000) 7 | frames = [] 8 | 9 | # Create frames for animation with more frames and slower speed 10 | for t in np.linspace(0, 4*np.pi, 120): # Doubled number of frames for smoother animation 11 | y = np.power(np.abs(x), 2/3) + 0.9 * np.sqrt(np.maximum(3.3 - x**2, 0)) * np.sin(t * np.pi * x) 12 | frames.append(go.Frame(data=[go.Scatter(x=x, y=y, mode='lines', line=dict(color='red', width=3))])) 13 | 14 | # Create the initial figure with larger size 15 | fig = go.Figure( 16 | data=[frames[0].data[0]], 17 | frames=frames 18 | ) 19 | 20 | # Update layout with larger size and settings 21 | fig.update_layout( 22 | title={ 23 | 'text': "Animated Love Graph ❤️", # Added color to title 24 | 'y': 0.9, 25 | 'x': 0.5, 26 | 'xanchor': 'center', 27 | 'yanchor': 'top', 28 | 'font': dict(size=24) 29 | }, 30 | showlegend=False, 31 | xaxis_visible=False, 32 | yaxis_visible=False, 33 | plot_bgcolor='white', 34 | width=1200, # Increased width further 35 | height=1000, # Increased height further 36 | margin=dict(l=20, r=20, t=60, b=20), # Reduced margins for more space 37 | updatemenus=[dict( 38 | type="buttons", 39 | showactive=False, 40 | x=0.5, # Center horizontally 41 | y=1.1, # Move above the title 42 | xanchor="center", 43 | yanchor="top", 44 | pad=dict(t=0, r=0), 45 | buttons=[dict( 46 | label="▶ Play", # Added play symbol 47 | method="animate", 48 | args=[None, dict( 49 | frame=dict(duration=100, redraw=True), # Increased duration for slower animation 50 | fromcurrent=True, 51 | mode='immediate', 52 | transition=dict(duration=50), # Added transition duration 53 | loop=True # Enable looping 54 | )] 55 | )] 56 | )], 57 | # Removed sliders section completely 58 | ) 59 | 60 | # Adjust axes ranges for even larger view 61 | fig.update_xaxes(range=[-4, 4]) # Increased range 62 | fig.update_yaxes(range=[-4, 4]) # Increased range 63 | 64 | return fig 65 | 66 | if __name__ == "__main__": 67 | create_animated_heart() 68 | -------------------------------------------------------------------------------- /pyfiles/geminicall.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import os 4 | import base64 5 | from dotenv import load_dotenv 6 | 7 | # Load environment variables from .env file 8 | load_dotenv() 9 | 10 | def generate_content(prompt, image_data=None): 11 | """ 12 | Generate content using Gemini API 13 | 14 | Args: 15 | prompt (str): Text prompt for the AI 16 | image_data (bytes, optional): Image data for multimodal input 17 | 18 | Returns: 19 | str: Generated text response 20 | """ 21 | # Get API key from environment variable 22 | api_key = os.getenv('GEMINI_API_KEY') 23 | 24 | if not api_key: 25 | raise ValueError("GEMINI_API_KEY not found in environment variables") 26 | 27 | # API endpoint 28 | url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}" 29 | 30 | # Request headers 31 | headers = { 32 | 'Content-Type': 'application/json' 33 | } 34 | 35 | # Prepare parts for the request 36 | parts = [{"text": prompt}] 37 | 38 | # Add image if provided 39 | if image_data: 40 | # Convert image bytes to base64 41 | image_b64 = base64.b64encode(image_data).decode('utf-8') 42 | parts.append({ 43 | "inline_data": { 44 | "mime_type": "image/png", 45 | "data": image_b64 46 | } 47 | }) 48 | 49 | # Request payload 50 | data = { 51 | "contents": [ 52 | { 53 | "parts": parts 54 | } 55 | ], 56 | "generationConfig": { 57 | "temperature": 1, 58 | "topP": 0.95, 59 | "topK": 40, 60 | "maxOutputTokens": 8192 61 | } 62 | } 63 | 64 | try: 65 | # Make the API request 66 | response = requests.post(url, headers=headers, json=data) 67 | response.raise_for_status() 68 | 69 | # Parse the response and extract just the text 70 | result = response.json() 71 | text_content = result['candidates'][0]['content']['parts'][0]['text'] 72 | return text_content.strip() 73 | 74 | except requests.exceptions.RequestException as e: 75 | raise Exception(f"Error making API request: {e}") 76 | except (KeyError, IndexError) as e: 77 | raise Exception(f"Error parsing response: {e}") 78 | 79 | def test_gemini_api(): 80 | """Test function for basic text generation""" 81 | try: 82 | response = generate_content("Explain how AI works in a few words") 83 | print(response) 84 | except Exception as e: 85 | print(f"Error: {e}") 86 | 87 | if __name__ == "__main__": 88 | test_gemini_api() -------------------------------------------------------------------------------- /static/js/converter.js: -------------------------------------------------------------------------------- 1 | let currentType = 'Length'; 2 | const unitMappings = { 3 | 'Length': { input: 'meters', output: 'feet' }, 4 | 'Weight': { input: 'kilograms', output: 'pounds' }, 5 | 'Temperature': { input: '°C', output: '°F' } 6 | }; 7 | 8 | document.addEventListener('DOMContentLoaded', function() { 9 | // Set up tab switching 10 | document.querySelectorAll('.converter-tab').forEach(tab => { 11 | tab.addEventListener('click', () => switchTab(tab)); 12 | }); 13 | 14 | // Set up conversion on input change 15 | document.getElementById('inputValue').addEventListener('input', performConversion); 16 | document.getElementById('convertBtn').addEventListener('click', performConversion); 17 | }); 18 | 19 | function switchTab(tab) { 20 | // Update active tab 21 | document.querySelectorAll('.converter-tab').forEach(t => t.classList.remove('active')); 22 | tab.classList.add('active'); 23 | 24 | // Update current type and unit labels 25 | currentType = tab.dataset.type; 26 | document.getElementById('inputUnit').textContent = unitMappings[currentType].input; 27 | document.getElementById('outputUnit').textContent = unitMappings[currentType].output; 28 | 29 | // Clear inputs 30 | document.getElementById('inputValue').value = ''; 31 | document.getElementById('outputValue').value = ''; 32 | } 33 | 34 | function performConversion() { 35 | const input = document.getElementById('inputValue').value; 36 | if (!input) { 37 | document.getElementById('outputValue').value = ''; 38 | return; 39 | } 40 | 41 | fetch('/convert-unit', { 42 | method: 'POST', 43 | headers: { 44 | 'Content-Type': 'application/json' 45 | }, 46 | body: JSON.stringify({ 47 | value: input, 48 | type: currentType 49 | }) 50 | }) 51 | .then(response => response.json()) 52 | .then(data => { 53 | if (data.status === 'success') { 54 | document.getElementById('outputValue').value = data.result; 55 | addToHistory(input, data.result); 56 | } else { 57 | showNotification('Error: ' + data.message, 'error'); 58 | } 59 | }) 60 | .catch(error => { 61 | showNotification('Error: ' + error, 'error'); 62 | }); 63 | } 64 | 65 | function addToHistory(input, result) { 66 | const historyList = document.getElementById('historyList'); 67 | const historyItem = document.createElement('div'); 68 | historyItem.className = 'history-item'; 69 | historyItem.innerHTML = ` 70 | ${input} ${unitMappings[currentType].input} 71 | 72 | ${result} 73 | `; 74 | historyList.insertBefore(historyItem, historyList.firstChild); 75 | 76 | // Keep only last 5 conversions 77 | if (historyList.children.length > 5) { 78 | historyList.removeChild(historyList.lastChild); 79 | } 80 | } 81 | 82 | function showNotification(message, type = 'success') { 83 | const notification = document.createElement('div'); 84 | notification.className = `notification ${type}`; 85 | notification.innerHTML = ` 86 | 87 | ${message} 88 | `; 89 | document.body.appendChild(notification); 90 | setTimeout(() => { 91 | notification.remove(); 92 | }, 3000); 93 | } 94 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Trackbeez Calculator Suite 5 | 6 | 7 | 8 | 53 | 54 |
55 |
56 | Trackbeez Logo 57 |
58 |

Trackbeez Calculator Suite

59 |

Choose your calculator type to get started

60 |
61 | 62 |
63 | 64 |
65 | 66 |

Scientific Calculator

67 |

Perform complex mathematical calculations with ease

68 |
69 | 70 | 71 |
72 | 73 |

Graphing Calculator

74 |

Visualize mathematical functions and equations

75 |
76 | 77 | 78 |
79 | 80 |

Matrix Calculator

81 |

Perform matrix operations and calculations

82 |
83 | 84 | 85 |
86 | 87 |

AI Drawing Calculator

88 |

Solve handwritten math problems using AI

89 |
90 | 91 | 92 |
93 | 94 |

Unit Converter

95 |

Convert between different units of measurement

96 |
97 |
98 | 99 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /static/js/matrix.js: -------------------------------------------------------------------------------- 1 | function createMatrix(rows, cols, containerId) { 2 | const container = document.getElementById(containerId); 3 | container.innerHTML = ''; 4 | container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`; 5 | 6 | for (let i = 0; i < rows * cols; i++) { 7 | const input = document.createElement('input'); 8 | input.type = 'number'; 9 | input.value = '0'; 10 | input.className = 'matrix-input'; 11 | container.appendChild(input); 12 | } 13 | } 14 | 15 | function createMatrixA() { 16 | const rows = parseInt(document.getElementById('matrixARows').value); 17 | const cols = parseInt(document.getElementById('matrixACols').value); 18 | createMatrix(rows, cols, 'matrixA'); 19 | } 20 | 21 | function createMatrixB() { 22 | const rows = parseInt(document.getElementById('matrixBRows').value); 23 | const cols = parseInt(document.getElementById('matrixBCols').value); 24 | createMatrix(rows, cols, 'matrixB'); 25 | } 26 | 27 | function getMatrixValues(containerId, rows, cols) { 28 | const inputs = document.querySelectorAll(`#${containerId} input`); 29 | const matrix = []; 30 | for (let i = 0; i < rows; i++) { 31 | matrix[i] = []; 32 | for (let j = 0; j < cols; j++) { 33 | matrix[i][j] = parseFloat(inputs[i * cols + j].value) || 0; 34 | } 35 | } 36 | return matrix; 37 | } 38 | 39 | function displayResult(result) { 40 | const container = document.getElementById('resultMatrix'); 41 | container.innerHTML = ''; 42 | container.style.gridTemplateColumns = `repeat(${result[0].length}, 1fr)`; 43 | 44 | for (let i = 0; i < result.length; i++) { 45 | for (let j = 0; j < result[0].length; j++) { 46 | const input = document.createElement('input'); 47 | input.type = 'number'; 48 | input.value = result[i][j].toFixed(2); 49 | input.className = 'matrix-input'; 50 | input.readOnly = true; 51 | container.appendChild(input); 52 | } 53 | } 54 | } 55 | 56 | function performOperation(operation) { 57 | const aRows = parseInt(document.getElementById('matrixARows').value); 58 | const aCols = parseInt(document.getElementById('matrixACols').value); 59 | const bRows = parseInt(document.getElementById('matrixBRows').value); 60 | const bCols = parseInt(document.getElementById('matrixBCols').value); 61 | 62 | if (operation === 'Add' && (aRows !== bRows || aCols !== bCols)) { 63 | showNotification('Matrices must have the same dimensions for addition', 'error'); 64 | return; 65 | } 66 | 67 | if (operation === 'Multiply' && aCols !== bRows) { 68 | showNotification('Number of columns in A must equal number of rows in B for multiplication', 'error'); 69 | return; 70 | } 71 | 72 | const matrixA = getMatrixValues('matrixA', aRows, aCols); 73 | const matrixB = getMatrixValues('matrixB', bRows, bCols); 74 | 75 | fetch('/matrix-calculate', { 76 | method: 'POST', 77 | headers: { 78 | 'Content-Type': 'application/json' 79 | }, 80 | body: JSON.stringify({ 81 | matrix_a: matrixA, 82 | matrix_b: matrixB, 83 | operation: operation 84 | }) 85 | }) 86 | .then(response => response.json()) 87 | .then(data => { 88 | if (data.status === 'success') { 89 | displayResult(data.result); 90 | showNotification('Operation completed successfully', 'success'); 91 | } else { 92 | showNotification('Error: ' + data.message, 'error'); 93 | } 94 | }) 95 | .catch(error => { 96 | showNotification('Error: ' + error, 'error'); 97 | }); 98 | } 99 | 100 | function showNotification(message, type = 'success') { 101 | const notification = document.createElement('div'); 102 | notification.className = `notification ${type}`; 103 | notification.innerHTML = ` 104 | 105 | ${message} 106 | `; 107 | document.body.appendChild(notification); 108 | setTimeout(() => { 109 | notification.remove(); 110 | }, 3000); 111 | } 112 | 113 | // Initialize matrices on page load 114 | document.addEventListener('DOMContentLoaded', function() { 115 | createMatrixA(); 116 | createMatrixB(); 117 | }); 118 | -------------------------------------------------------------------------------- /static/js/graphing.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | // Initialize plot with default settings 3 | plotFunction(); 4 | 5 | // Add keyboard support 6 | document.getElementById('function').addEventListener('keydown', (event) => { 7 | if (event.key === 'Enter') { 8 | plotFunction(); 9 | } 10 | }); 11 | }); 12 | 13 | function insertMathSymbol(symbol) { 14 | const functionInput = document.getElementById('function'); 15 | const cursorPos = functionInput.selectionStart; 16 | const textBefore = functionInput.value.substring(0, cursorPos); 17 | const textAfter = functionInput.value.substring(cursorPos); 18 | functionInput.value = textBefore + symbol + textAfter; 19 | functionInput.focus(); 20 | 21 | // Set cursor position after the inserted symbol 22 | const newPosition = cursorPos + symbol.length; 23 | functionInput.setSelectionRange(newPosition, newPosition); 24 | } 25 | 26 | function plotFunction() { 27 | const functionStr = document.getElementById('function').value || 'x^2'; 28 | const xMin = parseFloat(document.getElementById('xMin').value); 29 | const xMax = parseFloat(document.getElementById('xMax').value); 30 | const yMin = parseFloat(document.getElementById('yMin').value); 31 | const yMax = parseFloat(document.getElementById('yMax').value); 32 | 33 | // Show loading state 34 | document.getElementById('plotBtn').innerHTML = ' Plotting...'; 35 | 36 | fetch('/plot-function', { 37 | method: 'POST', 38 | headers: { 39 | 'Content-Type': 'application/json' 40 | }, 41 | body: JSON.stringify({ 42 | function: functionStr, 43 | xRange: [xMin, xMax], 44 | yRange: [yMin, yMax] 45 | }) 46 | }) 47 | .then(response => response.json()) 48 | .then(data => { 49 | if (data.status === 'success') { 50 | const plotData = JSON.parse(data.plot); 51 | 52 | // Update layout with custom ranges 53 | plotData.layout.xaxis = { range: [xMin, xMax] }; 54 | plotData.layout.yaxis = { range: [yMin, yMax] }; 55 | plotData.layout.title.text = `y = ${functionStr}`; 56 | 57 | Plotly.newPlot('graph', plotData.data, plotData.layout); 58 | } else { 59 | showNotification('Error: ' + data.message, 'error'); 60 | } 61 | }) 62 | .catch(error => { 63 | showNotification('Error: ' + error, 'error'); 64 | }) 65 | .finally(() => { 66 | // Reset button state 67 | document.getElementById('plotBtn').innerHTML = ' Plot Function'; 68 | }); 69 | } 70 | 71 | function showNotification(message, type = 'success') { 72 | const notification = document.createElement('div'); 73 | notification.className = `notification ${type}`; 74 | notification.innerHTML = ` 75 | 76 | ${message} 77 | `; 78 | document.body.appendChild(notification); 79 | setTimeout(() => { 80 | notification.remove(); 81 | }, 3000); 82 | } 83 | 84 | function switchTab(tab) { 85 | document.querySelectorAll('.graph-tab').forEach(t => t.classList.remove('active')); 86 | document.querySelector(`.graph-tab[onclick*="${tab}"]`).classList.add('active'); 87 | 88 | document.getElementById('functionPlot').style.display = tab === 'function' ? 'block' : 'none'; 89 | document.getElementById('loveGraph').style.display = tab === 'love' ? 'block' : 'none'; 90 | 91 | if (tab === 'love') { 92 | plotLoveGraph(); 93 | } 94 | } 95 | 96 | function plotLoveGraph() { 97 | const btn = document.getElementById('animateBtn'); 98 | btn.disabled = true; 99 | btn.innerHTML = ' Generating...'; 100 | 101 | fetch('/plot-love-graph', { 102 | method: 'POST', 103 | headers: { 104 | 'Content-Type': 'application/json' 105 | } 106 | }) 107 | .then(response => response.json()) 108 | .then(data => { 109 | if (data.status === 'success') { 110 | const plotData = JSON.parse(data.plot); 111 | Plotly.newPlot('loveGraphPlot', plotData.data, plotData.layout); 112 | showNotification('Love graph animated! ❤️', 'success'); 113 | } else { 114 | showNotification('Error: ' + data.message, 'error'); 115 | } 116 | }) 117 | .catch(error => { 118 | showNotification('Error: ' + error, 'error'); 119 | }) 120 | .finally(() => { 121 | btn.disabled = false; 122 | btn.innerHTML = ' Animate Love Graph'; 123 | }); 124 | } 125 | -------------------------------------------------------------------------------- /templates/draw.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drawing Board 5 | 6 | 7 | 8 | 9 | 10 |
53 | 54 |
55 |
56 |
57 | 58 | 59 |
60 |
61 | 62 | 63 | 5px 64 |
65 |
66 | 67 | 68 | 69 | 70 |
71 |
72 |
73 | 74 |
75 | 82 |
83 |
84 |

Saved Drawings

85 |
86 |
87 |
88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trackbeez Calculator Suite 🧮 2 | 3 |
4 | 5 | ![License](https://img.shields.io/badge/license-MIT-blue.svg) 6 | ![Python](https://img.shields.io/badge/python-3.8%2B-brightgreen) 7 | ![Flask](https://img.shields.io/badge/flask-2.0%2B-orange) 8 | ![Gemini AI](https://img.shields.io/badge/Gemini%20AI-Powered-blueviolet) 9 | 10 | A sophisticated calculator suite that combines advanced mathematical functions with modern web technologies and AI capabilities. 11 | 12 | [Live Demo](https://your-demo-link.com) | [Report Bug](https://github.com/yourusername/trackbeez-calc/issues) | [Request Feature](https://github.com/yourusername/trackbeez-calc/issues) 13 | 14 |
15 | 16 | ## ✨ Key Features 17 | 18 | ### 🔬 Scientific Calculator 19 | - **Advanced Functions**: sin, cos, tan, log, ln 20 | - **Real-time Calculations**: Instant results as you type 21 | - **Keyboard Support**: Full keyboard input functionality 22 | - **Memory Functions**: Store and recall values 23 | 24 | ### 📊 Graphing Calculator 25 | - **Interactive Plotting**: Dynamic function visualization 26 | - **Custom Ranges**: Adjustable x and y axis ranges 27 | - **Special Functions**: Trigonometric, logarithmic, exponential 28 | - **Love Graph**: Unique animated heart graph feature ❤️ 29 | 30 | ### 📐 Matrix Calculator 31 | - **Matrix Operations**: Addition, multiplication, transposition 32 | - **Dynamic Sizing**: Adjustable matrix dimensions 33 | - **Visual Interface**: Intuitive grid-based input 34 | - **Error Handling**: Real-time validation and feedback 35 | 36 | ### 🔄 Unit Converter 37 | - **Multiple Units**: Length, weight, temperature 38 | - **Real-time Conversion**: Instant results 39 | - **History Tracking**: Recent conversions log 40 | - **Clean Interface**: User-friendly design 41 | 42 | ### 🎨 AI Drawing Calculator 43 | - **Handwriting Recognition**: Powered by Google's Gemini AI 44 | - **Step-by-step Solutions**: Detailed mathematical breakdowns 45 | - **History Management**: Save and review calculations 46 | - **Export Options**: Share or save results 47 | 48 | ## 🚀 Quick Start 49 | 50 | ### Prerequisites 51 | - Python 3.8+ 52 | - pip (Python package installer) 53 | - Google Gemini API key 54 | 55 | ### Installation 56 | 57 | 1. Clone the repository 58 | ```bash 59 | git clone https://github.com/yourusername/trackbeez-calc.git 60 | cd trackbees-calc 61 | 62 | 63 | 2. Set up virtual environment 64 | ```bash 65 | python -m venv venv 66 | source venv/bin/activate # Linux/macOS 67 | venv\Scripts\activate # Windows 68 | ``` 69 | 70 | 3. Install dependencies 71 | ```bash 72 | pip install -r requirements.txt 73 | ``` 74 | 75 | 4. Configure environment 76 | ```bash 77 | cp .env.example .env 78 | # Edit .env and add your GEMINI_API_KEY 79 | ``` 80 | 81 | 5. Run the application 82 | ```bash 83 | flask run 84 | ``` 85 | 86 | ## 🎯 Features in Detail 87 | 88 | ### Theme Support 89 | - 🌓 Dark/Light mode switching 90 | - 🎨 Custom color schemes 91 | - 💫 Smooth transitions 92 | 93 | ### Responsive Design 94 | - 📱 Mobile-first approach 95 | - 🖥️ Desktop-optimized layouts 96 | - 🔄 Dynamic UI adjustments 97 | 98 | ### Advanced Functionality 99 | - ⚡ Real-time calculations 100 | - 🔒 Error prevention 101 | - 📊 Visual feedback 102 | - 💾 Local storage support 103 | 104 | ## 🛠️ Technical Stack 105 | 106 | ### Frontend 107 | - HTML5/CSS3 with Custom Properties 108 | - JavaScript (ES6+) 109 | - Plotly.js for data visualization 110 | - Font Awesome icons 111 | 112 | ### Backend 113 | - Flask web framework 114 | - NumPy for calculations 115 | - Google Gemini AI integration 116 | - Plotly for graph generation 117 | 118 | ## 📚 Documentation 119 | 120 | Detailed documentation is available in the [Wiki](https://github.com/yourusername/trackbeez-calc/wiki). 121 | 122 | ## 🤝 Contributing 123 | 124 | 1. Fork the repository 125 | 2. Create your feature branch 126 | ```bash 127 | git checkout -b feature/AmazingFeature 128 | ``` 129 | 3. Commit your changes 130 | ```bash 131 | git commit -m 'Add some AmazingFeature' 132 | ``` 133 | 4. Push to the branch 134 | ```bash 135 | git push origin feature/AmazingFeature 136 | ``` 137 | 5. Open a Pull Request 138 | 139 | ## 📝 License 140 | 141 | Distributed under the MIT License. See `LICENSE` for more information. 142 | 143 | ## 👥 Team 144 | 145 | - **Your Name** - *Lead Developer* - [@yourusername](https://github.com/yourusername) 146 | - **Contributor Name** - *Feature X Developer* - [@contributorusername](https://github.com/contributorusername) 147 | 148 | ## 🙏 Acknowledgments 149 | 150 | - [Flask](https://flask.palletsprojects.com/) 151 | - [Plotly](https://plotly.com/) 152 | - [Google Gemini AI](https://deepmind.google/technologies/gemini/) 153 | - [Font Awesome](https://fontawesome.com/) 154 | 155 | ## 📊 Project Status 156 | 157 | - ✅ Scientific Calculator 158 | - ✅ Graphing Calculator 159 | - ✅ Matrix Operations 160 | - ✅ Unit Converter 161 | - ✅ AI Drawing Recognition 162 | - 🚧 More features coming soon! 163 | 164 | --- 165 |
166 | Made with ❤️ by the Trackbeez Team 167 |
168 | -------------------------------------------------------------------------------- /templates/converter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unit Converter 5 | 6 | 7 | 8 | 9 | 10 |
11 | 54 | 55 | 56 |
57 |
58 |
59 | 62 | 65 | 68 |
69 | 70 |
71 |
72 | 73 | meters 74 |
75 |
76 | 77 |
78 |
79 | 80 | feet 81 |
82 |
83 | 84 | 87 | 88 |
89 |

Recent Conversions

90 |
91 |
92 |
93 |
94 |
95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /templates/calculator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scientific Calculator 5 | 6 | 7 | 8 | 9 | 10 |
53 | 54 |
55 |
56 | 57 |
58 | 59 |
60 |
61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 | 91 |
92 |
93 |
94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /templates/matrix.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix Calculator 5 | 6 | 7 | 8 | 9 | 10 |
11 | 54 | 55 | 56 |
57 |
58 | 59 |
60 |

Matrix A

61 |
62 |
63 | 64 | × 65 | 66 |
67 | 68 |
69 |
70 |
71 | 72 | 73 |
74 |

Matrix B

75 |
76 |
77 | 78 | × 79 | 80 |
81 | 82 |
83 |
84 |
85 | 86 | 87 |
88 | 91 | 94 | 97 |
98 | 99 | 100 |
101 |

Result

102 |
103 |
104 |
105 |
106 |
107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /templates/graphing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Graphing Calculator 5 | 6 | 7 | 8 | 9 | 10 | 11 |
54 | 55 |
56 |
57 | 60 | 63 |
64 | 65 |
66 |
67 |
68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 |
76 |
77 |
78 |
79 | 80 | to 81 | 82 |
83 |
84 | 85 | to 86 | 87 |
88 |
89 | 92 |
93 |
94 |
95 |
96 |
97 | 98 | 108 |
109 |
110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /static/js/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | const canvas = document.getElementById('drawingCanvas'); 3 | const ctx = canvas.getContext('2d'); 4 | const colorPicker = document.getElementById('colorPicker'); 5 | const brushSize = document.getElementById('brushSize'); 6 | const brushSizeValue = document.getElementById('brushSizeValue'); 7 | const clearBtn = document.getElementById('clearBtn'); 8 | const saveBtn = document.getElementById('saveBtn'); 9 | const drawingsContainer = document.getElementById('drawings-container'); 10 | const aiBtn = document.getElementById('aiBtn'); 11 | const themeBtn = document.getElementById('themeBtn'); 12 | const aiResultBox = document.getElementById('aiResultBox'); 13 | const aiResultContent = document.getElementById('aiResultContent'); 14 | const closeAiResult = document.getElementById('closeAiResult'); 15 | 16 | // Add reference to base URL 17 | const baseUrl = window.location.origin; 18 | 19 | // Set initial color based on theme 20 | colorPicker.value = "#ffffff"; // Start with white for dark theme 21 | let isDarkTheme = true; 22 | 23 | // Add loading state to buttons 24 | function setButtonLoading(button, isLoading) { 25 | if (isLoading) { 26 | button.disabled = true; 27 | button.innerHTML = ' Processing...'; 28 | } else { 29 | button.disabled = false; 30 | button.innerHTML = button.id === 'saveBtn' ? 31 | ' Save' : 32 | ' Clear'; 33 | } 34 | } 35 | 36 | // Theme toggle function 37 | function toggleTheme() { 38 | isDarkTheme = !isDarkTheme; 39 | document.documentElement.setAttribute('data-theme', isDarkTheme ? 'dark' : 'light'); 40 | themeBtn.innerHTML = isDarkTheme ? 41 | ' Theme' : 42 | ' Theme'; 43 | 44 | // Update canvas background and brush color 45 | ctx.fillStyle = isDarkTheme ? '#23272a' : '#ffffff'; 46 | ctx.fillRect(0, 0, canvas.width, canvas.height); 47 | 48 | // Update brush color based on theme 49 | colorPicker.value = isDarkTheme ? '#ffffff' : '#000000'; 50 | } 51 | 52 | // Add theme button listener 53 | themeBtn.addEventListener('click', toggleTheme); 54 | 55 | // Set initial canvas background 56 | function setCanvasBackground() { 57 | ctx.fillStyle = isDarkTheme ? '#23272a' : '#ffffff'; 58 | ctx.fillRect(0, 0, canvas.width, canvas.height); 59 | } 60 | 61 | // Set canvas size 62 | function resizeCanvas() { 63 | canvas.width = canvas.offsetWidth; 64 | canvas.height = canvas.offsetHeight; 65 | setCanvasBackground(); // Maintain dark background after resize 66 | } 67 | resizeCanvas(); 68 | window.addEventListener('resize', resizeCanvas); 69 | 70 | // Drawing state 71 | let isDrawing = false; 72 | let lastX = 0; 73 | let lastY = 0; 74 | 75 | // Drawing functions 76 | function draw(e) { 77 | if (!isDrawing) return; 78 | 79 | ctx.beginPath(); 80 | ctx.moveTo(lastX, lastY); 81 | ctx.lineTo(e.offsetX, e.offsetY); 82 | ctx.strokeStyle = colorPicker.value; 83 | ctx.lineWidth = brushSize.value; 84 | ctx.lineCap = 'round'; 85 | ctx.stroke(); 86 | 87 | [lastX, lastY] = [e.offsetX, e.offsetY]; 88 | } 89 | 90 | // Event listeners 91 | canvas.addEventListener('mousedown', (e) => { 92 | isDrawing = true; 93 | [lastX, lastY] = [e.offsetX, e.offsetY]; 94 | }); 95 | 96 | canvas.addEventListener('mousemove', draw); 97 | canvas.addEventListener('mouseup', () => isDrawing = false); 98 | canvas.addEventListener('mouseout', () => isDrawing = false); 99 | 100 | // Update brush size display 101 | brushSize.addEventListener('input', () => { 102 | brushSizeValue.textContent = `${brushSize.value}px`; 103 | }); 104 | 105 | // Enhanced clear canvas with confirmation 106 | clearBtn.addEventListener('click', () => { 107 | if (confirm('Are you sure you want to clear the canvas?')) { 108 | setButtonLoading(clearBtn, true); 109 | setTimeout(() => { 110 | setCanvasBackground(); // Use the function instead of direct color 111 | setButtonLoading(clearBtn, false); 112 | }, 500); 113 | } 114 | }); 115 | 116 | // Enhanced save functionality with feedback and preview 117 | saveBtn.addEventListener('click', async () => { 118 | try { 119 | setButtonLoading(saveBtn, true); 120 | 121 | // Create preview 122 | const timestamp = new Date().toLocaleTimeString(); 123 | const previewDiv = document.createElement('div'); 124 | previewDiv.className = 'drawing-preview'; 125 | previewDiv.innerHTML = ` 126 | Drawing at ${timestamp} 127 |
${timestamp}
128 | `; 129 | 130 | // Add preview to container 131 | drawingsContainer.insertBefore(previewDiv, drawingsContainer.firstChild); 132 | 133 | showNotification('Drawing saved to preview panel', 'success'); 134 | } catch (error) { 135 | showNotification('Error saving drawing', 'error'); 136 | console.error('Error:', error); 137 | } finally { 138 | setButtonLoading(saveBtn, false); 139 | } 140 | }); 141 | 142 | // AI button handler 143 | aiBtn.addEventListener('click', async () => { 144 | try { 145 | setButtonLoading(aiBtn, true); 146 | aiBtn.innerHTML = ' Processing...'; 147 | 148 | const drawing = canvas.toDataURL(); 149 | const response = await fetch(`${baseUrl}/ai-calculate`, { 150 | method: 'POST', 151 | headers: { 152 | 'Content-Type': 'application/json' 153 | }, 154 | body: JSON.stringify({ image: drawing }) 155 | }); 156 | 157 | const data = await response.json(); 158 | if (data.status === 'success') { 159 | aiResultContent.textContent = data.result; 160 | aiResultBox.classList.remove('hidden'); 161 | showNotification('AI calculation complete!', 'success'); 162 | } else { 163 | showNotification('AI calculation failed', 'error'); 164 | } 165 | } catch (error) { 166 | showNotification('Error during AI calculation', 'error'); 167 | console.error('Error:', error); 168 | } finally { 169 | setButtonLoading(aiBtn, false); 170 | aiBtn.innerHTML = ' AI Calculate'; 171 | } 172 | }); 173 | 174 | // Close AI result box 175 | closeAiResult.addEventListener('click', () => { 176 | aiResultBox.classList.add('hidden'); 177 | }); 178 | 179 | // Add notification system 180 | function showNotification(message, type = 'success') { 181 | const notification = document.createElement('div'); 182 | notification.className = `notification ${type}`; 183 | notification.innerHTML = ` 184 | 185 | ${message} 186 | `; 187 | document.body.appendChild(notification); 188 | setTimeout(() => { 189 | notification.remove(); 190 | }, 3000); 191 | } 192 | }); 193 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, jsonify 2 | import numpy as np 3 | import base64 4 | from dotenv import load_dotenv 5 | import plotly.graph_objects as go 6 | import re 7 | import os 8 | from pyfiles.geminicall import generate_content 9 | 10 | app = Flask(__name__, 11 | static_folder='static', 12 | template_folder='templates' 13 | ) 14 | load_dotenv() 15 | 16 | # Check if API key exists 17 | api_key = os.getenv("GEMINI_API_KEY") 18 | if not api_key: 19 | raise ValueError("No API key found. Please set the GEMINI_API_KEY environment variable.") 20 | 21 | @app.route('/') 22 | def home(): 23 | return render_template('index.html') # Changed from calculator.html 24 | 25 | @app.route('/calculator') 26 | def calculator(): 27 | return render_template('calculator.html') 28 | 29 | @app.route('/graphing') 30 | def graphing(): 31 | return render_template('graphing.html') 32 | 33 | @app.route('/matrix') 34 | def matrix(): 35 | return render_template('matrix.html') 36 | 37 | @app.route('/converter') 38 | def converter(): 39 | return render_template('converter.html') 40 | 41 | @app.route('/draw') 42 | def draw(): 43 | return render_template('draw.html') # Add drawing page route 44 | 45 | @app.route('/plot-function', methods=['POST']) 46 | def plot_function(): 47 | try: 48 | data = request.json 49 | function = data.get('function', 'x^2').replace('^', '**') 50 | x_range = data.get('xRange', [-10, 10]) 51 | y_range = data.get('yRange', [-10, 10]) 52 | 53 | x = np.linspace(x_range[0], x_range[1], 500) 54 | y = [eval(function.replace('x', str(i))) for i in x] 55 | 56 | fig = go.Figure() 57 | fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name=function)) 58 | fig.update_layout( 59 | title={'text': f'y = {function}', 'x': 0.5}, 60 | xaxis_title='x', 61 | yaxis_title='y', 62 | showlegend=True, 63 | hovermode='x unified' 64 | ) 65 | 66 | return jsonify({ 67 | "status": "success", 68 | "plot": fig.to_json() 69 | }) 70 | except Exception as e: 71 | return jsonify({ 72 | "status": "error", 73 | "message": str(e) 74 | }) 75 | 76 | @app.route('/matrix-calculate', methods=['POST']) 77 | def matrix_calculate(): 78 | try: 79 | data = request.json 80 | matrix_a = np.array(data.get('matrix_a')) 81 | matrix_b = np.array(data.get('matrix_b')) 82 | operation = data.get('operation') 83 | 84 | if operation == "Add": 85 | result = np.add(matrix_a, matrix_b) 86 | elif operation == "Multiply": 87 | result = np.dot(matrix_a, matrix_b) 88 | elif operation == "Transpose A": 89 | result = np.transpose(matrix_a) 90 | 91 | return jsonify({ 92 | "status": "success", 93 | "result": result.tolist() 94 | }) 95 | except Exception as e: 96 | return jsonify({ 97 | "status": "error", 98 | "message": str(e) 99 | }) 100 | 101 | @app.route('/convert-unit', methods=['POST']) 102 | def convert_unit(): 103 | try: 104 | data = request.json 105 | value = float(data.get('value', 0)) 106 | unit_type = data.get('type') 107 | 108 | if unit_type == "Length": 109 | result = value * 3.28084 110 | response = f"{value} meters = {result:.2f} feet" 111 | elif unit_type == "Weight": 112 | result = value * 2.20462 113 | response = f"{value} kilograms = {result:.2f} pounds" 114 | elif unit_type == "Temperature": 115 | result = (value * 9/5) + 32 116 | response = f"{value}°C = {result:.2f}°F" 117 | 118 | return jsonify({ 119 | "status": "success", 120 | "result": response 121 | }) 122 | except Exception as e: 123 | return jsonify({ 124 | "status": "error", 125 | "message": str(e) 126 | }) 127 | 128 | @app.route('/save', methods=['POST']) 129 | def save_drawing(): 130 | try: 131 | data = request.json 132 | image_data = data.get('image') 133 | # You can implement saving functionality here 134 | return jsonify({"status": "success"}) 135 | except Exception as e: 136 | return jsonify({"status": "error", "message": str(e)}) 137 | 138 | @app.route('/ai-calculate', methods=['POST']) 139 | def ai_calculate(): 140 | try: 141 | data = request.json 142 | image_data = data.get('image') 143 | 144 | # Extract base64 image data 145 | image_data = re.sub('^data:image/.+;base64,', '', image_data) 146 | image_bytes = base64.b64decode(image_data) 147 | 148 | # Prepare prompt for mathematical analysis 149 | prompt = """You are a mathematical expert. Given this handwritten mathematical expression: 150 | 1. First, identify what is written 151 | 2. Then, solve it step by step 152 | 3. Finally, provide the final answer 153 | 154 | Format your response clearly with: 155 | - Expression Identified: [what you see] 156 | - Step-by-step Solution: [numbered steps] 157 | - Final Answer: [result] 158 | 159 | Be precise and show all mathematical steps. 160 | do not use * ** in your response""" 161 | 162 | # Generate response using geminicall 163 | response_text = generate_content(prompt, image_bytes) 164 | 165 | return jsonify({ 166 | "status": "success", 167 | "result": response_text 168 | }) 169 | except Exception as e: 170 | return jsonify({ 171 | "status": "error", 172 | "message": str(e) 173 | }) 174 | 175 | def create_animated_heart(): 176 | # Create base coordinates for heart shape 177 | x = np.linspace(-2, 2, 1000) 178 | frames = [] 179 | 180 | # Create frames for animation with smooth transitions 181 | for t in np.linspace(0, 4*np.pi, 120): 182 | y = np.power(np.abs(x), 2/3) + 0.9 * np.sqrt(np.maximum(3.3 - x**2, 0)) * np.sin(t * np.pi * x) 183 | frames.append(go.Frame(data=[go.Scatter( 184 | x=x, 185 | y=y, 186 | mode='lines', 187 | line=dict( 188 | color='red', 189 | width=3 190 | ) 191 | )])) 192 | 193 | # Create initial figure 194 | fig = go.Figure( 195 | data=[frames[0].data[0]], 196 | frames=frames 197 | ) 198 | 199 | # Enhanced layout configuration 200 | fig.update_layout( 201 | title={ 202 | 'text': "Animated Love Graph ❤️", 203 | 'y': 0.9, 204 | 'x': 0.5, 205 | 'xanchor': 'center', 206 | 'yanchor': 'top', 207 | 'font': dict(size=24) 208 | }, 209 | showlegend=False, 210 | xaxis_visible=False, 211 | yaxis_visible=False, 212 | plot_bgcolor='rgba(0,0,0,0)', 213 | width=1200, 214 | height=1000, 215 | margin=dict(l=20, r=20, t=60, b=20), 216 | updatemenus=[dict( 217 | type="buttons", 218 | showactive=False, 219 | x=0.5, 220 | y=1.1, 221 | xanchor="center", 222 | yanchor="top", 223 | pad=dict(t=0, r=0), 224 | buttons=[dict( 225 | label="▶ Play", 226 | method="animate", 227 | args=[None, dict( 228 | frame=dict(duration=100, redraw=True), 229 | fromcurrent=True, 230 | mode='immediate', 231 | transition=dict(duration=50), 232 | loop=True 233 | )] 234 | )] 235 | )] 236 | ) 237 | 238 | # Set axis ranges for better view 239 | fig.update_xaxes(range=[-4, 4]) 240 | fig.update_yaxes(range=[-4, 4]) 241 | 242 | return fig 243 | 244 | @app.route('/plot-love-graph', methods=['POST']) 245 | def plot_love_graph(): 246 | try: 247 | fig = create_animated_heart() 248 | return jsonify({ 249 | "status": "success", 250 | "plot": fig.to_json() 251 | }) 252 | except Exception as e: 253 | return jsonify({ 254 | "status": "error", 255 | "message": str(e) 256 | }) 257 | 258 | if __name__ == '__main__': 259 | app.run(debug=True) 260 | -------------------------------------------------------------------------------- /static/css/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: #7289da; 3 | --secondary-color: #99aab5; 4 | --background-color: #2c2f33; 5 | --tool-bg-color: #23272a; 6 | --text-color: #ffffff; 7 | --shadow: 0 4px 6px rgba(0, 0, 0, 0.3); 8 | --transition: all 0.3s ease; 9 | --button-hover: #677bc4; 10 | --ai-color: #9b59b6; 11 | --ai-hover: #8e44ad; 12 | --theme-color: #ffd700; 13 | --theme-hover: #ffc800; 14 | } 15 | 16 | body { 17 | margin: 0; 18 | padding: 0; 19 | background: var(--background-color); 20 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 21 | color: var(--text-color); 22 | } 23 | 24 | .header { 25 | background: var(--tool-bg-color); 26 | color: var(--text-color); 27 | padding: 1rem; 28 | text-align: center; 29 | box-shadow: var(--shadow); 30 | border-bottom: 2px solid var(--primary-color); 31 | } 32 | 33 | .header h1 { 34 | margin: 0; 35 | font-size: 2rem; 36 | } 37 | 38 | .container { 39 | display: flex; 40 | gap: 20px; 41 | max-width: 100%; 42 | margin: 1rem; 43 | height: calc(100vh - 2rem); 44 | } 45 | 46 | .main-content { 47 | flex: 1; 48 | display: flex; 49 | flex-direction: column; 50 | gap: 1rem; 51 | margin-left: 280px; 52 | padding: 1.5rem; 53 | width: calc(100% - 280px); 54 | } 55 | 56 | .tools { 57 | background: var(--tool-bg-color); 58 | padding: 1rem; 59 | border-radius: 10px; 60 | box-shadow: var(--shadow); 61 | border: 1px solid var(--primary-color); 62 | display: flex; 63 | gap: 2rem; 64 | align-items: center; 65 | } 66 | 67 | .preview-panel { 68 | width: 250px; 69 | background: var(--tool-bg-color); 70 | border-radius: 10px; 71 | padding: 1rem; 72 | border: 1px solid var(--primary-color); 73 | display: flex; 74 | flex-direction: column; 75 | max-height: 100%; 76 | } 77 | 78 | .preview-panel h3 { 79 | margin-top: 0; 80 | color: var(--text-color); 81 | border-bottom: 1px solid var(--primary-color); 82 | padding-bottom: 0.5rem; 83 | } 84 | 85 | #drawings-container { 86 | overflow-y: auto; 87 | flex: 1; 88 | padding-right: 0.5rem; 89 | margin-top: 1rem; 90 | /* Scrollbar styling */ 91 | scrollbar-width: thin; 92 | scrollbar-color: var(--primary-color) var(--tool-bg-color); 93 | } 94 | 95 | #drawings-container::-webkit-scrollbar { 96 | width: 6px; 97 | } 98 | 99 | #drawings-container::-webkit-scrollbar-track { 100 | background: var(--tool-bg-color); 101 | } 102 | 103 | #drawings-container::-webkit-scrollbar-thumb { 104 | background-color: var(--primary-color); 105 | border-radius: 3px; 106 | } 107 | 108 | .drawing-preview { 109 | margin-bottom: 1rem; 110 | border: 1px solid var(--primary-color); 111 | border-radius: 5px; 112 | overflow: hidden; 113 | transition: var(--transition); 114 | } 115 | 116 | .drawing-preview:hover { 117 | transform: translateY(-2px); 118 | box-shadow: var(--shadow); 119 | } 120 | 121 | .preview-time { 122 | padding: 0.5rem; 123 | background: var(--tool-bg-color); 124 | color: var(--text-color); 125 | font-size: 0.8rem; 126 | text-align: center; 127 | border-top: 1px solid var(--primary-color); 128 | } 129 | 130 | .drawing-preview img { 131 | width: 100%; 132 | display: block; 133 | } 134 | 135 | .canvas-container { 136 | flex: 1; 137 | background: var(--tool-bg-color); 138 | padding: 0.5rem; 139 | border-radius: 10px; 140 | box-shadow: var(--shadow); 141 | border: 1px solid var(--primary-color); 142 | overflow: hidden; 143 | position: relative; 144 | } 145 | 146 | #drawingCanvas { 147 | width: 100%; 148 | height: 100%; 149 | background: var(--tool-bg-color); 150 | cursor: crosshair; 151 | display: block; /* Remove any potential spacing */ 152 | } 153 | 154 | #brushSizeValue { 155 | min-width: 45px; 156 | text-align: center; 157 | color: var(--secondary-color); 158 | } 159 | 160 | @media (max-width: 768px) { 161 | .tools { 162 | flex-direction: column; 163 | gap: 1rem; 164 | } 165 | 166 | .tool-group { 167 | width: 100%; 168 | justify-content: center; 169 | } 170 | } 171 | 172 | /* Add styles for notifications */ 173 | .notification { 174 | position: fixed; 175 | top: 20px; 176 | right: 20px; 177 | padding: 15px 20px; 178 | background: var(--tool-bg-color); 179 | color: var(--text-color); 180 | border-left: 4px solid var(--primary-color); 181 | border-radius: 4px; 182 | box-shadow: var(--shadow); 183 | z-index: 1000; 184 | } 185 | 186 | .notification.success { 187 | border-color: #43b581; 188 | } 189 | 190 | .notification.error { 191 | border-color: #f04747; 192 | } 193 | 194 | /* Enhanced button styles */ 195 | .btn { 196 | padding: 0.8rem 1.5rem; 197 | border: none; 198 | border-radius: 8px; 199 | background: var(--tool-bg-color); 200 | color: var(--text-color); 201 | font-size: 1rem; 202 | cursor: pointer; 203 | transition: var(--transition); 204 | display: flex; 205 | align-items: center; 206 | gap: 0.5rem; 207 | border: 1px solid var(--primary-color); 208 | } 209 | 210 | .btn:hover { 211 | transform: translateY(-2px); 212 | box-shadow: var(--shadow); 213 | background: var(--button-hover); 214 | } 215 | 216 | .btn:active { 217 | transform: translateY(0); 218 | } 219 | 220 | .btn.primary { 221 | background: var(--primary-color); 222 | } 223 | 224 | .btn.primary:hover { 225 | background: var(--button-hover); 226 | } 227 | 228 | .btn.ai { 229 | background: var(--ai-color); 230 | color: white; 231 | } 232 | 233 | .btn.ai:hover { 234 | background: var(--ai-hover); 235 | } 236 | 237 | .btn.theme { 238 | background: var(--theme-color); 239 | color: var(--tool-bg-color); 240 | } 241 | 242 | .btn.theme:hover { 243 | background: var(--theme-hover); 244 | } 245 | 246 | /* Tool inputs styling */ 247 | input[type="color"] { 248 | width: 50px; 249 | height: 50px; 250 | border: none; 251 | border-radius: 8px; 252 | cursor: pointer; 253 | transition: var(--transition); 254 | padding: 0; 255 | } 256 | 257 | input[type="range"] { 258 | width: 150px; 259 | accent-color: var(--primary-color); 260 | } 261 | 262 | .button-group { 263 | display: flex; 264 | gap: 0.5rem; 265 | margin-left: auto; /* Push buttons to the right */ 266 | } 267 | 268 | /* AI result box styles */ 269 | .ai-result-box { 270 | position: fixed; 271 | bottom: 0; 272 | left: 0; 273 | right: 0; 274 | background: var(--tool-bg-color); 275 | border-top: 2px solid var(--ai-color); 276 | padding: 1rem; 277 | transition: transform 0.3s ease; 278 | z-index: 1000; 279 | max-height: 30vh; 280 | overflow-y: auto; 281 | } 282 | 283 | .ai-result-box.hidden { 284 | transform: translateY(100%); 285 | } 286 | 287 | .ai-result-header { 288 | display: flex; 289 | justify-content: space-between; 290 | align-items: center; 291 | margin-bottom: 1rem; 292 | } 293 | 294 | .ai-result-header h3 { 295 | margin: 0; 296 | color: var(--ai-color); 297 | } 298 | 299 | .btn-close { 300 | background: none; 301 | border: none; 302 | color: var(--text-color); 303 | cursor: pointer; 304 | padding: 5px; 305 | font-size: 1.2rem; 306 | } 307 | 308 | .ai-result-content { 309 | font-family: monospace; 310 | white-space: pre-wrap; 311 | padding: 1rem; 312 | background: rgba(0,0,0,0.2); 313 | border-radius: 5px; 314 | border: 1px solid var(--ai-color); 315 | } 316 | 317 | /* Add styles for index page */ 318 | .main-title { 319 | color: var(--primary-color); 320 | text-align: center; 321 | margin: 2rem 0; 322 | } 323 | 324 | .subtitle { 325 | text-align: center; 326 | color: var(--secondary-color); 327 | margin-bottom: 2rem; 328 | } 329 | 330 | /* Graphing Calculator Styles */ 331 | .graph-controls { 332 | background: var(--tool-bg-color); 333 | padding: 1rem; 334 | border-radius: 10px; 335 | margin-bottom: 1rem; 336 | border: 1px solid var(--primary-color); 337 | } 338 | 339 | .function-buttons { 340 | display: flex; 341 | gap: 0.5rem; 342 | margin-top: 0.5rem; 343 | } 344 | 345 | .function-buttons button { 346 | padding: 0.5rem 1rem; 347 | border: 1px solid var(--primary-color); 348 | border-radius: 5px; 349 | background: var(--tool-bg-color); 350 | color: var(--text-color); 351 | cursor: pointer; 352 | transition: var(--transition); 353 | } 354 | 355 | .function-buttons button:hover { 356 | background: var(--primary-color); 357 | } 358 | 359 | .range-controls { 360 | display: flex; 361 | gap: 1rem; 362 | margin: 1rem 0; 363 | } 364 | 365 | .range-group { 366 | display: flex; 367 | align-items: center; 368 | gap: 0.5rem; 369 | } 370 | 371 | .range-group input[type="number"] { 372 | width: 80px; 373 | padding: 0.3rem; 374 | border: 1px solid var(--primary-color); 375 | border-radius: 5px; 376 | background: var(--background-color); 377 | color: var(--text-color); 378 | } 379 | 380 | .graph-container { 381 | background: var(--tool-bg-color); 382 | padding: 1rem; 383 | border-radius: 10px; 384 | border: 1px solid var(--primary-color); 385 | height: calc(100vh - 250px); 386 | } 387 | 388 | .graph-tabs { 389 | display: flex; 390 | gap: 1rem; 391 | margin-bottom: 1rem; 392 | } 393 | 394 | .graph-tab { 395 | padding: 0.5rem 1rem; 396 | border: 1px solid var(--primary-color); 397 | border-radius: 5px; 398 | cursor: pointer; 399 | transition: var(--transition); 400 | } 401 | 402 | .graph-tab.active { 403 | background: var(--primary-color); 404 | color: var(--text-color); 405 | } 406 | 407 | /* Improved Sidebar Styles */ 408 | .sidebar { 409 | width: 280px; 410 | background: var(--tool-bg-color); 411 | border-right: 1px solid var(--primary-color); 412 | display: flex; 413 | flex-direction: column; 414 | height: 100vh; 415 | position: fixed; 416 | left: 0; 417 | top: 0; 418 | } 419 | 420 | .sidebar-header { 421 | padding: 1.5rem; 422 | border-bottom: 1px solid var(--primary-color); 423 | } 424 | 425 | .sidebar-header h2 { 426 | margin: 0; 427 | display: flex; 428 | align-items: center; 429 | gap: 0.5rem; 430 | font-size: 1.5rem; 431 | color: var(--primary-color); 432 | } 433 | 434 | .nav-buttons { 435 | padding: 1rem; 436 | display: flex; 437 | flex-direction: column; 438 | gap: 0.5rem; 439 | flex: 1; 440 | } 441 | 442 | .nav-btn { 443 | display: flex; 444 | flex-direction: column; 445 | padding: 1rem; 446 | border-radius: 8px; 447 | color: var(--text-color); 448 | text-decoration: none; 449 | transition: var(--transition); 450 | border: 1px solid transparent; 451 | } 452 | 453 | .nav-btn i { 454 | font-size: 1.5rem; 455 | margin-bottom: 0.5rem; 456 | } 457 | 458 | .nav-text { 459 | font-size: 1rem; 460 | font-weight: 500; 461 | } 462 | 463 | .nav-description { 464 | font-size: 0.8rem; 465 | color: var(--secondary-color); 466 | margin-top: 0.25rem; 467 | } 468 | 469 | .nav-btn:hover { 470 | background: rgba(114, 137, 218, 0.1); 471 | border-color: var(--primary-color); 472 | transform: translateX(5px); 473 | } 474 | 475 | .nav-btn.active { 476 | background: var(--primary-color); 477 | border-color: var(--primary-color); 478 | } 479 | 480 | .nav-btn.active .nav-description { 481 | color: rgba(255, 255, 255, 0.8); 482 | } 483 | 484 | .sidebar-footer { 485 | padding: 1rem; 486 | border-top: 1px solid var(--primary-color); 487 | } 488 | 489 | .theme-btn { 490 | width: 100%; 491 | padding: 0.8rem; 492 | border-radius: 8px; 493 | background: var(--theme-color); 494 | color: var(--tool-bg-color); 495 | border: none; 496 | cursor: pointer; 497 | display: flex; 498 | align-items: center; 499 | justify-content: center; 500 | gap: 0.5rem; 501 | transition: var(--transition); 502 | } 503 | 504 | .theme-btn:hover { 505 | background: var(--theme-hover); 506 | transform: translateY(-2px); 507 | } 508 | 509 | /* Responsive adjustments */ 510 | @media (max-width: 768px) { 511 | .sidebar { 512 | width: 60px; 513 | } 514 | 515 | .sidebar-header h2, .nav-text, .nav-description { 516 | display: none; 517 | } 518 | 519 | .nav-btn { 520 | padding: 0.8rem; 521 | align-items: center; 522 | } 523 | 524 | .nav-btn i { 525 | margin: 0; 526 | } 527 | 528 | .main-content { 529 | margin-left: 60px; 530 | width: calc(100% - 60px); 531 | } 532 | 533 | .theme-btn span { 534 | display: none; 535 | } 536 | } 537 | 538 | /* Add hover tooltip for mobile */ 539 | @media (max-width: 768px) { 540 | .nav-btn { 541 | position: relative; 542 | } 543 | 544 | .nav-btn:hover::after { 545 | content: attr(data-title); 546 | position: absolute; 547 | left: 100%; 548 | top: 50%; 549 | transform: translateY(-50%); 550 | background: var(--tool-bg-color); 551 | padding: 0.5rem; 552 | border-radius: 4px; 553 | border: 1px solid var(--primary-color); 554 | white-space: nowrap; 555 | z-index: 1000; 556 | } 557 | } 558 | 559 | /* Calculator Specific Styles */ 560 | .calculator-display { 561 | background: var(--tool-bg-color); 562 | padding: 1rem; 563 | border-radius: 10px; 564 | margin-bottom: 1rem; 565 | border: 1px solid var(--primary-color); 566 | } 567 | 568 | .calc-display { 569 | width: 100%; 570 | padding: 1rem; 571 | font-size: 2rem; 572 | background: var(--background-color); 573 | color: var(--text-color); 574 | border: 1px solid var(--primary-color); 575 | border-radius: 5px; 576 | text-align: right; 577 | } 578 | 579 | .calculator-pad { 580 | background: var(--tool-bg-color); 581 | padding: 1rem; 582 | border-radius: 10px; 583 | border: 1px solid var(--primary-color); 584 | } 585 | 586 | .number-pad { 587 | display: grid; 588 | grid-template-columns: repeat(4, 1fr); 589 | gap: 0.5rem; 590 | margin-top: 1rem; 591 | } 592 | 593 | .number-pad .btn { 594 | padding: 1.5rem; 595 | font-size: 1.25rem; 596 | justify-content: center; 597 | } 598 | 599 | .btn.equals { 600 | grid-column: 1 / -1; 601 | margin-top: 0.5rem; 602 | } 603 | 604 | .btn.danger { 605 | background: #f04747; 606 | border-color: #f04747; 607 | } 608 | 609 | .btn.danger:hover { 610 | background: #d04545; 611 | } 612 | 613 | /* Matrix Calculator Styles */ 614 | .matrix-container { 615 | display: flex; 616 | flex-direction: column; 617 | gap: 2rem; 618 | padding: 1rem; 619 | } 620 | 621 | .matrix-input-group { 622 | background: var(--tool-bg-color); 623 | padding: 1.5rem; 624 | border-radius: 10px; 625 | border: 1px solid var(--primary-color); 626 | box-shadow: var(--shadow); 627 | transition: var(--transition); 628 | } 629 | 630 | .matrix-input-group:hover { 631 | border-color: var(--primary-color); 632 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); 633 | } 634 | 635 | .matrix-input-group h3 { 636 | color: var(--primary-color); 637 | margin-bottom: 1rem; 638 | font-size: 1.5rem; 639 | display: flex; 640 | align-items: center; 641 | gap: 0.5rem; 642 | } 643 | 644 | .matrix-input-group h3::before { 645 | content: '⊞'; 646 | font-size: 1.2em; 647 | } 648 | 649 | .matrix-controls { 650 | display: flex; 651 | gap: 1rem; 652 | align-items: center; 653 | margin-bottom: 1.5rem; 654 | padding: 1rem; 655 | background: rgba(0, 0, 0, 0.2); 656 | border-radius: 8px; 657 | } 658 | 659 | .dimension-controls { 660 | display: flex; 661 | align-items: center; 662 | gap: 0.8rem; 663 | font-size: 1.2rem; 664 | } 665 | 666 | .dimension-input { 667 | width: 70px; 668 | padding: 0.8rem; 669 | border: 2px solid var(--primary-color); 670 | border-radius: 8px; 671 | background: var(--background-color); 672 | color: var(--text-color); 673 | font-size: 1.1rem; 674 | text-align: center; 675 | transition: var(--transition); 676 | } 677 | 678 | .dimension-input:focus { 679 | border-color: var(--theme-color); 680 | box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2); 681 | outline: none; 682 | } 683 | 684 | .matrix-grid { 685 | display: grid; 686 | gap: 0.8rem; 687 | padding: 1.5rem; 688 | background: rgba(0,0,0,0.2); 689 | border-radius: 8px; 690 | margin-top: 1rem; 691 | } 692 | 693 | .matrix-input { 694 | width: 100%; 695 | padding: 0.8rem; 696 | text-align: center; 697 | border: 2px solid var(--primary-color); 698 | border-radius: 8px; 699 | background: var(--background-color); 700 | color: var(--text-color); 701 | font-size: 1.1rem; 702 | transition: var(--transition); 703 | } 704 | 705 | .matrix-input:focus { 706 | border-color: var(--theme-color); 707 | box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2); 708 | outline: none; 709 | } 710 | 711 | .matrix-input:read-only { 712 | background: rgba(114, 137, 218, 0.1); 713 | border-color: var(--secondary-color); 714 | } 715 | 716 | .matrix-operations { 717 | display: flex; 718 | gap: 1rem; 719 | flex-wrap: wrap; 720 | padding: 1rem; 721 | background: var(--tool-bg-color); 722 | border-radius: 10px; 723 | border: 1px solid var(--primary-color); 724 | box-shadow: var(--shadow); 725 | } 726 | 727 | .matrix-operations .btn { 728 | min-width: 150px; 729 | justify-content: center; 730 | font-weight: 500; 731 | } 732 | 733 | .matrix-operations .btn i { 734 | margin-right: 0.5rem; 735 | } 736 | 737 | .matrix-result { 738 | background: var(--tool-bg-color); 739 | padding: 1.5rem; 740 | border-radius: 10px; 741 | border: 1px solid var(--primary-color); 742 | box-shadow: var(--shadow); 743 | } 744 | 745 | .matrix-result h3 { 746 | color: var(--theme-color); 747 | margin-bottom: 1rem; 748 | font-size: 1.5rem; 749 | display: flex; 750 | align-items: center; 751 | gap: 0.5rem; 752 | } 753 | 754 | .matrix-result h3::before { 755 | content: '='; 756 | font-size: 1.2em; 757 | } 758 | 759 | /* Matrix responsive styles */ 760 | @media (max-width: 768px) { 761 | .matrix-controls { 762 | flex-direction: column; 763 | align-items: stretch; 764 | } 765 | 766 | .dimension-controls { 767 | justify-content: center; 768 | } 769 | 770 | .matrix-operations { 771 | justify-content: center; 772 | } 773 | 774 | .matrix-operations .btn { 775 | min-width: calc(50% - 0.5rem); 776 | } 777 | } 778 | 779 | /* Unit Converter Styles */ 780 | .converter-container { 781 | display: flex; 782 | flex-direction: column; 783 | gap: 2rem; 784 | max-width: 800px; 785 | margin: 0 auto; 786 | } 787 | 788 | .converter-tabs { 789 | display: flex; 790 | gap: 1rem; 791 | margin-bottom: 1rem; 792 | } 793 | 794 | .converter-tab { 795 | padding: 1rem 2rem; 796 | border: 1px solid var(--primary-color); 797 | border-radius: 8px; 798 | background: var(--tool-bg-color); 799 | color: var(--text-color); 800 | cursor: pointer; 801 | transition: var(--transition); 802 | display: flex; 803 | align-items: center; 804 | gap: 0.5rem; 805 | font-size: 1.1rem; 806 | } 807 | 808 | .converter-tab:hover { 809 | background: rgba(114, 137, 218, 0.1); 810 | } 811 | 812 | .converter-tab.active { 813 | background: var(--primary-color); 814 | border-color: var(--primary-color); 815 | } 816 | 817 | .converter-input-group { 818 | display: flex; 819 | align-items: center; 820 | gap: 2rem; 821 | padding: 2rem; 822 | background: var(--tool-bg-color); 823 | border-radius: 10px; 824 | border: 1px solid var(--primary-color); 825 | } 826 | 827 | .input-section, .output-section { 828 | flex: 1; 829 | display: flex; 830 | align-items: center; 831 | gap: 1rem; 832 | } 833 | 834 | .converter-input { 835 | width: 100%; 836 | padding: 1rem; 837 | font-size: 1.2rem; 838 | background: var(--background-color); 839 | color: var(--text-color); 840 | border: 2px solid var(--primary-color); 841 | border-radius: 8px; 842 | transition: var(--transition); 843 | } 844 | 845 | .converter-input:focus { 846 | border-color: var(--theme-color); 847 | outline: none; 848 | box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2); 849 | } 850 | 851 | .unit-label { 852 | font-size: 1.2rem; 853 | color: var(--secondary-color); 854 | min-width: 80px; 855 | } 856 | 857 | .converter-arrow { 858 | font-size: 2rem; 859 | color: var (--primary-color); 860 | animation: pulse 2s infinite; 861 | } 862 | 863 | .convert-btn { 864 | align-self: center; 865 | min-width: 200px; 866 | justify-content: center; 867 | font-size: 1.2rem; 868 | } 869 | 870 | .conversion-history { 871 | background: var(--tool-bg-color); 872 | padding: 1.5rem; 873 | border-radius: 10px; 874 | border: 1px solid var(--primary-color); 875 | } 876 | 877 | .conversion-history h3 { 878 | color: var(--primary-color); 879 | margin-bottom: 1rem; 880 | display: flex; 881 | align-items: center; 882 | gap: 0.5rem; 883 | } 884 | 885 | .history-item { 886 | display: flex; 887 | align-items: center; 888 | gap: 1rem; 889 | padding: 0.8rem; 890 | border-bottom: 1px solid rgba(255, 255, 255, 0.1); 891 | animation: fadeIn 0.3s ease; 892 | } 893 | 894 | .history-item i { 895 | color: var(--primary-color); 896 | } 897 | 898 | @keyframes pulse { 899 | 0% { transform: scale(1); } 900 | 50% { transform: scale(1.1); } 901 | 100% { transform: scale(1); } 902 | } 903 | 904 | @keyframes fadeIn { 905 | from { opacity: 0; transform: translateY(-10px); } 906 | to { opacity: 1; transform: translateY(0); } 907 | } 908 | --------------------------------------------------------------------------------