From 62af2031f68914a3b2982c3e2c175de6f369f668 Mon Sep 17 00:00:00 2001 From: Shirone Date: Tue, 13 Jan 2026 19:33:09 +0100 Subject: [PATCH] feat: enhance dev server URL handling and improve accessibility - Added URL and URLSearchParams as readonly globals in ESLint configuration. - Updated WorktreeActionsDropdown and WorktreeTab components to include aria-labels for better accessibility. - Implemented error handling for dev server URL opening, ensuring only valid HTTP/HTTPS protocols are used and providing user feedback for errors. These changes improve user experience and accessibility when interacting with the dev server functionality. --- apps/ui/eslint.config.mjs | 2 + .../components/worktree-actions-dropdown.tsx | 8 +++- .../components/worktree-tab.tsx | 37 ++++++++++++------- .../worktree-panel/hooks/use-dev-servers.ts | 33 ++++++++++++++++- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/apps/ui/eslint.config.mjs b/apps/ui/eslint.config.mjs index d7bc54d4..6db837e3 100644 --- a/apps/ui/eslint.config.mjs +++ b/apps/ui/eslint.config.mjs @@ -70,6 +70,8 @@ const eslintConfig = defineConfig([ AbortSignal: 'readonly', Audio: 'readonly', ScrollBehavior: 'readonly', + URL: 'readonly', + URLSearchParams: 'readonly', // Timers setTimeout: 'readonly', setInterval: 'readonly', diff --git a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx index c7d8f26b..27cc38d8 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx +++ b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx @@ -143,8 +143,12 @@ export function WorktreeActionsDropdown({ Dev Server Running (:{devServerInfo?.port}) - onOpenDevServerUrl(worktree)} className="text-xs"> - + onOpenDevServerUrl(worktree)} + className="text-xs" + aria-label={`Open dev server on port ${devServerInfo?.port} in browser`} + > + onOpenDevServerUrl(worktree)} - title={`Open dev server (port ${devServerInfo?.port})`} - > - - + + + + + + +

Open dev server (:{devServerInfo?.port})

+
+
+
)} { const serverInfo = runningDevServers.get(getWorktreeKey(worktree)); - if (serverInfo) { - window.open(serverInfo.url, '_blank'); + if (!serverInfo) { + logger.warn('No dev server info found for worktree:', getWorktreeKey(worktree)); + toast.error('Dev server not found', { + description: 'The dev server may have stopped. Try starting it again.', + }); + return; + } + + try { + // Rewrite URL hostname to match the current browser's hostname. + // This ensures dev server URLs work when accessing Automaker from + // remote machines (e.g., 192.168.x.x or hostname.local instead of localhost). + const devServerUrl = new URL(serverInfo.url); + + // Security: Only allow http/https protocols to prevent potential attacks + // via data:, javascript:, file:, or other dangerous URL schemes + if (devServerUrl.protocol !== 'http:' && devServerUrl.protocol !== 'https:') { + logger.error('Invalid dev server URL protocol:', devServerUrl.protocol); + toast.error('Invalid dev server URL', { + description: 'The server returned an unsupported URL protocol.', + }); + return; + } + + devServerUrl.hostname = window.location.hostname; + window.open(devServerUrl.toString(), '_blank'); + } catch (error) { + logger.error('Failed to parse dev server URL:', error); + toast.error('Failed to open dev server', { + description: 'The server URL could not be processed. Please try again.', + }); } }, [runningDevServers, getWorktreeKey]