Spaces:
Sleeping
Sleeping
Commit
·
ba690ef
1
Parent(s):
60bebee
Upd files page
Browse files- app.py +12 -0
- static/index.html +9 -0
- static/script.js +43 -3
- static/sidebar.js +16 -6
app.py
CHANGED
|
@@ -525,6 +525,18 @@ async def list_project_files(user_id: str, project_id: str):
|
|
| 525 |
return {"files": files, "filenames": filenames}
|
| 526 |
|
| 527 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 528 |
@app.get("/cards")
|
| 529 |
def list_cards(user_id: str, project_id: str, filename: Optional[str] = None, limit: int = 50, skip: int = 0):
|
| 530 |
"""List cards for a project"""
|
|
|
|
| 525 |
return {"files": files, "filenames": filenames}
|
| 526 |
|
| 527 |
|
| 528 |
+
@app.delete("/files", response_model=MessageResponse)
|
| 529 |
+
async def delete_file(user_id: str, project_id: str, filename: str):
|
| 530 |
+
"""Delete a file summary and associated chunks for a project."""
|
| 531 |
+
try:
|
| 532 |
+
rag.db["files"].delete_many({"user_id": user_id, "project_id": project_id, "filename": filename})
|
| 533 |
+
rag.db["chunks"].delete_many({"user_id": user_id, "project_id": project_id, "filename": filename})
|
| 534 |
+
logger.info(f"[FILES] Deleted file {filename} for user {user_id} project {project_id}")
|
| 535 |
+
return MessageResponse(message="File deleted")
|
| 536 |
+
except Exception as e:
|
| 537 |
+
raise HTTPException(500, detail=f"Failed to delete file: {str(e)}")
|
| 538 |
+
|
| 539 |
+
|
| 540 |
@app.get("/cards")
|
| 541 |
def list_cards(user_id: str, project_id: str, filename: Optional[str] = None, limit: int = 50, skip: int = 0):
|
| 542 |
"""List cards for a project"""
|
static/index.html
CHANGED
|
@@ -136,6 +136,15 @@
|
|
| 136 |
|
| 137 |
<!-- Project Content (hidden until project selected) -->
|
| 138 |
<div class="project-content" id="project-content" style="display:none;">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
<!-- Upload Section -->
|
| 140 |
<section class="card reveal" id="upload-section">
|
| 141 |
<div class="card-header">
|
|
|
|
| 136 |
|
| 137 |
<!-- Project Content (hidden until project selected) -->
|
| 138 |
<div class="project-content" id="project-content" style="display:none;">
|
| 139 |
+
<!-- Files Section -->
|
| 140 |
+
<section class="card reveal" id="files-section" style="display:none;">
|
| 141 |
+
<div class="card-header">
|
| 142 |
+
<h2>📄 Project Files</h2>
|
| 143 |
+
<p>Browse your files, view summaries, and manage them</p>
|
| 144 |
+
</div>
|
| 145 |
+
<div id="files-list"></div>
|
| 146 |
+
</section>
|
| 147 |
+
|
| 148 |
<!-- Upload Section -->
|
| 149 |
<section class="card reveal" id="upload-section">
|
| 150 |
<div class="card-header">
|
static/script.js
CHANGED
|
@@ -153,14 +153,54 @@
|
|
| 153 |
|
| 154 |
function renderStoredFiles(files) {
|
| 155 |
const container = document.getElementById('stored-file-items');
|
| 156 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
if (!files || files.length === 0) {
|
| 158 |
-
|
| 159 |
return;
|
| 160 |
}
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
}
|
| 163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
// Duplicate detection: returns {toUpload, replace, renameMap}
|
| 165 |
async function resolveDuplicates(files) {
|
| 166 |
const existing = window.__sb_current_filenames || new Set();
|
|
|
|
| 153 |
|
| 154 |
function renderStoredFiles(files) {
|
| 155 |
const container = document.getElementById('stored-file-items');
|
| 156 |
+
if (container) {
|
| 157 |
+
if (!files || files.length === 0) {
|
| 158 |
+
container.innerHTML = '<div class="muted">No files stored yet.</div>';
|
| 159 |
+
} else {
|
| 160 |
+
container.innerHTML = files.map(f => `<div class=\"pill\">${f.filename}</div>`).join(' ');
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
// Also render into Files page section
|
| 164 |
+
const list = document.getElementById('files-list');
|
| 165 |
+
if (!list) return;
|
| 166 |
if (!files || files.length === 0) {
|
| 167 |
+
list.innerHTML = '<div class="muted">No files in this project.</div>';
|
| 168 |
return;
|
| 169 |
}
|
| 170 |
+
list.innerHTML = files.map(f => `
|
| 171 |
+
<div class="file-card">
|
| 172 |
+
<div class="file-card-head">
|
| 173 |
+
<div class="file-name">${f.filename}</div>
|
| 174 |
+
<button class="file-del" data-fn="${encodeURIComponent(f.filename)}" title="Delete">🗑️</button>
|
| 175 |
+
</div>
|
| 176 |
+
<div class="file-summary">${(f.summary || '').replace(/</g,'<')}</div>
|
| 177 |
+
</div>
|
| 178 |
+
`).join('');
|
| 179 |
+
// bind deletes
|
| 180 |
+
list.querySelectorAll('.file-del').forEach(btn => {
|
| 181 |
+
btn.addEventListener('click', async () => {
|
| 182 |
+
const filename = decodeURIComponent(btn.getAttribute('data-fn'));
|
| 183 |
+
if (!confirm(`Delete ${filename}? This will remove all related chunks.`)) return;
|
| 184 |
+
const user = window.__sb_get_user();
|
| 185 |
+
const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
|
| 186 |
+
if (!user || !currentProject) return;
|
| 187 |
+
try {
|
| 188 |
+
const res = await fetch(`/files?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}&filename=${encodeURIComponent(filename)}`, { method: 'DELETE' });
|
| 189 |
+
if (res.ok) {
|
| 190 |
+
await loadStoredFiles();
|
| 191 |
+
} else {
|
| 192 |
+
alert('Failed to delete file');
|
| 193 |
+
}
|
| 194 |
+
} catch {}
|
| 195 |
+
});
|
| 196 |
+
});
|
| 197 |
}
|
| 198 |
|
| 199 |
+
// Expose show files section
|
| 200 |
+
window.__sb_show_files_section = async () => {
|
| 201 |
+
await loadStoredFiles();
|
| 202 |
+
};
|
| 203 |
+
|
| 204 |
// Duplicate detection: returns {toUpload, replace, renameMap}
|
| 205 |
async function resolveDuplicates(files) {
|
| 206 |
const existing = window.__sb_current_filenames || new Set();
|
static/sidebar.js
CHANGED
|
@@ -110,22 +110,22 @@
|
|
| 110 |
switch (section) {
|
| 111 |
case 'projects':
|
| 112 |
// Projects section is always visible, no action needed
|
|
|
|
| 113 |
break;
|
| 114 |
case 'files':
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
| 117 |
break;
|
| 118 |
case 'chat':
|
| 119 |
-
|
| 120 |
-
console.log('Navigate to Chat section');
|
| 121 |
break;
|
| 122 |
case 'analytics':
|
| 123 |
// Could show usage analytics or insights
|
| 124 |
-
console.log('Navigate to Analytics section');
|
| 125 |
break;
|
| 126 |
case 'settings':
|
| 127 |
// Could show user settings or preferences
|
| 128 |
-
console.log('Navigate to Settings section');
|
| 129 |
break;
|
| 130 |
}
|
| 131 |
|
|
@@ -135,6 +135,16 @@
|
|
| 135 |
}
|
| 136 |
}
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
function updatePageTitle() {
|
| 139 |
const titles = {
|
| 140 |
'projects': 'Projects',
|
|
|
|
| 110 |
switch (section) {
|
| 111 |
case 'projects':
|
| 112 |
// Projects section is always visible, no action needed
|
| 113 |
+
showSection('upload');
|
| 114 |
break;
|
| 115 |
case 'files':
|
| 116 |
+
showSection('files');
|
| 117 |
+
if (window.__sb_show_files_section) {
|
| 118 |
+
window.__sb_show_files_section();
|
| 119 |
+
}
|
| 120 |
break;
|
| 121 |
case 'chat':
|
| 122 |
+
showSection('chat');
|
|
|
|
| 123 |
break;
|
| 124 |
case 'analytics':
|
| 125 |
// Could show usage analytics or insights
|
|
|
|
| 126 |
break;
|
| 127 |
case 'settings':
|
| 128 |
// Could show user settings or preferences
|
|
|
|
| 129 |
break;
|
| 130 |
}
|
| 131 |
|
|
|
|
| 135 |
}
|
| 136 |
}
|
| 137 |
|
| 138 |
+
function showSection(name) {
|
| 139 |
+
const upload = document.getElementById('upload-section');
|
| 140 |
+
const chat = document.getElementById('chat-section');
|
| 141 |
+
const files = document.getElementById('files-section');
|
| 142 |
+
if (!upload || !chat || !files) return;
|
| 143 |
+
upload.style.display = name === 'upload' ? 'block' : 'none';
|
| 144 |
+
chat.style.display = name === 'chat' ? 'block' : 'none';
|
| 145 |
+
files.style.display = name === 'files' ? 'block' : 'none';
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
function updatePageTitle() {
|
| 149 |
const titles = {
|
| 150 |
'projects': 'Projects',
|