Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -215,6 +215,9 @@ TEMPLATE = """
|
|
215 |
font-weight: 500;
|
216 |
font-size: 1rem;
|
217 |
color: #f1f5f9;
|
|
|
|
|
|
|
218 |
}
|
219 |
|
220 |
.dropdown {
|
@@ -367,21 +370,6 @@ TEMPLATE = """
|
|
367 |
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.4);
|
368 |
}
|
369 |
|
370 |
-
/* Loading Animation */
|
371 |
-
.loading {
|
372 |
-
display: inline-block;
|
373 |
-
width: 20px;
|
374 |
-
height: 20px;
|
375 |
-
border: 2px solid rgba(255, 255, 255, 0.3);
|
376 |
-
border-radius: 50%;
|
377 |
-
border-top-color: #667eea;
|
378 |
-
animation: spin 1s ease-in-out infinite;
|
379 |
-
}
|
380 |
-
|
381 |
-
@keyframes spin {
|
382 |
-
to { transform: rotate(360deg); }
|
383 |
-
}
|
384 |
-
|
385 |
/* Responsive Design */
|
386 |
@media (max-width: 768px) {
|
387 |
.container {
|
@@ -412,7 +400,7 @@ TEMPLATE = """
|
|
412 |
}
|
413 |
|
414 |
::-webkit-scrollbar-thumb {
|
415 |
-
background: rgba(102, 126, 234, 0.5);
|
416 |
border-radius: 4px;
|
417 |
}
|
418 |
|
@@ -449,11 +437,9 @@ TEMPLATE = """
|
|
449 |
<form action="/upload" method="post" enctype="multipart/form-data" class="upload-container">
|
450 |
<input type="file" name="file" required class="file-input" id="fileInput">
|
451 |
<input type="hidden" name="path" value="{{ path }}">
|
452 |
-
<label for="fileInput" class="btn">
|
453 |
-
π€ Upload File
|
454 |
-
</label>
|
455 |
</form>
|
456 |
-
<button class="btn btn-secondary" onclick="
|
457 |
π New Folder
|
458 |
</button>
|
459 |
</div>
|
@@ -493,6 +479,18 @@ TEMPLATE = """
|
|
493 |
</div>
|
494 |
</div>
|
495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
496 |
<!-- Rename Modal -->
|
497 |
<div class="modal-overlay" id="renameModal">
|
498 |
<div class="modal">
|
@@ -520,6 +518,7 @@ TEMPLATE = """
|
|
520 |
<script>
|
521 |
let currentRenamePath = '';
|
522 |
let currentDeletePath = '';
|
|
|
523 |
let activeDropdown = null;
|
524 |
|
525 |
function nav(p) {
|
@@ -532,16 +531,20 @@ TEMPLATE = """
|
|
532 |
|
533 |
function toggleDropdown(button) {
|
534 |
const dropdown = button.closest('.dropdown');
|
535 |
-
|
536 |
-
// Close other dropdowns
|
537 |
if (activeDropdown && activeDropdown !== dropdown) {
|
538 |
activeDropdown.classList.remove('active');
|
539 |
}
|
540 |
-
|
541 |
dropdown.classList.toggle('active');
|
542 |
activeDropdown = dropdown.classList.contains('active') ? dropdown : null;
|
543 |
}
|
544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
545 |
function showRenameModal(path, currentName) {
|
546 |
currentRenamePath = path;
|
547 |
document.getElementById('renameInput').value = currentName;
|
@@ -571,81 +574,74 @@ TEMPLATE = """
|
|
571 |
activeDropdown = null;
|
572 |
}
|
573 |
|
574 |
-
async function
|
575 |
-
const
|
576 |
-
if (!
|
577 |
-
|
578 |
try {
|
579 |
-
const response = await fetch('/
|
580 |
method: 'POST',
|
581 |
headers: { 'Content-Type': 'application/json' },
|
582 |
-
body: JSON.stringify({
|
583 |
-
old_path: currentRenamePath,
|
584 |
-
new_path: newName
|
585 |
-
})
|
586 |
});
|
587 |
-
|
588 |
if (response.ok) {
|
589 |
-
closeModal('
|
590 |
location.reload();
|
591 |
}
|
592 |
} catch (error) {
|
593 |
-
console.error('
|
594 |
}
|
595 |
}
|
596 |
|
597 |
-
async function
|
|
|
|
|
|
|
|
|
|
|
598 |
try {
|
599 |
-
const response = await fetch('/
|
600 |
method: 'POST',
|
601 |
headers: { 'Content-Type': 'application/json' },
|
602 |
-
body: JSON.stringify({
|
603 |
});
|
604 |
-
|
605 |
if (response.ok) {
|
606 |
-
closeModal('
|
607 |
location.reload();
|
608 |
}
|
609 |
} catch (error) {
|
610 |
-
console.error('
|
611 |
}
|
612 |
}
|
613 |
|
614 |
-
async function
|
615 |
-
const name = prompt('Enter folder name:');
|
616 |
-
if (!name) return;
|
617 |
-
|
618 |
try {
|
619 |
-
const
|
620 |
-
const response = await fetch('/create_folder', {
|
621 |
method: 'POST',
|
622 |
headers: { 'Content-Type': 'application/json' },
|
623 |
-
body: JSON.stringify({ path:
|
624 |
});
|
625 |
-
|
626 |
if (response.ok) {
|
|
|
627 |
location.reload();
|
628 |
}
|
629 |
} catch (error) {
|
630 |
-
console.error('
|
631 |
}
|
632 |
}
|
633 |
|
634 |
-
// Close dropdowns when clicking outside
|
635 |
document.addEventListener('click', function(e) {
|
636 |
if (!e.target.closest('.dropdown')) {
|
637 |
closeAllDropdowns();
|
638 |
}
|
639 |
});
|
640 |
|
641 |
-
// Handle file input change for better UX
|
642 |
document.getElementById('fileInput').addEventListener('change', function(e) {
|
643 |
if (e.target.files.length > 0) {
|
644 |
e.target.closest('form').submit();
|
645 |
}
|
646 |
});
|
647 |
|
648 |
-
// Handle modal close on overlay click
|
649 |
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
650 |
overlay.addEventListener('click', function(e) {
|
651 |
if (e.target === overlay) {
|
@@ -654,7 +650,12 @@ TEMPLATE = """
|
|
654 |
});
|
655 |
});
|
656 |
|
657 |
-
|
|
|
|
|
|
|
|
|
|
|
658 |
document.getElementById('renameInput').addEventListener('keypress', function(e) {
|
659 |
if (e.key === 'Enter') {
|
660 |
confirmRename();
|
@@ -681,7 +682,6 @@ def list_folder(path=""):
|
|
681 |
items.append({"type":"dir","name":dir_name,"path":dir_path})
|
682 |
else:
|
683 |
items.append({"type":"file","name":rest,"path":(prefix + rest).strip("/")})
|
684 |
-
# sort dirs then files
|
685 |
items.sort(key=lambda x: (x["type"]!="dir", x["name"].lower()))
|
686 |
return items
|
687 |
|
@@ -693,7 +693,6 @@ def index():
|
|
693 |
@app.route("/download", methods=["GET"])
|
694 |
def download():
|
695 |
p = request.args.get("path","")
|
696 |
-
# download via hf_hub_download into /tmp
|
697 |
local = hf_hub_download(repo_id=REPO_ID, filename=p, repo_type="dataset", token=HF_TOKEN, cache_dir=tempfile.gettempdir())
|
698 |
return send_file(local, as_attachment=True, download_name=os.path.basename(p))
|
699 |
|
|
|
215 |
font-weight: 500;
|
216 |
font-size: 1rem;
|
217 |
color: #f1f5f9;
|
218 |
+
white-space: nowrap;
|
219 |
+
overflow: hidden;
|
220 |
+
text-overflow: ellipsis;
|
221 |
}
|
222 |
|
223 |
.dropdown {
|
|
|
370 |
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.4);
|
371 |
}
|
372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
373 |
/* Responsive Design */
|
374 |
@media (max-width: 768px) {
|
375 |
.container {
|
|
|
400 |
}
|
401 |
|
402 |
::-webkit-scrollbar-thumb {
|
403 |
+
background: rgba(102, 126, 234, 0.5 design);
|
404 |
border-radius: 4px;
|
405 |
}
|
406 |
|
|
|
437 |
<form action="/upload" method="post" enctype="multipart/form-data" class="upload-container">
|
438 |
<input type="file" name="file" required class="file-input" id="fileInput">
|
439 |
<input type="hidden" name="path" value="{{ path }}">
|
440 |
+
<label for="fileInput" class="btn">π€ Upload File</label>
|
|
|
|
|
441 |
</form>
|
442 |
+
<button class="btn btn-secondary" onclick="showCreateFolderModal('{{ path }}')">
|
443 |
π New Folder
|
444 |
</button>
|
445 |
</div>
|
|
|
479 |
</div>
|
480 |
</div>
|
481 |
|
482 |
+
<!-- Create Folder Modal -->
|
483 |
+
<div class="modal-overlay" id="createFolderModal">
|
484 |
+
<div class="modal">
|
485 |
+
<h3 class="modal-title">Create New Folder</h3>
|
486 |
+
<input type="text" class="modal-input" id="folderNameInput" placeholder="Enter folder name">
|
487 |
+
<div class="modal-actions">
|
488 |
+
<button class="btn btn-secondary" onclick="closeModal('createFolderModal')">Cancel</button>
|
489 |
+
<button class="btn" onclick="confirmCreateFolder()">Create</button>
|
490 |
+
</div>
|
491 |
+
</div>
|
492 |
+
</div>
|
493 |
+
|
494 |
<!-- Rename Modal -->
|
495 |
<div class="modal-overlay" id="renameModal">
|
496 |
<div class="modal">
|
|
|
518 |
<script>
|
519 |
let currentRenamePath = '';
|
520 |
let currentDeletePath = '';
|
521 |
+
let currentFolderPath = '';
|
522 |
let activeDropdown = null;
|
523 |
|
524 |
function nav(p) {
|
|
|
531 |
|
532 |
function toggleDropdown(button) {
|
533 |
const dropdown = button.closest('.dropdown');
|
|
|
|
|
534 |
if (activeDropdown && activeDropdown !== dropdown) {
|
535 |
activeDropdown.classList.remove('active');
|
536 |
}
|
|
|
537 |
dropdown.classList.toggle('active');
|
538 |
activeDropdown = dropdown.classList.contains('active') ? dropdown : null;
|
539 |
}
|
540 |
|
541 |
+
function showCreateFolderModal(currentPath) {
|
542 |
+
currentFolderPath = currentPath;
|
543 |
+
document.getElementById('folderNameInput').value = '';
|
544 |
+
showModal('createFolderModal');
|
545 |
+
setTimeout(() => document.getElementById('folderNameInput').focus(), 100);
|
546 |
+
}
|
547 |
+
|
548 |
function showRenameModal(path, currentName) {
|
549 |
currentRenamePath = path;
|
550 |
document.getElementById('renameInput').value = currentName;
|
|
|
574 |
activeDropdown = null;
|
575 |
}
|
576 |
|
577 |
+
async function confirmCreateFolder() {
|
578 |
+
const name = document.getElementById('folderNameInput').value.trim();
|
579 |
+
if (!name) return;
|
580 |
+
const folderPath = currentFolderPath ? `${currentFolderPath}/${name}` : name;
|
581 |
try {
|
582 |
+
const response = await fetch('/create_folder', {
|
583 |
method: 'POST',
|
584 |
headers: { 'Content-Type': 'application/json' },
|
585 |
+
body: JSON.stringify({ path: folderPath })
|
|
|
|
|
|
|
586 |
});
|
|
|
587 |
if (response.ok) {
|
588 |
+
closeModal('createFolderModal');
|
589 |
location.reload();
|
590 |
}
|
591 |
} catch (error) {
|
592 |
+
console.error('Create folder failed:', error);
|
593 |
}
|
594 |
}
|
595 |
|
596 |
+
async function confirmRename() {
|
597 |
+
const newName = document.getElementById('renameInput').value.trim();
|
598 |
+
if (!newName) return;
|
599 |
+
const oldPathParts = currentRenamePath.split('/');
|
600 |
+
oldPathParts[oldPathParts.length - 1] = newName;
|
601 |
+
const newPath = oldPathParts.join('/');
|
602 |
try {
|
603 |
+
const response = await fetch('/rename', {
|
604 |
method: 'POST',
|
605 |
headers: { 'Content-Type': 'application/json' },
|
606 |
+
body: JSON.stringify({ old_path: currentRenamePath, new_path: newPath })
|
607 |
});
|
|
|
608 |
if (response.ok) {
|
609 |
+
closeModal('renameModal');
|
610 |
location.reload();
|
611 |
}
|
612 |
} catch (error) {
|
613 |
+
console.error('Rename failed:', error);
|
614 |
}
|
615 |
}
|
616 |
|
617 |
+
async function confirmDelete() {
|
|
|
|
|
|
|
618 |
try {
|
619 |
+
const response = await fetch('/delete', {
|
|
|
620 |
method: 'POST',
|
621 |
headers: { 'Content-Type': 'application/json' },
|
622 |
+
body: JSON.stringify({ path: currentDeletePath })
|
623 |
});
|
|
|
624 |
if (response.ok) {
|
625 |
+
closeModal('deleteModal');
|
626 |
location.reload();
|
627 |
}
|
628 |
} catch (error) {
|
629 |
+
console.error('Delete failed:', error);
|
630 |
}
|
631 |
}
|
632 |
|
|
|
633 |
document.addEventListener('click', function(e) {
|
634 |
if (!e.target.closest('.dropdown')) {
|
635 |
closeAllDropdowns();
|
636 |
}
|
637 |
});
|
638 |
|
|
|
639 |
document.getElementById('fileInput').addEventListener('change', function(e) {
|
640 |
if (e.target.files.length > 0) {
|
641 |
e.target.closest('form').submit();
|
642 |
}
|
643 |
});
|
644 |
|
|
|
645 |
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
646 |
overlay.addEventListener('click', function(e) {
|
647 |
if (e.target === overlay) {
|
|
|
650 |
});
|
651 |
});
|
652 |
|
653 |
+
document.getElementById('folderNameInput').addEventListener('keypress', function(e) {
|
654 |
+
if (e.key === 'Enter') {
|
655 |
+
confirmCreateFolder();
|
656 |
+
}
|
657 |
+
});
|
658 |
+
|
659 |
document.getElementById('renameInput').addEventListener('keypress', function(e) {
|
660 |
if (e.key === 'Enter') {
|
661 |
confirmRename();
|
|
|
682 |
items.append({"type":"dir","name":dir_name,"path":dir_path})
|
683 |
else:
|
684 |
items.append({"type":"file","name":rest,"path":(prefix + rest).strip("/")})
|
|
|
685 |
items.sort(key=lambda x: (x["type"]!="dir", x["name"].lower()))
|
686 |
return items
|
687 |
|
|
|
693 |
@app.route("/download", methods=["GET"])
|
694 |
def download():
|
695 |
p = request.args.get("path","")
|
|
|
696 |
local = hf_hub_download(repo_id=REPO_ID, filename=p, repo_type="dataset", token=HF_TOKEN, cache_dir=tempfile.gettempdir())
|
697 |
return send_file(local, as_attachment=True, download_name=os.path.basename(p))
|
698 |
|