Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
Son Of Anton
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Son Of Anton
Commits
10eb3289
Commit
10eb3289
authored
Mar 29, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kokowawa
parent
459309cd
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2538 additions
and
1899 deletions
+2538
-1899
FULL_CODEBASE.txt
FULL_CODEBASE.txt
+2501
-1880
files_routes.py
backend/routes/files_routes.py
+23
-9
api.js
frontend/src/api.js
+9
-3
ChatView.jsx
frontend/src/components/ChatView.jsx
+5
-7
No files found.
FULL_CODEBASE.txt
View file @
10eb3289
This source diff could not be displayed because it is too large. You can
view the blob
instead.
backend/routes/files_routes.py
View file @
10eb3289
...
...
@@ -3,7 +3,9 @@ Code extraction and file download helpers.
"""
import
io
import
re
import
zipfile
from
typing
import
Optional
from
pydantic
import
BaseModel
from
fastapi
import
APIRouter
...
...
@@ -16,6 +18,7 @@ router = APIRouter()
class
ExtractBody
(
BaseModel
):
markdown
:
str
title
:
Optional
[
str
]
=
None
@
router
.
post
(
"/extract"
)
...
...
@@ -30,20 +33,31 @@ def download_zip(body: ExtractBody):
if
not
blocks
:
return
{
"error"
:
"No code blocks found"
}
# Keep LAST occurrence of each filename (latest version wins)
file_map
:
dict
[
str
,
str
]
=
{}
for
b
in
blocks
:
file_map
[
b
[
"filename"
]]
=
b
[
"code"
]
if
not
file_map
:
return
{
"error"
:
"No code blocks found"
}
# Build a safe zip filename from chat title
raw_title
=
(
body
.
title
or
""
)
.
strip
()
if
not
raw_title
or
raw_title
==
"New Chat"
:
safe_title
=
"code"
else
:
safe_title
=
re
.
sub
(
r'[^\w\s-]'
,
''
,
raw_title
)
.
strip
()
safe_title
=
re
.
sub
(
r'[\s]+'
,
'-'
,
safe_title
)[:
60
]
or
"code"
zip_filename
=
f
"{safe_title}.zip"
buf
=
io
.
BytesIO
()
with
zipfile
.
ZipFile
(
buf
,
"w"
,
zipfile
.
ZIP_DEFLATED
)
as
zf
:
seen
=
set
()
for
b
in
blocks
:
name
=
b
[
"filename"
]
if
name
in
seen
:
base
,
ext
=
name
.
rsplit
(
"."
,
1
)
if
"."
in
name
else
(
name
,
"txt"
)
name
=
f
"{base}_{len(seen)}.{ext}"
seen
.
add
(
name
)
zf
.
writestr
(
name
,
b
[
"code"
])
for
name
,
code
in
file_map
.
items
():
zf
.
writestr
(
name
,
code
)
buf
.
seek
(
0
)
return
StreamingResponse
(
buf
,
media_type
=
"application/zip"
,
headers
=
{
"Content-Disposition"
:
"attachment; filename=son-of-anton-code.zip"
},
headers
=
{
"Content-Disposition"
:
f
'attachment; filename="{zip_filename}"'
},
)
\ No newline at end of file
frontend/src/api.js
View file @
10eb3289
...
...
@@ -193,11 +193,11 @@ export const adminListChats = (token) =>
// Code Download
// ═══════════════════════════════════════════════════
export
async
function
downloadZip
(
token
,
markdown
)
{
export
async
function
downloadZip
(
token
,
markdown
,
chatTitle
)
{
const
res
=
await
fetch
(
`
${
BASE
}
/files/download-zip`
,
{
method
:
"POST"
,
headers
:
headers
(
token
),
body
:
JSON
.
stringify
({
markdown
}),
body
:
JSON
.
stringify
({
markdown
,
title
:
chatTitle
||
null
}),
});
if
(
!
res
.
ok
)
throw
new
Error
(
"Download failed"
);
const
ct
=
res
.
headers
.
get
(
"content-type"
)
||
""
;
...
...
@@ -206,7 +206,13 @@ export async function downloadZip(token, markdown) {
const
url
=
URL
.
createObjectURL
(
blob
);
const
a
=
document
.
createElement
(
"a"
);
a
.
href
=
url
;
a
.
download
=
"son-of-anton-code.zip"
;
// Derive filename from chat title, fallback to generic
const
raw
=
(
chatTitle
||
""
).
trim
();
const
safeName
=
raw
&&
raw
!==
"New Chat"
?
raw
.
replace
(
/
[^\w\s
-
]
/g
,
""
).
trim
().
replace
(
/
\s
+/g
,
"-"
).
slice
(
0
,
60
)
||
"code"
:
"code"
;
a
.
download
=
`
${
safeName
}
.zip`
;
a
.
click
();
URL
.
revokeObjectURL
(
url
);
}
else
{
...
...
frontend/src/components/ChatView.jsx
View file @
10eb3289
...
...
@@ -333,18 +333,16 @@ export default function ChatView({ chatId }) {
<
div
className=
"flex items-end gap-1.5"
>
<
button
onClick=
{
toggleSettings
}
className=
{
`p-2.5 rounded-xl transition shrink-0 min-w-[40px] min-h-[40px] flex items-center justify-center ${
showSettings ? "bg-anton-accent/20 text-anton-accent" : "text-anton-muted hover:text-white hover:bg-anton-card active:bg-anton-card"
}`
}
className=
{
`p-2.5 rounded-xl transition shrink-0 min-w-[40px] min-h-[40px] flex items-center justify-center ${showSettings ? "bg-anton-accent/20 text-anton-accent" : "text-anton-muted hover:text-white hover:bg-anton-card active:bg-anton-card"
}`
}
>
<
Settings2
size=
{
18
}
/>
</
button
>
<
button
onClick=
{
()
=>
fileRef
.
current
?.
click
()
}
className=
{
`p-2.5 rounded-xl transition shrink-0 min-w-[40px] min-h-[40px] flex items-center justify-center ${
pendingFiles.length ? "bg-green-500/20 text-green-400" : "text-anton-muted hover:text-white hover:bg-anton-card active:bg-anton-card"
}`
}
className=
{
`p-2.5 rounded-xl transition shrink-0 min-w-[40px] min-h-[40px] flex items-center justify-center ${pendingFiles.length ? "bg-green-500/20 text-green-400" : "text-anton-muted hover:text-white hover:bg-anton-card active:bg-anton-card"
}`
}
title=
"Attach files"
>
<
Paperclip
size=
{
18
}
/>
...
...
@@ -407,7 +405,7 @@ export default function ChatView({ chatId }) {
<
button
onClick=
{
async
()
=>
{
const
all
=
messages
.
filter
((
m
)
=>
m
.
role
===
"assistant"
).
map
((
m
)
=>
m
.
content
).
join
(
"
\n\n
---
\n\n
"
);
if
(
all
)
try
{
await
downloadZip
(
state
.
token
,
all
);
}
catch
{
/* */
}
if
(
all
)
try
{
await
downloadZip
(
state
.
token
,
all
,
currentChat
?.
title
);
}
catch
{
/* */
}
}
}
className=
"ml-auto hover:text-anton-accent transition"
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a 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