Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
math_tutor_dev
public_math_tutor
Commits
3d5974ad
Commit
3d5974ad
authored
Jan 19, 2026
by
Kantz
Browse files
api eingerichetet und Mathpix verbunde
parent
104161ac
Changes
5
Hide whitespace changes
Inline
Side-by-side
math-tutor/.gitignore
0 → 100644
View file @
3d5974ad
.venv/
.env
__pycache__/
drawings/
\ No newline at end of file
math-tutor/backend/app/main.py
View file @
3d5974ad
from
__future__
import
annotations
from
typing
import
List
,
Optional
import
base64
import
os
import
time
from
pathlib
import
Path
from
fastapi
import
FastAPI
,
HTTPException
from
fastapi.middleware.cors
import
CORSMiddleware
from
pydantic
import
BaseModel
,
Field
from
dotenv
import
load_dotenv
try
:
from
mpxpy.mathpix_client
import
MathpixClient
except
ImportError
:
# pragma: no cover - optional dependency
MathpixClient
=
None
app
=
FastAPI
(
title
=
"Math Tutor API"
,
version
=
"0.1.0"
)
load_dotenv
()
app
.
add_middleware
(
CORSMiddleware
,
allow_origins
=
[
"http://localhost:5173"
],
allow_credentials
=
True
,
allow_methods
=
[
"*"
],
allow_headers
=
[
"*"
],
)
MATHPIX_APP_ID
=
os
.
getenv
(
"MATHPIX_APP_ID"
)
MATHPIX_APP_KEY
=
os
.
getenv
(
"MATHPIX_APP_KEY"
)
mathpix_client
=
None
if
MathpixClient
and
MATHPIX_APP_ID
and
MATHPIX_APP_KEY
:
mathpix_client
=
MathpixClient
(
app_id
=
MATHPIX_APP_ID
,
app_key
=
MATHPIX_APP_KEY
)
class
ChatMessage
(
BaseModel
):
role
:
str
=
Field
(...,
pattern
=
"^(user|assistant)$"
)
...
...
@@ -54,8 +82,46 @@ def save_canvas(request: CanvasSaveRequest) -> CanvasSaveResponse:
if
not
request
.
data_url
.
startswith
(
"data:image"
):
raise
HTTPException
(
status_code
=
400
,
detail
=
"data_url must be an image"
)
header
,
encoded
=
request
.
data_url
.
split
(
","
,
1
)
if
"image/png"
not
in
header
:
raise
HTTPException
(
status_code
=
400
,
detail
=
"only image/png supported"
)
try
:
raw
=
base64
.
b64decode
(
encoded
)
except
base64
.
binascii
.
Error
as
exc
:
raise
HTTPException
(
status_code
=
400
,
detail
=
"invalid base64 payload"
)
from
exc
drawings_dir
=
Path
(
__file__
).
resolve
().
parent
/
"storage"
/
"drawings"
drawings_dir
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
timestamp
=
int
(
time
.
time
())
safe_hint
=
request
.
filename_hint
or
"drawing"
safe_hint
=
""
.
join
(
ch
for
ch
in
safe_hint
if
ch
.
isalnum
()
or
ch
in
(
"-"
,
"_"
))
if
not
safe_hint
:
safe_hint
=
"drawing"
filename
=
f
"
{
safe_hint
}
-
{
timestamp
}
.png"
file_path
=
drawings_dir
/
filename
with
file_path
.
open
(
"wb"
)
as
handle
:
handle
.
write
(
raw
)
latex
=
"
\\
frac{a}{b}"
if
mathpix_client
:
try
:
image
=
mathpix_client
.
image_new
(
str
(
file_path
))
mmd
=
image
.
mmd
()
conversion
=
mathpix_client
.
conversion_new
(
mmd
=
mmd
,
convert_to_md
=
True
,
)
conversion
.
wait_until_complete
()
latex
=
conversion
.
to_md_text
()
except
Exception
as
exc
:
raise
HTTPException
(
status_code
=
500
,
detail
=
"mathpix failed"
)
from
exc
return
CanvasSaveResponse
(
status
=
"ok"
,
latex
=
"
\\
frac{a}{b}"
,
saved_as
=
request
.
filename_hint
,
latex
=
latex
,
saved_as
=
str
(
file_path
)
,
)
math-tutor/backend/requirements.txt
0 → 100644
View file @
3d5974ad
fastapi
uvicorn
python-dotenv
mpxpy
pillow
\ No newline at end of file
math-tutor/frontend/src/components/Canvas/CanvasDrawer.tsx
View file @
3d5974ad
...
...
@@ -5,7 +5,7 @@ type CanvasDrawerProps = {
isVisible
:
boolean
;
onToggle
:
()
=>
void
;
onClear
?:
()
=>
void
;
onSave
?:
(
dataUrl
:
string
)
=>
void
;
onSave
?:
(
dataUrl
:
string
)
=>
void
|
Promise
<
void
>
;
};
export
default
function
CanvasDrawer
({
...
...
math-tutor/frontend/src/pages/App.tsx
View file @
3d5974ad
...
...
@@ -63,19 +63,44 @@ export default function App() {
setIsCanvasVisible
((
prev
)
=>
!
prev
);
};
const
handleCanvasSave
=
(
dataUrl
:
string
)
=>
{
setDraft
((
prev
)
=>
prev
?
`
${
prev
}
[drawing saved]`
:
"
[drawing saved]
"
);
const
handleCanvasSave
=
async
(
dataUrl
:
string
)
=>
{
setMessages
((
prev
)
=>
[
...
prev
,
{
id
:
`m-
${
Date
.
now
()}
-assistant`
,
role
:
"
assistant
"
,
text
:
"
Drawing saved (mock). Ready to send to Mathpix
.
"
,
text
:
"
Saving drawing and converting to LaTeX..
.
"
,
},
]);
void
dataUrl
;
try
{
const
response
=
await
fetch
(
"
http://localhost:8000/api/canvas/save
"
,
{
method
:
"
POST
"
,
headers
:
{
"
Content-Type
"
:
"
application/json
"
},
body
:
JSON
.
stringify
({
data_url
:
dataUrl
,
filename_hint
:
"
canvas
"
}),
});
if
(
!
response
.
ok
)
{
throw
new
Error
(
`Save failed:
${
response
.
status
}
`
);
}
const
payload
:
{
latex
?:
string
}
=
await
response
.
json
();
if
(
payload
.
latex
)
{
setDraft
((
prev
)
=>
prev
?
`
${
prev
}
${
payload
.
latex
}
`
:
payload
.
latex
);
}
}
catch
(
error
)
{
setMessages
((
prev
)
=>
[
...
prev
,
{
id
:
`m-
${
Date
.
now
()}
-assistant`
,
role
:
"
assistant
"
,
text
:
"
Canvas save failed. Check the API logs.
"
,
},
]);
void
error
;
}
};
const
handleCiteDoc
=
(
docId
:
string
)
=>
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment