diff --git a/src/webdrop_bridge/ui/bridge_script_intercept.js b/src/webdrop_bridge/ui/bridge_script_intercept.js index e600d0d..b3c2f52 100644 --- a/src/webdrop_bridge/ui/bridge_script_intercept.js +++ b/src/webdrop_bridge/ui/bridge_script_intercept.js @@ -82,9 +82,7 @@ originalAddEventListener.call(document, 'dragstart', function(e) { currentDragUrl = null; // Reset - // Check once per drag if native DnD is disabled via URL param (e.g. ?disablednd=true) - var disabledNativeDnD = /[?&]disablednd=true/i.test(window.location.search); - console.log('%c[Intercept] dragstart', 'background: #FF9800; color: white; padding: 2px 6px;', 'ALT:', e.altKey, '| disablednd:', disabledNativeDnD); + console.log('%c[Intercept] dragstart', 'background: #FF9800; color: white; padding: 2px 6px;', 'ALT:', e.altKey); // Call Angular's handlers first to let them set the data var handled = 0; @@ -104,8 +102,7 @@ console.log('[Intercept] Called', handled, 'Angular handlers, URL:', currentDragUrl ? currentDragUrl.substring(0, 60) : 'none'); // NOW check if we should intercept - // Intercept when: Alt key held (normal mode) OR native DnD disabled via URL param - if ((e.altKey || disabledNativeDnD) && currentDragUrl) { + if (e.altKey && currentDragUrl) { var shouldIntercept = false; // Check against configured URL mappings @@ -191,7 +188,7 @@ }); } - console.log('%c[WebDrop Intercept] Ready! ALT-drag or ?disablednd=true will use Qt file drag.', + console.log('%c[WebDrop Intercept] Ready! ALT-drag will use Qt file drag.', 'background: #4CAF50; color: white; font-weight: bold; padding: 4px 8px;'); } catch(e) { console.error('[WebDrop Intercept] FATAL ERROR in bridge script:', e); diff --git a/src/webdrop_bridge/ui/main_window.py b/src/webdrop_bridge/ui/main_window.py index b6d3b64..2b05a07 100644 --- a/src/webdrop_bridge/ui/main_window.py +++ b/src/webdrop_bridge/ui/main_window.py @@ -200,101 +200,6 @@ DEFAULT_WELCOME_PAGE = """ """ -class OpenDropZone(QWidget): - """Drop target widget that opens dragged files with their system default application. - - Displays an 'open folder' icon in the navigation toolbar. When a file is - dragged from the web view and dropped here, the file's URL is passed to - ``QDesktopServices.openUrl()`` so the OS opens it with the associated - programme — exactly the same way double-clicking a file in Explorer/Finder - would behave. - - Visual feedback is provided on drag-enter (green border highlight) so the - user can see the drop target is active. - - Signals: - file_opened (str): Emitted with the local file path when successfully opened. - file_open_failed (str, str): Emitted with (path, error_message) on failure. - """ - - file_opened = Signal(str) - file_open_failed = Signal(str, str) - - _NORMAL_STYLE = "QLabel { padding: 4px; border: 2px solid transparent; border-radius: 4px; }" - _HOVER_STYLE = ( - "QLabel { padding: 4px; border: 2px solid #4CAF50; border-radius: 4px;" - " background: #E8F5E9; }" - ) - - def __init__(self, parent: Optional[QWidget] = None) -> None: - """Initialize the OpenDropZone widget. - - Args: - parent: Parent widget. - """ - super().__init__(parent) - self.setAcceptDrops(True) - - layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - self._icon_label = QLabel(self) - icon = self.style().standardIcon(self.style().StandardPixmap.SP_DirOpenIcon) - pixmap = icon.pixmap(QSize(32, 32)) - self._icon_label.setPixmap(pixmap) - self._icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self._icon_label.setStyleSheet(self._NORMAL_STYLE) - self._icon_label.setToolTip("Drop a file here to open it with its default application") - layout.addWidget(self._icon_label) - - self.setMinimumSize(QSize(44, 44)) - self.setMaximumSize(QSize(48, 48)) - - # ------------------------------------------------------------------ - # Drop handling - # ------------------------------------------------------------------ - - def dragEnterEvent(self, event) -> None: # type: ignore[override] - """Accept drag events that carry file URLs.""" - if event.mimeData().hasUrls(): - event.acceptProposedAction() - self._icon_label.setStyleSheet(self._HOVER_STYLE) - else: - event.ignore() - - def dragLeaveEvent(self, event) -> None: # type: ignore[override] - """Reset appearance when drag leaves the widget.""" - self._icon_label.setStyleSheet(self._NORMAL_STYLE) - super().dragLeaveEvent(event) - - def dropEvent(self, event) -> None: # type: ignore[override] - """Open each dropped file with the system default application. - - Accepts the drop action so that the originating ``QDrag`` reports - success (preserving normal drag-started accounting), then immediately - opens the file via ``QDesktopServices.openUrl()``. - """ - self._icon_label.setStyleSheet(self._NORMAL_STYLE) - mime = event.mimeData() - if not mime.hasUrls(): - event.ignore() - return - - event.acceptProposedAction() - for url in mime.urls(): - if url.isLocalFile(): - file_path = url.toLocalFile() - logger.info(f"OpenDropZone: opening '{file_path}' with system default app") - if QDesktopServices.openUrl(url): - self.file_opened.emit(file_path) - else: - msg = "OS could not open the file" - logger.warning(f"OpenDropZone: {msg}: {file_path}") - self.file_open_failed.emit(file_path, msg) - else: - logger.debug(f"OpenDropZone: skipping non-local URL {url.toString()}") - - class _DragBridge(QObject): """JavaScript bridge for drag operations via QWebChannel. @@ -994,30 +899,6 @@ class MainWindow(QMainWindow): f"Could not complete the drag-and-drop operation.\n\nError: {error}", ) - def _on_file_opened_via_drop(self, file_path: str) -> None: - """Handle a file successfully opened via the OpenDropZone. - - Args: - file_path: Local file path that was opened. - """ - logger.info(f"Opened via drop zone: {file_path}") - self.statusBar().showMessage(f"Opened: {Path(file_path).name}", 4000) - - def _on_file_open_failed_via_drop(self, file_path: str, error: str) -> None: - """Handle a failure to open a file dropped on the OpenDropZone. - - Args: - file_path: Local file path that could not be opened. - error: Error description. - """ - logger.warning(f"Failed to open via drop zone '{file_path}': {error}") - QMessageBox.warning( - self, - "Open File Error", - f"Could not open the file with its default application.\n\n" - f"File: {file_path}\nError: {error}", - ) - def _on_download_requested(self, download: QWebEngineDownloadRequest) -> None: """Handle download requests from the embedded web view. @@ -1221,14 +1102,6 @@ class MainWindow(QMainWindow): refresh_action = self.web_view.pageAction(self.web_view.page().WebAction.Reload) toolbar.addAction(refresh_action) - # Open-with-default-app drop zone (right of Reload) - self._open_drop_zone = OpenDropZone() - self._open_drop_zone.file_opened.connect(self._on_file_opened_via_drop) - self._open_drop_zone.file_open_failed.connect(self._on_file_open_failed_via_drop) - open_drop_action = QWidgetAction(toolbar) - open_drop_action.setDefaultWidget(self._open_drop_zone) - toolbar.addAction(open_drop_action) - # Add stretch spacer to push help buttons to the right spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) diff --git a/src/webdrop_bridge/ui/restricted_web_view.py b/src/webdrop_bridge/ui/restricted_web_view.py index a8359a4..bd77741 100644 --- a/src/webdrop_bridge/ui/restricted_web_view.py +++ b/src/webdrop_bridge/ui/restricted_web_view.py @@ -204,7 +204,7 @@ class RestrictedWebEngineView(QWebEngineView): - Exact domain matches: example.com - Wildcard patterns: *.example.com - Localhost variations: localhost, 127.0.0.1 - - Internal/local URLs: file://, data:, about:, blob:, qrc: + - File URLs: file://... Args: url: QUrl to check @@ -216,8 +216,8 @@ class RestrictedWebEngineView(QWebEngineView): host = url.host() scheme = url.scheme() - # Allow internal browser/Qt schemes (never send these to the OS) - if scheme in ("file", "data", "about", "blob", "qrc"): + # Allow file:// URLs (local webapp) + if scheme == "file": return True # If no whitelist, allow all URLs