5m4ck3r commited on
Commit
d7508be
·
verified ·
1 Parent(s): 637228d

Upload 11 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ COPY . .
10
+
11
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "-w", "20", "main:app"]
app.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ from flask import Flask, request, jsonify, render_template
3
+ from io import BytesIO
4
+ from maincode import *
5
+ import re
6
+
7
+ app = Flask(__name__)
8
+
9
+ def calculate(expression):
10
+ try:
11
+ result = []
12
+ for eq in expression:
13
+ result.append(eval(eq.replace("=", "")))
14
+ print(result)
15
+ return result
16
+ except Exception as e:
17
+ print(f"Error : {e}")
18
+ return []
19
+
20
+ def advanced_calculator(text):
21
+ pattern = r'(\d+(?:\s*[+\-*/]\s*\d+)*)\s?='
22
+ matches = re.findall(pattern, text)
23
+ filtered_expressions = []
24
+ for match in matches:
25
+ equation = match + '='
26
+ remaining_text = text[text.find(equation)+len(equation):].strip()
27
+ if remaining_text and remaining_text[0].isdigit():
28
+ continue
29
+ filtered_expressions.append(equation.replace(" ", "").strip())
30
+ return calculate(filtered_expressions)
31
+
32
+ @app.route('/')
33
+ def home():
34
+ return render_template('index.html')
35
+
36
+ @app.route('/whitebai', methods=['POST'])
37
+ def whitebai():
38
+ data = request.get_json()
39
+ image_data = data.get('image')
40
+ if not image_data:
41
+ return jsonify({'message': 'No image data received'}), 400
42
+ image_data = image_data.split(',')[1]
43
+ image_bytes = base64.b64decode(image_data)
44
+ image_file = BytesIO(image_bytes)
45
+ elt = ExtractTextFromImage(image_file)
46
+ elt.process_file()
47
+ dat = elt.get_text()
48
+ return jsonify({'message': advanced_calculator(dat)})
49
+
50
+ if __name__ == '__main__':
51
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ requests
2
+ flask
3
+ gunicorn
static/css/style.css ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ overflow: hidden;
5
+ }
6
+
7
+ .whiteboard-container {
8
+ position: fixed;
9
+ top: 0;
10
+ left: 0;
11
+ width: 100vw;
12
+ height: 100vh;
13
+ }
14
+
15
+ #whiteboard {
16
+ width: 100%;
17
+ height: 100%;
18
+ cursor: crosshair;
19
+ touch-action: none;
20
+ }
21
+
22
+ .brush-preview {
23
+ position: fixed;
24
+ pointer-events: none;
25
+ border: 1px solid #000;
26
+ border-radius: 50%;
27
+ display: none;
28
+ }
29
+
30
+ .bottom-toolbar {
31
+ position: fixed;
32
+ bottom: 20px;
33
+ left: 50%;
34
+ transform: translateX(-50%);
35
+ background: white;
36
+ border-radius: 8px;
37
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
38
+ z-index: 1000;
39
+ transition: transform 0.3s ease;
40
+ }
41
+
42
+ .bottom-toolbar.hidden .toolbar-content {
43
+ display: none;
44
+ }
45
+
46
+ .bottom-toolbar.hidden .toggle-btn .material-icons {
47
+ transform: rotate(180deg);
48
+ }
49
+
50
+ .toggle-btn {
51
+ position: absolute;
52
+ left: 50%;
53
+ transform: translateX(-50%) translateY(-100%);
54
+ border-radius: 8px 8px 0 0;
55
+ padding: 4px 16px;
56
+ background: white;
57
+ border: none;
58
+ cursor: pointer;
59
+ box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
60
+ }
61
+
62
+ .toggle-btn .material-icons {
63
+ transition: transform 0.3s ease;
64
+ }
65
+
66
+ .toolbar-content {
67
+ display: flex;
68
+ gap: 20px;
69
+ padding: 10px;
70
+ }
71
+
72
+ .tool-group {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 10px;
76
+ padding: 0 10px;
77
+ border-right: 1px solid #eee;
78
+ }
79
+
80
+ .tool-group:last-child {
81
+ border-right: none;
82
+ }
83
+
84
+ .tool-btn {
85
+ padding: 8px;
86
+ border: none;
87
+ border-radius: 4px;
88
+ cursor: pointer;
89
+ background: #f5f5f5;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ }
94
+
95
+ .tool-btn.active {
96
+ background: #e0e0e0;
97
+ color: #2196F3;
98
+ }
99
+
100
+ .tool-btn .material-icons {
101
+ font-size: 20px;
102
+ }
103
+
104
+ #colorPicker {
105
+ width: 40px;
106
+ height: 40px;
107
+ padding: 0;
108
+ border: none;
109
+ border-radius: 4px;
110
+ cursor: pointer;
111
+ }
112
+
113
+ #brushSize {
114
+ width: 100px;
115
+ }
116
+
117
+ #brushSizeLabel {
118
+ min-width: 40px;
119
+ text-align: center;
120
+ }
121
+
122
+ .page-controls {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 10px;
126
+ }
127
+
128
+ #pageInfo {
129
+ min-width: 80px;
130
+ text-align: center;
131
+ }
132
+
133
+ .action-buttons {
134
+ display: flex;
135
+ gap: 10px;
136
+ }
137
+
138
+ .action-btn {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 5px;
142
+ padding: 8px 16px;
143
+ border: none;
144
+ border-radius: 4px;
145
+ cursor: pointer;
146
+ font-size: 14px;
147
+ background: #f5f5f5;
148
+ color: #333;
149
+ }
150
+
151
+ .action-btn:hover {
152
+ background: #e0e0e0;
153
+ }
154
+
155
+ .ai-btn {
156
+ background: #2196F3;
157
+ color: white;
158
+ }
159
+
160
+ .ai-btn:hover {
161
+ background: #1976D2;
162
+ }
163
+
164
+ .submit-btn {
165
+ background: #4CAF50;
166
+ color: white;
167
+ }
168
+
169
+ .submit-btn:hover {
170
+ background: #388E3C;
171
+ }
static/js/history.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class History {
2
+ constructor(maxStates = 50) {
3
+ this.states = [];
4
+ this.currentIndex = -1;
5
+ this.maxStates = maxStates;
6
+ }
7
+
8
+ push(state) {
9
+ // Remove any states after current index
10
+ this.states = this.states.slice(0, this.currentIndex + 1);
11
+
12
+ // Add new state
13
+ this.states.push(state);
14
+
15
+ // Remove oldest state if exceeding maxStates
16
+ if (this.states.length > this.maxStates) {
17
+ this.states.shift();
18
+ }
19
+
20
+ this.currentIndex = this.states.length - 1;
21
+ }
22
+
23
+ undo() {
24
+ if (this.currentIndex > 0) {
25
+ this.currentIndex--;
26
+ return this.states[this.currentIndex];
27
+ }
28
+ return null;
29
+ }
30
+
31
+ redo() {
32
+ if (this.currentIndex < this.states.length - 1) {
33
+ this.currentIndex++;
34
+ return this.states[this.currentIndex];
35
+ }
36
+ return null;
37
+ }
38
+
39
+ canUndo() {
40
+ return this.currentIndex > 0;
41
+ }
42
+
43
+ canRedo() {
44
+ return this.currentIndex < this.states.length - 1;
45
+ }
46
+ }
static/js/main.js ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Whiteboard {
2
+ constructor() {
3
+ this.canvas = document.getElementById('whiteboard');
4
+ this.ctx = this.canvas.getContext('2d');
5
+ this.isDrawing = false;
6
+ this.setupCanvas();
7
+ this.setupEventListeners();
8
+ }
9
+
10
+ setupCanvas() {
11
+ const resize = () => {
12
+ this.canvas.width = window.innerWidth;
13
+ this.canvas.height = window.innerHeight;
14
+
15
+ this.ctx.strokeStyle = '#000';
16
+ this.ctx.lineWidth = 2;
17
+ this.ctx.lineCap = 'round';
18
+ this.ctx.lineJoin = 'round';
19
+ };
20
+
21
+ window.addEventListener('resize', resize);
22
+ resize();
23
+ }
24
+
25
+ setupEventListeners() {
26
+ this.canvas.addEventListener('mousedown', this.startDrawing.bind(this));
27
+ this.canvas.addEventListener('mousemove', this.draw.bind(this));
28
+ this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this));
29
+ this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this));
30
+
31
+ this.canvas.addEventListener('touchstart', (e) => {
32
+ e.preventDefault();
33
+ const touch = e.touches[0];
34
+ const mouseEvent = new MouseEvent('mousedown', {
35
+ clientX: touch.clientX,
36
+ clientY: touch.clientY
37
+ });
38
+ this.canvas.dispatchEvent(mouseEvent);
39
+ });
40
+
41
+ this.canvas.addEventListener('touchmove', (e) => {
42
+ e.preventDefault();
43
+ const touch = e.touches[0];
44
+ const mouseEvent = new MouseEvent('mousemove', {
45
+ clientX: touch.clientX,
46
+ clientY: touch.clientY
47
+ });
48
+ this.canvas.dispatchEvent(mouseEvent);
49
+ });
50
+
51
+ this.canvas.addEventListener('touchend', (e) => {
52
+ e.preventDefault();
53
+ const mouseEvent = new MouseEvent('mouseup', {});
54
+ this.canvas.dispatchEvent(mouseEvent);
55
+ });
56
+
57
+ document.getElementById('processBtn').addEventListener('click', this.processDrawing.bind(this));
58
+ document.getElementById('clearBtn').addEventListener('click', this.clearCanvas.bind(this));
59
+ }
60
+
61
+ startDrawing(e) {
62
+ this.isDrawing = true;
63
+ const rect = this.canvas.getBoundingClientRect();
64
+ const x = e.clientX - rect.left;
65
+ const y = e.clientY - rect.top;
66
+ this.ctx.beginPath();
67
+ this.ctx.moveTo(x, y);
68
+ }
69
+
70
+ draw(e) {
71
+ if (!this.isDrawing) return;
72
+ const rect = this.canvas.getBoundingClientRect();
73
+ const x = e.clientX - rect.left;
74
+ const y = e.clientY - rect.top;
75
+ this.ctx.lineTo(x, y);
76
+ this.ctx.stroke();
77
+ }
78
+
79
+ stopDrawing() {
80
+ this.isDrawing = false;
81
+ }
82
+
83
+ clearCanvas() {
84
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
85
+ }
86
+
87
+ processDrawing() {
88
+ const imageData = this.canvas.toDataURL('image/png');
89
+ console.log('Processing drawing...');
90
+ console.log(imageData.substring(0, 100) + '...');
91
+ }
92
+ }
93
+
94
+ document.addEventListener('DOMContentLoaded', () => {
95
+ new Whiteboard();
96
+ });
static/js/pages.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Pages {
2
+ constructor() {
3
+ this.pages = [{}]; // Initially one empty page
4
+ this.currentPage = 0;
5
+ this.drawing = false; // Whether the user is currently drawing
6
+ this.lastX = 0;
7
+ this.lastY = 0;
8
+ this.ctx = null; // Canvas context
9
+ this.setupControls();
10
+ this.setupCanvas();
11
+ this.setupColorPicker();
12
+ this.setupBrushSize();
13
+ }
14
+
15
+ getAllPages() {
16
+ return this.pages; // Simply return all pages in the array
17
+ }
18
+
19
+ setPageContent(states) {
20
+ const currentPageData = this.pages[this.currentPage];
21
+ if (states.length > 0) {
22
+ currentPageData.drawing = states[states.length - 1].imageData; // Store only the latest state
23
+ }
24
+ }
25
+
26
+ setupControls() {
27
+ const prevBtn = document.getElementById('prevBtn');
28
+ const nextBtn = document.getElementById('nextBtn');
29
+ const pageInfo = document.getElementById('pageInfo');
30
+
31
+ prevBtn.addEventListener('click', () => this.previousPage());
32
+ nextBtn.addEventListener('click', () => this.nextPage());
33
+
34
+ this.updatePageInfo();
35
+ }
36
+
37
+ setupCanvas() {
38
+ const canvas = document.getElementById('whiteboard');
39
+ this.ctx = canvas.getContext('2d');
40
+ canvas.width = window.innerWidth - 40;
41
+ canvas.height = window.innerHeight - 100;
42
+ this.clearCanvas();
43
+
44
+ // Event listeners for drawing
45
+ canvas.addEventListener('mousedown', (e) => this.startDrawing(e));
46
+ canvas.addEventListener('mousemove', (e) => this.draw(e));
47
+ canvas.addEventListener('mouseup', () => this.stopDrawing());
48
+ canvas.addEventListener('mouseout', () => this.stopDrawing());
49
+ }
50
+
51
+ getPageContent() {
52
+ return this.pages[this.currentPage].drawing; // Return the latest drawing state (imageData)
53
+ }
54
+
55
+ startDrawing(e) {
56
+ this.drawing = true;
57
+ const mousePos = getMousePos(this.ctx.canvas, e);
58
+ this.lastX = mousePos.x;
59
+ this.lastY = mousePos.y;
60
+ }
61
+
62
+ draw(e) {
63
+ if (!this.drawing) return;
64
+ const mousePos = getMousePos(this.ctx.canvas, e);
65
+ this.ctx.beginPath();
66
+ this.ctx.moveTo(this.lastX, this.lastY);
67
+ this.ctx.lineTo(mousePos.x, mousePos.y);
68
+ this.ctx.strokeStyle = this.pages[this.currentPage].color || '#000000'; // Default black if no color set
69
+ this.ctx.lineWidth = this.pages[this.currentPage].brushSize || 2; // Default brush size
70
+ this.ctx.stroke();
71
+ this.lastX = mousePos.x;
72
+ this.lastY = mousePos.y;
73
+ }
74
+
75
+ stopDrawing() {
76
+ this.drawing = false;
77
+ }
78
+
79
+ setupColorPicker() {
80
+ const colorPicker = document.getElementById('colorPicker');
81
+ colorPicker.addEventListener('input', (event) => {
82
+ this.pages[this.currentPage].color = event.target.value; // Store color for current page
83
+ });
84
+ }
85
+
86
+ setupBrushSize() {
87
+ const brushSizeInput = document.getElementById('brushSize');
88
+ brushSizeInput.addEventListener('input', (event) => {
89
+ this.pages[this.currentPage].brushSize = event.target.value; // Store brush size for current page
90
+ });
91
+ }
92
+
93
+ getCurrentPage() {
94
+ return this.currentPage;
95
+ }
96
+
97
+ getPageCount() {
98
+ return this.pages.length;
99
+ }
100
+
101
+ addPage() {
102
+ this.pages.push({}); // Add a new empty page
103
+ this.currentPage = this.pages.length - 1;
104
+ this.clearCanvas(); // Clear the canvas for the new page
105
+ this.updatePageInfo();
106
+ }
107
+
108
+ previousPage() {
109
+ if (this.currentPage > 0) {
110
+ this.pages[this.currentPage].drawing = this.ctx.getImageData(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); // Save current page's content
111
+
112
+ this.currentPage--;
113
+ this.loadPageData(); // Load the content of the previous page
114
+ this.updatePageInfo();
115
+ }
116
+ }
117
+
118
+ nextPage() {
119
+ if (this.pages[this.currentPage]) {
120
+ // Save content of current page to its storage
121
+ this.pages[this.currentPage].drawing = this.ctx.getImageData(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
122
+ }
123
+
124
+ // Add new page and clear canvas
125
+ if (this.pages[this.currentPage].drawing !== null) {
126
+ // Add new page only if current page is not empty
127
+ this.pages.push({ drawing: null });
128
+ this.currentPage++;
129
+ }
130
+
131
+ this.clearCanvas(); // Clear the canvas for the new page
132
+ this.loadPageData(); // Load the newly added page's data
133
+ this.updatePageInfo();
134
+ }
135
+
136
+ updatePageInfo() {
137
+ const pageInfo = document.getElementById('pageInfo');
138
+ pageInfo.textContent = `Page ${this.currentPage + 1}`;
139
+ }
140
+
141
+ clearCanvas() {
142
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
143
+ }
144
+
145
+ loadPageData() {
146
+ const pageData = this.pages[this.currentPage];
147
+
148
+ // If there is saved content for this page, restore it
149
+ if (pageData.drawing) {
150
+ this.ctx.putImageData(pageData.drawing, 0, 0);
151
+ } else {
152
+ this.clearCanvas(); // If no content, clear canvas (empty page)
153
+ }
154
+
155
+ // You can add any additional settings here (color picker, brush size, etc.)
156
+ if (pageData.color) {
157
+ document.getElementById('colorPicker').value = pageData.color;
158
+ }
159
+ if (pageData.brushSize) {
160
+ document.getElementById('brushSize').value = pageData.brushSize;
161
+ }
162
+ }
163
+ }
164
+
165
+ function getMousePos(canvas, evt) {
166
+ const rect = canvas.getBoundingClientRect();
167
+ return {
168
+ x: evt.clientX - rect.left,
169
+ y: evt.clientY - rect.top,
170
+ };
171
+
172
+ }
static/js/script.js ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Whiteboard {
2
+ constructor() {
3
+ this.canvas = document.getElementById('whiteboard');
4
+ this.ctx = this.canvas.getContext('2d');
5
+ this.isDrawing = false;
6
+
7
+ this.history = new History();
8
+ this.tools = new DrawingTools(this.canvas, this.ctx);
9
+ this.pages = new Pages();
10
+
11
+ this.setupCanvas();
12
+ this.setupEventListeners();
13
+ this.setupBrushPreview();
14
+ this.setupToolbarToggle();
15
+ }
16
+
17
+ setupCanvas() {
18
+ const resize = () => {
19
+ this.canvas.width = window.innerWidth;
20
+ this.canvas.height = window.innerHeight;
21
+ this.setupDrawingStyle();
22
+
23
+ // Restore the current page
24
+ const pageContent = this.pages.getPageContent();
25
+ if (pageContent) {
26
+ this.ctx.putImageData(pageContent, 0, 0); // Use the drawing content from the current page
27
+ }
28
+ };
29
+
30
+ window.addEventListener('resize', resize);
31
+ resize();
32
+ }
33
+
34
+ setupToolbarToggle() {
35
+ const toolbar = document.querySelector('.bottom-toolbar');
36
+ const toggleBtn = document.getElementById('toggleToolbar');
37
+
38
+ toggleBtn.addEventListener('click', () => {
39
+ toolbar.classList.toggle('hidden');
40
+ });
41
+ }
42
+
43
+ setupBrushPreview() {
44
+ this.brushPreview = document.getElementById('brushPreview');
45
+
46
+ this.canvas.addEventListener('mousemove', (e) => {
47
+ const { x, y } = getMousePos(this.canvas, e);
48
+ this.updateBrushPreview(x, y);
49
+ });
50
+
51
+ this.canvas.addEventListener('mouseenter', () => {
52
+ this.brushPreview.style.display = 'block';
53
+ });
54
+
55
+ this.canvas.addEventListener('mouseleave', () => {
56
+ this.brushPreview.style.display = 'none';
57
+ });
58
+ }
59
+
60
+ updateBrushPreview(x, y) {
61
+ const size = this.tools.brushSize;
62
+ this.brushPreview.style.width = size + 'px';
63
+ this.brushPreview.style.height = size + 'px';
64
+ this.brushPreview.style.left = (x - size/2) + 'px';
65
+ this.brushPreview.style.top = (y - size/2) + 'px';
66
+ this.brushPreview.style.borderColor = this.tools.isEraser ? '#000' : this.tools.color;
67
+ }
68
+
69
+ setupDrawingStyle() {
70
+ this.ctx.lineCap = 'round';
71
+ this.ctx.lineJoin = 'round';
72
+ this.ctx.font = '24px Arial';
73
+ this.tools.applyToolSettings();
74
+ }
75
+
76
+ setupEventListeners() {
77
+ // Mouse events
78
+ this.canvas.addEventListener('mousedown', this.startDrawing.bind(this));
79
+ this.canvas.addEventListener('mousemove', this.draw.bind(this));
80
+ this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this));
81
+ this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this));
82
+
83
+ // Touch events
84
+ this.canvas.addEventListener('touchstart', this.handleTouch.bind(this));
85
+ this.canvas.addEventListener('touchmove', this.handleTouch.bind(this));
86
+ this.canvas.addEventListener('touchend', this.handleTouchEnd.bind(this));
87
+
88
+ // Button events
89
+ document.getElementById('aiBtn').addEventListener('click', this.processDrawing.bind(this));
90
+ document.getElementById('submitBtn').addEventListener('click', this.generatePDF.bind(this));
91
+ document.getElementById('clearBtn').addEventListener('click', this.clearCanvas.bind(this));
92
+ document.getElementById('undoBtn').addEventListener('click', this.undo.bind(this));
93
+ document.getElementById('redoBtn').addEventListener('click', this.redo.bind(this));
94
+
95
+ // Keyboard shortcuts
96
+ document.addEventListener('keydown', (e) => {
97
+ if (e.ctrlKey || e.metaKey) {
98
+ if (e.key === 'z') {
99
+ e.preventDefault();
100
+ this.undo();
101
+ } else if (e.key === 'y') {
102
+ e.preventDefault();
103
+ this.redo();
104
+ }
105
+ }
106
+ });
107
+ }
108
+
109
+ handleTouch(e) {
110
+ e.preventDefault();
111
+ const touch = e.touches[0];
112
+ const eventType = e.type === 'touchstart' ? 'mousedown' : 'mousemove';
113
+ const mouseEvent = new MouseEvent(eventType, {
114
+ clientX: touch.clientX,
115
+ clientY: touch.clientY
116
+ });
117
+ this.canvas.dispatchEvent(mouseEvent);
118
+ }
119
+
120
+ handleTouchEnd(e) {
121
+ e.preventDefault();
122
+ const mouseEvent = new MouseEvent('mouseup', {});
123
+ this.canvas.dispatchEvent(mouseEvent);
124
+ }
125
+
126
+ startDrawing(e) {
127
+ this.isDrawing = true;
128
+ const { x, y } = getMousePos(this.canvas, e);
129
+ this.tools.applyToolSettings();
130
+ this.ctx.beginPath();
131
+ this.ctx.moveTo(x, y);
132
+ }
133
+
134
+ draw(e) {
135
+ if (!this.isDrawing) return;
136
+ const { x, y } = getMousePos(this.canvas, e);
137
+ this.ctx.lineTo(x, y);
138
+ this.ctx.stroke();
139
+ }
140
+
141
+ stopDrawing() {
142
+ if (this.isDrawing) {
143
+ this.isDrawing = false;
144
+ this.saveState();
145
+ }
146
+ }
147
+
148
+ saveState() {
149
+ const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
150
+ this.history.push(new DrawingState(imageData));
151
+ this.pages.setPageContent(this.history.states);
152
+ }
153
+
154
+ undo() {
155
+ const state = this.history.undo();
156
+ if (state) {
157
+ this.ctx.putImageData(state.imageData, 0, 0);
158
+ this.pages.setPageContent(this.history.states);
159
+ }
160
+ }
161
+
162
+ redo() {
163
+ const state = this.history.redo();
164
+ if (state) {
165
+ this.ctx.putImageData(state.imageData, 0, 0);
166
+ this.pages.setPageContent(this.history.states);
167
+ }
168
+ }
169
+
170
+ clearCanvas() {
171
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
172
+ this.saveState();
173
+ }
174
+
175
+ processDrawing() {
176
+ const tempCanvas = document.createElement('canvas');
177
+ const tempContext = tempCanvas.getContext('2d');
178
+ tempCanvas.width = this.canvas.width;
179
+ tempCanvas.height = this.canvas.height;
180
+ tempContext.fillStyle = 'white';
181
+ tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
182
+ tempContext.drawImage(this.canvas, 0, 0);
183
+ const imageData = tempCanvas.toDataURL('image/png');
184
+ console.log('Processing drawing with AI...');
185
+ console.log(imageData.substring(0, 100) + '...');
186
+ fetch('/whitebai', {
187
+ method: 'POST',
188
+ headers: {
189
+ 'Content-Type': 'application/json',
190
+ },
191
+ body: JSON.stringify({
192
+ image: imageData
193
+ })
194
+ })
195
+ .then(response => response.json())
196
+ .then(data => {
197
+ document.getElementById('result').innerText = data.message;
198
+ })
199
+ .catch(error => {
200
+ console.error('Error:', error);
201
+ });
202
+ }
203
+
204
+ async generatePDF() {
205
+ const pages = this.pages.getAllPages();
206
+
207
+ for (let i = 0; i < pages.length; i++) {
208
+ const pageContent = pages[i];
209
+ const tempCanvas = document.createElement('canvas');
210
+ tempCanvas.width = this.canvas.width;
211
+ tempCanvas.height = this.canvas.height;
212
+ const tempCtx = tempCanvas.getContext('2d');
213
+ tempCtx.fillStyle = 'white';
214
+ tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
215
+
216
+ if (pageContent && pageContent.drawing) {
217
+ const imageData = pageContent.drawing;
218
+
219
+ if (imageData) {
220
+ try {
221
+ tempCtx.putImageData(imageData, 0, 0);
222
+ console.log("Image applied to canvas for page " + i);
223
+ } catch (error) {
224
+ console.log("Error putting image data on page " + i, error);
225
+ }
226
+ } else {
227
+ console.log("No image data for page " + i);
228
+ }
229
+ }
230
+
231
+ const imgData = tempCanvas.toDataURL('image/png');
232
+ console.log(`Base64 data for page ${i}:`, imgData);
233
+ }
234
+ }
235
+
236
+ writeText(text, x = 20, y = 40) {
237
+ this.setupDrawingStyle();
238
+ this.ctx.fillStyle = this.tools.color;
239
+ this.ctx.fillText(text.toString(), x, y);
240
+ this.saveState();
241
+ }
242
+ }
243
+
244
+ const whiteboard = new Whiteboard();
245
+ const canvas = document.getElementById('whiteboard');
246
+
247
+ document.addEventListener('keydown', function(event) {
248
+ if (event.ctrlKey && event.key === 's') {
249
+ event.preventDefault();
250
+ const tempCanvas = document.createElement('canvas');
251
+ const tempContext = tempCanvas.getContext('2d');
252
+ tempCanvas.width = canvas.width;
253
+ tempCanvas.height = canvas.height;
254
+ tempContext.fillStyle = 'white';
255
+ tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
256
+ tempContext.drawImage(canvas, 0, 0);
257
+ const imageData = tempCanvas.toDataURL("image/png");
258
+ const link = document.createElement('a');
259
+ link.href = imageData;
260
+ link.download = 'canvas-image.png';
261
+ link.click();
262
+ console.log("Canvas saved as image with white background!");
263
+ }
264
+ });
static/js/tools.js ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class DrawingTools {
2
+ constructor(canvas, ctx) {
3
+ this.canvas = canvas;
4
+ this.ctx = ctx;
5
+ this.color = '#000000';
6
+ this.brushSize = 2;
7
+ this.isEraser = false;
8
+ this.setupTools();
9
+ }
10
+
11
+ setupTools() {
12
+ const colorPicker = document.getElementById('colorPicker');
13
+ const brushSize = document.getElementById('brushSize');
14
+ const brushSizeLabel = document.getElementById('brushSizeLabel');
15
+ const eraserBtn = document.getElementById('eraserBtn');
16
+
17
+ colorPicker.addEventListener('input', (e) => {
18
+ this.color = e.target.value;
19
+ this.isEraser = false;
20
+ eraserBtn.classList.remove('active');
21
+ });
22
+
23
+ brushSize.addEventListener('input', (e) => {
24
+ this.brushSize = e.target.value;
25
+ brushSizeLabel.textContent = `${this.brushSize}px`;
26
+ });
27
+
28
+ eraserBtn.addEventListener('click', () => {
29
+ this.isEraser = !this.isEraser;
30
+ eraserBtn.classList.toggle('active');
31
+ });
32
+ }
33
+
34
+ applyToolSettings() {
35
+ if (this.isEraser) {
36
+ this.ctx.globalCompositeOperation = 'destination-out';
37
+ this.ctx.strokeStyle = 'rgba(0,0,0,1)';
38
+ } else {
39
+ this.ctx.globalCompositeOperation = 'source-over';
40
+ this.ctx.strokeStyle = this.color;
41
+ }
42
+ this.ctx.lineWidth = this.brushSize;
43
+ }
44
+ }
static/js/utils.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class DrawingState {
2
+ constructor(imageData) {
3
+ this.imageData = imageData;
4
+ }
5
+ }
6
+
7
+ function getMousePos(canvas, evt) {
8
+ const rect = canvas.getBoundingClientRect();
9
+ return {
10
+ x: evt.clientX - rect.left,
11
+ y: evt.clientY - rect.top
12
+ };
13
+ }
14
+
15
+ function getTouchPos(canvas, evt) {
16
+ const rect = canvas.getBoundingClientRect();
17
+ return {
18
+ x: evt.touches[0].clientX - rect.left,
19
+ y: evt.touches[0].clientY - rect.top
20
+ };
21
+ }
templates/index.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Whiteboard</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
8
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
9
+ </head>
10
+ <body>
11
+ <div class="whiteboard-container">
12
+ <div style="display: flex; justify-content: center; align-items: center; height: 100vh;">
13
+ <canvas id="whiteboard"></canvas>
14
+ </div>
15
+ <div class="brush-preview" id="brushPreview"></div>
16
+ <div class="bottom-toolbar">
17
+ <button id="toggleToolbar" class="tool-btn toggle-btn" title="Toggle Toolbar">
18
+ <span class="material-icons">keyboard_arrow_up</span>
19
+ </button>
20
+ <div id="toolbarContent" class="toolbar-content">
21
+ <div class="tool-group">
22
+ <button id="undoBtn" class="tool-btn" title="Undo (Ctrl+Z)">
23
+ <span class="material-icons">undo</span>
24
+ </button>
25
+ <button id="redoBtn" class="tool-btn" title="Redo (Ctrl+Y)">
26
+ <span class="material-icons">redo</span>
27
+ </button>
28
+ </div>
29
+ <div class="tool-group">
30
+ <input type="color" id="colorPicker" value="#000000" title="Color">
31
+ <button id="eraserBtn" class="tool-btn" title="Eraser">
32
+ <span class="material-icons">auto_fix_high</span>
33
+ </button>
34
+ </div>
35
+ <div class="tool-group">
36
+ <input type="range" id="brushSize" min="1" max="50" value="2" title="Brush Size">
37
+ <span id="brushSizeLabel">2px</span>
38
+ </div>
39
+ <div class="page-controls">
40
+ <button id="prevBtn" class="tool-btn" title="Previous Page">
41
+ <span class="material-icons">navigate_before</span>
42
+ </button>
43
+ <span id="pageInfo">Page 1</span>
44
+ <button id="nextBtn" class="tool-btn" title="Next Page">
45
+ <span class="material-icons">navigate_next</span>
46
+ </button>
47
+ </div>
48
+ <div class="action-buttons">
49
+ <button id="clearBtn" class="tool-btn" title="Clear Page">
50
+ <span class="material-icons">delete_outline</span>
51
+ </button>
52
+ <button id="aiBtn" class="action-btn ai-btn">
53
+ <span class="material-icons">psychology</span>
54
+ AI
55
+ </button>
56
+ <button id="submitBtn" class="action-btn submit-btn">
57
+ <span class="material-icons">upload_file</span>
58
+ Submit
59
+ </button>
60
+ <p id="result">Testing</p>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ <script src="{{ url_for('static', filename='js/utils.js') }}"></script>
66
+ <script src="{{ url_for('static', filename='js/history.js') }}"></script>
67
+ <script src="{{ url_for('static', filename='js/tools.js') }}"></script>
68
+ <script src="{{ url_for('static', filename='js/pages.js') }}"></script>
69
+ <script src="{{ url_for('static', filename='js/script.js') }}"></script>
70
+ </body>
71
+ </html>