(ev)
| 197 | } |
| 198 | |
| 199 | function menuClickDelegate(ev) { |
| 200 | const btn = ev.target.closest('.mi[data-action]'); |
| 201 | if (!btn) return; |
| 202 | ev.stopPropagation(); |
| 203 | |
| 204 | // CLOSE MENU FIRST so it can’t overlay the modal |
| 205 | const ctxRow = window.__filr_ctx_row || null; |
| 206 | hideFileContextMenu(); |
| 207 | |
| 208 | const action = btn.dataset.action; |
| 209 | const s = window.__filr_ctx_state || currentSelection(); |
| 210 | const folder = window.currentFolder || 'root'; |
| 211 | |
| 212 | switch (action) { |
| 213 | case 'create_file': openCreateFileModal(); break; |
| 214 | case 'delete_selected': handleDeleteSelected(new Event('click')); break; |
| 215 | case 'copy_selected': handleCopySelected(new Event('click')); break; |
| 216 | case 'move_selected': handleMoveSelected(new Event('click')); break; |
| 217 | case 'download_zip': handleDownloadZipSelected(new Event('click')); break; |
| 218 | case 'extract_zip': handleExtractZipSelected(new Event('click')); break; |
| 219 | case 'download_plain': |
| 220 | // Uses current checkbox selection; limit enforced in fileListView |
| 221 | downloadSelectedFilesIndividually(s.files); |
| 222 | break; |
| 223 | |
| 224 | case 'tag_selected': |
| 225 | openMultiTagModal(s.files); // s.files are the real file objects |
| 226 | break; |
| 227 | |
| 228 | case 'preview': |
| 229 | if (s.file) { |
| 230 | const sourceId = String(s.file.sourceId || '').trim() |
| 231 | || (window.__frGetActiveSourceId ? String(window.__frGetActiveSourceId() || '').trim() : ''); |
| 232 | previewFile(buildPreviewUrl(folder, s.file.name, sourceId), s.file.name); |
| 233 | } |
| 234 | break; |
| 235 | |
| 236 | case 'edit': |
| 237 | if (s.file && s.canEdit) { |
| 238 | const sourceId = String(s.file.sourceId || '').trim() |
| 239 | || (window.__frGetActiveSourceId ? String(window.__frGetActiveSourceId() || '').trim() : ''); |
| 240 | editFile(s.file.name, folder, sourceId, s.file.sizeBytes); |
| 241 | } |
| 242 | break; |
| 243 | |
| 244 | case 'rename': |
| 245 | if (s.file) { |
| 246 | const row = ctxRow; |
| 247 | if (row && typeof startInlineRenameFromContext === 'function') { |
| 248 | const started = startInlineRenameFromContext(s.file, row); |
| 249 | if (started) break; |
| 250 | } |
| 251 | renameFile(s.file.name, folder); |
| 252 | } |
| 253 | break; |
| 254 | |
| 255 | case 'tag_file': |
| 256 | if (s.file) openTagModal(s.file); |
nothing calls this directly
no test coverage detected