├── 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 |
 }})
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 |
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 | 
6 | 
7 | 
8 | 
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 |
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 |
71 |
72 |
73 |
85 |
86 |
87 |
88 |
91 |
94 |
97 |
98 |
99 |
100 |
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 |
77 |
89 |
92 |
93 |
96 |
97 |
98 |
99 |
100 |
103 |
104 |
107 |
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 |
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 |
--------------------------------------------------------------------------------