Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion server/modules/browser-use/browser-use.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ function runCommand(command: string, args: string[]): Promise<void> {
shell: false,
stdio: ['ignore', 'pipe', 'pipe'],
});
console.info('[Browser] Running:', command, args.join(' '), '| cwd:', process.cwd());
const output: string[] = [];
let settled = false;
const finish = (fn: () => void) => {
Expand Down Expand Up @@ -279,7 +280,9 @@ function runCommand(command: string, args: string[]): Promise<void> {
return;
}

reject(new Error(output.join('').trim() || `${command} ${args.join(' ')} exited with code ${code}`));
const errorMsg = output.join('').trim() || `${command} ${args.join(' ')} exited with code ${code}`;
console.error('[Browser] Command failed:', errorMsg.slice(0, 500));
reject(new Error(errorMsg));
}));
});
}
Expand Down
139 changes: 68 additions & 71 deletions src/components/browser-use/view/BrowserUsePanel.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ export default function ProviderSelectionEmptyState({
</DialogTrigger>

<DialogContent className="max-w-md overflow-hidden p-0">
<DialogTitle>Model Selector</DialogTitle>
<DialogTitle>{t("providerSelection.modelSelector", { defaultValue: "Model Selector" })}</DialogTitle>
<div className="border-b border-border/60 bg-muted/20 px-4 py-3">
<p className="text-sm font-semibold text-foreground">Choose a model</p>
<p className="text-sm font-semibold text-foreground">{t("providerSelection.chooseAmodel", { defaultValue: "Choose a model" })}</p>
</div>
<Command>
<CommandInput
Expand Down
22 changes: 12 additions & 10 deletions src/components/git-panel/view/GitPanelHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AlertCircle, Check, ChevronDown, Download, GitBranch, Plus, RefreshCw, RotateCcw, Upload, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useEffect, useRef, useState } from 'react';
import type { ConfirmationRequest, GitRemoteStatus } from '../types/types';
import NewBranchModal from './modals/NewBranchModal';
Expand Down Expand Up @@ -52,6 +53,7 @@ export default function GitPanelHeader({
onClearError,
onRequestConfirmation,
}: GitPanelHeaderProps) {
const { t } = useTranslation('common');
const [showBranchDropdown, setShowBranchDropdown] = useState(false);
const [showNewBranchModal, setShowNewBranchModal] = useState(false);
const dropdownRef = useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -129,17 +131,17 @@ export default function GitPanelHeader({
{remoteStatus?.hasRemote && (
<span className="flex items-center gap-0.5 text-xs">
{aheadCount > 0 && (
<span className="text-green-600 dark:text-green-400" title={`${aheadCount} ahead`}>
<span className="text-green-600 dark:text-green-400" title={`${aheadCount} ${t('gitPanel.header.ahead')}`}>
↑{aheadCount}
</span>
)}
{behindCount > 0 && (
<span className="text-primary" title={`${behindCount} behind`}>
<span className="text-primary" title={`${behindCount} ${t('gitPanel.header.behind')}`}>
↓{behindCount}
</span>
)}
{remoteStatus.isUpToDate && (
<span className="text-muted-foreground" title="Up to date">✓</span>
<span className="text-muted-foreground" title={t('gitPanel.header.upToDate')}>✓</span>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)}
</span>
)}
Expand Down Expand Up @@ -174,7 +176,7 @@ export default function GitPanelHeader({
className="flex w-full items-center space-x-2 px-4 py-2 text-left text-sm transition-colors hover:bg-accent"
>
<Plus className="h-3 w-3" />
<span>Create new branch</span>
<span>{t('gitPanel.header.createNewBranch')}</span>
</button>
</div>
</div>
Expand All @@ -193,7 +195,7 @@ export default function GitPanelHeader({
title={`Publish "${currentBranch}" to ${remoteName}`}
>
<Upload className={`h-3 w-3 ${isPublishing ? 'animate-pulse' : ''}`} />
{!isMobile && <span>{isPublishing ? 'Publishing…' : 'Publish'}</span>}
{!isMobile && <span>{isPublishing ? t('gitPanel.header.publishing') : t('gitPanel.header.publish')}</span>}
</button>
) : (
<>
Expand All @@ -205,7 +207,7 @@ export default function GitPanelHeader({
title={`Fetch from ${remoteName}`}
>
<RefreshCw className={`h-3 w-3 ${isFetching ? 'animate-spin' : ''}`} />
{!isMobile && <span>{isFetching ? 'Fetching…' : 'Fetch'}</span>}
{!isMobile && <span>{isFetching ? t('gitPanel.header.fetching') : t('gitPanel.header.fetch')}</span>}
</button>

{behindCount > 0 && (
Expand All @@ -216,7 +218,7 @@ export default function GitPanelHeader({
title={`Pull ${behindCount} from ${remoteName}`}
>
<Download className={`h-3 w-3 ${isPulling ? 'animate-pulse' : ''}`} />
{!isMobile && <span>{isPulling ? 'Pulling…' : `Pull ${behindCount}`}</span>}
{!isMobile && <span>{isPulling ? t('gitPanel.header.pulling') : `${t('gitPanel.header.pull')} ${behindCount}`}</span>}
</button>
)}

Expand All @@ -228,7 +230,7 @@ export default function GitPanelHeader({
title={`Push ${aheadCount} to ${remoteName}`}
>
<Upload className={`h-3 w-3 ${isPushing ? 'animate-pulse' : ''}`} />
{!isMobile && <span>{isPushing ? 'Pushing…' : `Push ${aheadCount}`}</span>}
{!isMobile && <span>{isPushing ? t('gitPanel.header.pushing') : `${t('gitPanel.header.push')} ${aheadCount}`}</span>}
</button>
)}
</>
Expand All @@ -240,7 +242,7 @@ export default function GitPanelHeader({
onClick={requestRevertLocalCommitConfirmation}
disabled={isRevertingLocalCommit}
className={`rounded-lg transition-colors hover:bg-accent disabled:opacity-50 ${isMobile ? 'p-1' : 'p-1.5'}`}
title="Revert latest local commit"
title={t('gitPanel.header.revertLatestCommit')}
>
<RotateCcw
className={`text-muted-foreground ${isRevertingLocalCommit ? 'animate-pulse' : ''} ${isMobile ? 'h-3 w-3' : 'h-4 w-4'}`}
Expand All @@ -251,7 +253,7 @@ export default function GitPanelHeader({
onClick={onRefresh}
disabled={isLoading}
className={`rounded-lg transition-colors hover:bg-accent ${isMobile ? 'p-1' : 'p-1.5'}`}
title="Refresh git status"
title={t('gitPanel.header.refreshStatus')}
>
<RefreshCw className={`text-muted-foreground ${isLoading ? 'animate-spin' : ''} ${isMobile ? 'h-3 w-3' : 'h-4 w-4'}`} />
</button>
Expand Down
15 changes: 9 additions & 6 deletions src/components/git-panel/view/GitViewTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FileText, GitBranch, History } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import type { GitPanelView } from '../types/types';

type GitViewTabsProps = {
Expand All @@ -8,13 +9,15 @@ type GitViewTabsProps = {
onChange: (view: GitPanelView) => void;
};

const TABS: { id: GitPanelView; label: string; Icon: typeof FileText }[] = [
{ id: 'changes', label: 'Changes', Icon: FileText },
{ id: 'history', label: 'Commits', Icon: History },
{ id: 'branches', label: 'Branches', Icon: GitBranch },
];

export default function GitViewTabs({ activeView, isHidden, changeCount, onChange }: GitViewTabsProps) {
const { t } = useTranslation('common');

const TABS: { id: GitPanelView; label: string; Icon: typeof FileText }[] = [
{ id: 'changes', label: t('gitPanel.tabs.changes'), Icon: FileText },
{ id: 'history', label: t('gitPanel.tabs.commits'), Icon: History },
{ id: 'branches', label: t('gitPanel.tabs.branches'), Icon: GitBranch },
];

return (
<div
className={`flex border-b border-border/60 transition-all duration-300 ease-in-out ${
Expand Down
28 changes: 17 additions & 11 deletions src/components/git-panel/view/branches/BranchesView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Check, GitBranch, Globe, Plus, RefreshCw, Trash2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import type { ConfirmationRequest, GitRemoteStatus } from '../../types/types';
import NewBranchModal from '../modals/NewBranchModal';
Expand Down Expand Up @@ -33,6 +34,7 @@ type BranchRowProps = {
};

function BranchRow({ name, isCurrent, isRemote, aheadCount, behindCount, isMobile, onSwitch, onDelete }: BranchRowProps) {
const { t } = useTranslation('common');
return (
<div
className={`group flex items-center gap-3 border-b border-border/40 px-4 transition-colors hover:bg-accent/40 ${
Expand All @@ -58,23 +60,23 @@ function BranchRow({ name, isCurrent, isRemote, aheadCount, behindCount, isMobil
</span>
{isCurrent && (
<span className="shrink-0 rounded-full bg-primary/15 px-1.5 py-0.5 text-xs font-semibold text-primary">
current
{t('gitPanel.branches.current')}
</span>
)}
{isRemote && !isCurrent && (
<span className="shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
remote
{t('gitPanel.branches.remote')}
</span>
)}
</div>
{/* Ahead/behind — only meaningful for the current branch */}
{isCurrent && (aheadCount > 0 || behindCount > 0) && (
<div className="flex items-center gap-2 text-xs">
{aheadCount > 0 && (
<span className="text-green-600 dark:text-green-400">↑{aheadCount} ahead</span>
<span className="text-green-600 dark:text-green-400">↑{aheadCount} {t('gitPanel.branches.ahead')}</span>
)}
{behindCount > 0 && (
<span className="text-primary">↓{behindCount} behind</span>
<span className="text-primary">↓{behindCount} {t('gitPanel.branches.behind')}</span>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)}
</div>
)}
Expand All @@ -91,7 +93,7 @@ function BranchRow({ name, isCurrent, isRemote, aheadCount, behindCount, isMobil
className="rounded-md px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
title={`Switch to ${name}`}
>
Switch
{t('gitPanel.branches.switch')}
</button>
<button
onClick={onDelete}
Expand All @@ -114,7 +116,7 @@ function BranchRow({ name, isCurrent, isRemote, aheadCount, behindCount, isMobil
function SectionHeader({ label, count }: { label: string; count: number }) {
return (
<div className="sticky top-0 z-10 flex items-center justify-between bg-background/95 px-4 py-2 backdrop-blur-sm">
<span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">{label}</span>
<span className="text-xs font-semibold tracking-wider text-muted-foreground">{label}</span>
<span className="rounded-full bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">{count}</span>
</div>
);
Expand All @@ -137,6 +139,7 @@ export default function BranchesView({
onDeleteBranch,
onRequestConfirmation,
}: BranchesViewProps) {
const { t } = useTranslation('common');
const [showNewBranchModal, setShowNewBranchModal] = useState(false);

const aheadCount = remoteStatus?.ahead ?? 0;
Expand Down Expand Up @@ -166,27 +169,30 @@ export default function BranchesView({
);
}

const remoteSuffix = remoteBranches.length > 0 ? `, ${remoteBranches.length} ${t('gitPanel.branches.remote')}` : '';
const branchCountText = `${localBranches.length} ${t('gitPanel.branches.local')}${remoteSuffix}`;

return (
<div className="flex flex-1 flex-col overflow-hidden">
{/* Create branch button */}
<div className="flex items-center justify-between border-b border-border/40 px-4 py-2.5">
<span className="text-sm text-muted-foreground">
{localBranches.length} local{remoteBranches.length > 0 ? `, ${remoteBranches.length} remote` : ''}
{branchCountText}
</span>
<button
onClick={() => setShowNewBranchModal(true)}
className="flex items-center gap-1.5 rounded-lg bg-primary/10 px-3 py-1.5 text-sm font-medium text-primary transition-colors hover:bg-primary/20"
>
<Plus className="h-3.5 w-3.5" />
New branch
{t('gitPanel.branches.newBranch')}
</button>
</div>

{/* Branch list */}
<div className="flex-1 overflow-y-auto">
{localBranches.length > 0 && (
<>
<SectionHeader label="Local" count={localBranches.length} />
<SectionHeader label={t('gitPanel.branches.local')} count={localBranches.length} />
{localBranches.map((branch) => (
<BranchRow
key={`local:${branch}`}
Expand All @@ -205,7 +211,7 @@ export default function BranchesView({

{remoteBranches.length > 0 && (
<>
<SectionHeader label="Remote" count={remoteBranches.length} />
<SectionHeader label={t('gitPanel.branches.remote')} count={remoteBranches.length} />
{remoteBranches.map((branch) => (
<BranchRow
key={`remote:${branch}`}
Expand All @@ -225,7 +231,7 @@ export default function BranchesView({
{localBranches.length === 0 && remoteBranches.length === 0 && (
<div className="flex h-32 flex-col items-center justify-center gap-2 text-muted-foreground">
<GitBranch className="h-10 w-10 opacity-30" />
<p className="text-sm">No branches found</p>
<p className="text-sm">{t('gitPanel.branches.noBranchesFound')}</p>
</div>
)}
</div>
Expand Down
28 changes: 15 additions & 13 deletions src/components/git-panel/view/changes/ChangesView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GitBranch, GitCommit, RefreshCw } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { ConfirmationRequest, FileStatusCode, GitDiffMap, GitStatusResponse } from '../../types/types';
import { getAllChangedFiles, hasChangedFiles } from '../../utils/gitPanelUtils';
Expand Down Expand Up @@ -43,6 +44,7 @@ export default function ChangesView({
onRequestConfirmation,
onExpandedFilesChange,
}: ChangesViewProps) {
const { t } = useTranslation('common');
const [expandedFiles, setExpandedFiles] = useState<Set<string>>(new Set());
const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set());

Expand Down Expand Up @@ -161,9 +163,9 @@ export default function ChangesView({
<div className="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-muted/50">
<GitBranch className="h-7 w-7 text-muted-foreground/50" />
</div>
<h3 className="mb-2 text-lg font-medium text-foreground">No commits yet</h3>
<h3 className="mb-2 text-lg font-medium text-foreground">{t('gitPanel.changes.noCommitsYet')}</h3>
<p className="mb-6 max-w-md text-sm text-muted-foreground">
This repository doesn&apos;t have any commits yet. Create your first commit to start tracking changes.
{t('gitPanel.changes.noCommitsDesc')}
</p>
<button
onClick={() => void onCreateInitialCommit()}
Expand All @@ -173,39 +175,39 @@ export default function ChangesView({
{isCreatingInitialCommit ? (
<>
<RefreshCw className="h-4 w-4 animate-spin" />
<span>Creating Initial Commit...</span>
<span>{t('gitPanel.changes.creatingInitialCommit')}</span>
</>
) : (
<>
<GitCommit className="h-4 w-4" />
<span>Create Initial Commit</span>
<span>{t('gitPanel.changes.createInitialCommit')}</span>
</>
)}
</button>
</div>
) : !gitStatus || !hasChangedFiles(gitStatus) ? (
<div className="flex h-32 flex-col items-center justify-center text-muted-foreground">
<GitCommit className="mb-2 h-10 w-10 opacity-40" />
<p className="text-sm">No changes detected</p>
<p className="text-sm">{t('gitPanel.changes.noChangesDetected')}</p>
</div>
) : (
<div className={isMobile ? 'pb-4' : ''}>
{/* STAGED section */}
<div className="flex items-center justify-between border-b border-border/60 bg-muted/30 px-3 py-1.5">
<span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
Staged ({selectedFiles.size})
<span className="text-xs font-semibold tracking-wide text-muted-foreground">
{t('gitPanel.changes.staged')} ({selectedFiles.size})
</span>
{selectedFiles.size > 0 && (
<button
onClick={() => setSelectedFiles(new Set())}
className="text-xs text-primary transition-colors hover:text-primary/80"
>
Unstage All
{t('gitPanel.changes.unstageAll')}
</button>
)}
</div>
{selectedFiles.size === 0 ? (
<div className="px-3 py-2 text-xs text-muted-foreground italic">No staged files</div>
<div className="px-3 py-2 text-xs text-muted-foreground italic">{t('gitPanel.changes.noStagedFiles')}</div>
) : (
<FileChangeList
gitStatus={gitStatus}
Expand All @@ -225,20 +227,20 @@ export default function ChangesView({

{/* CHANGES section */}
<div className="flex items-center justify-between border-b border-border/60 bg-muted/30 px-3 py-1.5">
<span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
Changes ({unstagedFiles.size})
<span className="text-xs font-semibold tracking-wide text-muted-foreground">
{t('gitPanel.changes.changes')} ({unstagedFiles.size})
</span>
{unstagedFiles.size > 0 && (
<button
onClick={() => setSelectedFiles(new Set(changedFiles))}
className="text-xs text-primary transition-colors hover:text-primary/80"
>
Stage All
{t('gitPanel.changes.stageAll')}
</button>
)}
</div>
{unstagedFiles.size === 0 ? (
<div className="px-3 py-2 text-xs text-muted-foreground italic">All changes staged</div>
<div className="px-3 py-2 text-xs text-muted-foreground italic">{t('gitPanel.changes.allChangesStaged')}</div>
) : (
<FileChangeList
gitStatus={gitStatus}
Expand Down
Loading