perf(ui): migrate icons to SVG sprite system
Replace @remixicon/react with a shared Icon component that renders via <use href> references to a single hidden SVG sprite. This reduces DOM node count by replacing inline SVGs with lightweight references. - Create Icon component with sprite injection (packages/ui/src/components/icon/) - Migrate all 164 files from @remixicon/react to Icon component - Auto-generate sprite data from remixicon bundle (scripts/generate-icon-sprite.mjs) - Add bun run icons:generate to package.json - Move @remixicon/react to devDependencies - Add icon usage instructions to theme-system skill
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { RiArrowDownSLine, RiArrowRightSLine, RiEditLine, RiGitCommitLine, RiLoader4Line, RiTextWrap } from '@remixicon/react';
|
||||
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
|
||||
@@ -26,6 +25,7 @@ import type { DiffViewMode } from '@/components/chat/message/types';
|
||||
import { PierreDiffViewer } from './PierreDiffViewer';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { getContextFileOpenFailureMessage, validateContextFileOpen } from '@/lib/contextFileOpenGuard';
|
||||
import { sessionEvents } from '@/lib/sessionEvents';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -232,7 +232,7 @@ const FileSelector = React.memo<FileSelectorProps>(({
|
||||
) : (
|
||||
<span className="text-muted-foreground">{t('diffView.selector.selectFile')}</span>
|
||||
)}
|
||||
<RiArrowDownSLine className="size-4 opacity-50" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-50" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="max-h-[70vh] min-w-[320px] overflow-y-auto">
|
||||
@@ -297,7 +297,7 @@ const DiffViewModeSelector = React.memo<DiffViewModeSelectorProps>(({ mode, onMo
|
||||
<span className="min-w-0 truncate typography-meta">
|
||||
{t(currentOption.labelKey)}
|
||||
</span>
|
||||
<RiArrowDownSLine className="size-4 opacity-50" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-50" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="min-w-[140px]">
|
||||
@@ -788,9 +788,9 @@ const MultiFileDiffEntry = React.memo<MultiFileDiffEntryProps>(({
|
||||
<div className="relative flex min-w-0 flex-1 items-center gap-2">
|
||||
<span className="flex size-5 items-center justify-center opacity-70 group-hover/header:opacity-100">
|
||||
{isExpanded ? (
|
||||
<RiArrowDownSLine className="size-4" />
|
||||
<Icon name="arrow-down-s" className="size-4" />
|
||||
) : (
|
||||
<RiArrowRightSLine className="size-4" />
|
||||
<Icon name="arrow-right-s" className="size-4" />
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
@@ -856,9 +856,9 @@ const MultiFileDiffEntry = React.memo<MultiFileDiffEntryProps>(({
|
||||
disabled={isOpeningInEditor}
|
||||
>
|
||||
{isOpeningInEditor ? (
|
||||
<RiLoader4Line className="size-3.5 animate-spin" />
|
||||
<Icon name="loader-4" className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RiEditLine className="size-3.5" />
|
||||
<Icon name="edit" className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
) : null}
|
||||
@@ -895,7 +895,7 @@ const MultiFileDiffEntry = React.memo<MultiFileDiffEntryProps>(({
|
||||
) : null}
|
||||
{isLoading && !diffData && !diffLoadError ? (
|
||||
<div className="flex items-center justify-center gap-2 px-4 py-8 text-sm text-muted-foreground">
|
||||
<RiLoader4Line size={16} className="animate-spin" />
|
||||
<Icon name="loader-4" className="animate-spin" />
|
||||
Loading diff…
|
||||
</div>
|
||||
) : null}
|
||||
@@ -987,7 +987,6 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
const pendingScrollFrameRef = React.useRef<number | null>(null);
|
||||
const shouldPinAfterAlignRef = React.useRef(false);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!pinSelectedFileHeaderToTopOnNavigate || !isStackedView || !pinnedStackedTarget) {
|
||||
return;
|
||||
@@ -1630,7 +1629,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
if (isLoadingStatus && !status) {
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground">
|
||||
<RiLoader4Line size={16} className="animate-spin" />
|
||||
<Icon name="loader-4" className="animate-spin" />
|
||||
{t('diffView.state.loadingRepositoryStatus')}
|
||||
</div>
|
||||
);
|
||||
@@ -1682,7 +1681,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<RiLoader4Line size={16} className="animate-spin" />
|
||||
<Icon name="loader-4" className="animate-spin" />
|
||||
{t('diffView.state.loadingDiff')}
|
||||
</>
|
||||
)}
|
||||
@@ -1697,7 +1696,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
<div className="flex items-center gap-3 px-3 py-2 bg-background">
|
||||
{!isMobile && (
|
||||
<div className="flex items-center gap-1 rounded-md px-2 py-1 text-muted-foreground shrink-0">
|
||||
<RiGitCommitLine size={16} />
|
||||
<Icon name="git-commit" className="h-4 w-4" />
|
||||
<span className="typography-ui-label font-semibold text-foreground">
|
||||
{isLoadingStatus && !status
|
||||
? t('diffView.state.loadingChanges')
|
||||
@@ -1734,7 +1733,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
)}
|
||||
title={diffWrapLines ? t('diffView.actions.disableLineWrap') : t('diffView.actions.enableLineWrap')}
|
||||
>
|
||||
<RiTextWrap className="size-4" />
|
||||
<Icon name="text-wrap" className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
{showOpenInEditorAction && selectedFileEntry && !isStackedView && (
|
||||
@@ -1749,9 +1748,9 @@ export const DiffView: React.FC<DiffViewProps> = ({
|
||||
title={t('diffView.actions.openFileAtFirstChangedLine')}
|
||||
>
|
||||
{isOpeningSelectedInEditor ? (
|
||||
<RiLoader4Line className="size-3.5 animate-spin" />
|
||||
<Icon name="loader-4" className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RiEditLine className="size-3.5" />
|
||||
<Icon name="edit" className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -1,36 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
RiArrowLeftSLine,
|
||||
RiArrowDownSLine,
|
||||
RiClipboardLine,
|
||||
RiCloseLine,
|
||||
RiFileCopy2Line,
|
||||
RiCheckLine,
|
||||
RiFolder3Fill,
|
||||
RiFolderOpenFill,
|
||||
RiFolderReceivedLine,
|
||||
RiFileCheckFill,
|
||||
RiFileCheckLine,
|
||||
RiFullscreenExitLine,
|
||||
RiFullscreenLine,
|
||||
RiLoader4Line,
|
||||
RiRefreshLine,
|
||||
RiSearchLine,
|
||||
RiSave3Line,
|
||||
RiTextWrap,
|
||||
RiMore2Fill,
|
||||
RiFileAddLine,
|
||||
RiFolderAddLine,
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
RiFileCopyLine,
|
||||
RiFileTransferLine,
|
||||
RiCodeSSlashLine,
|
||||
RiNodeTree,
|
||||
RiDownloadLine,
|
||||
RiMenuFold2Line,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
|
||||
@@ -81,6 +50,7 @@ import { useFilesViewShowGitignored } from '@/lib/filesViewShowGitignored';
|
||||
import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
|
||||
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
|
||||
import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { ensurePierreThemeRegistered } from '@/lib/shiki/appThemeRegistry';
|
||||
import { getDefaultTheme } from '@/lib/theme/themes';
|
||||
import { openDesktopFileInApp, openDesktopPath } from '@/lib/desktop';
|
||||
@@ -388,9 +358,9 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
>
|
||||
{isDir ? (
|
||||
isExpanded ? (
|
||||
<RiFolderOpenFill className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
<Icon name="folder-open-fill" className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
) : (
|
||||
<RiFolder3Fill className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
<Icon name="folder-3-fill" className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
)
|
||||
) : (
|
||||
getFileIcon(node.path, node.extension)
|
||||
@@ -425,13 +395,13 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
className="h-6 w-6"
|
||||
onClick={handleMenuButtonClick}
|
||||
>
|
||||
<RiMore2Fill className="h-4 w-4" />
|
||||
<Icon name="more-2-fill" className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" side={isMobile ? "bottom" : "bottom"} onCloseAutoFocus={() => setContextMenuPath(null)}>
|
||||
{canRename && (
|
||||
<DropdownMenuItem onClick={(e) => { e.stopPropagation(); onOpenDialog('rename', node); }}>
|
||||
<RiEditLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.rename')}
|
||||
<Icon name="edit" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.rename')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem onClick={(e) => {
|
||||
@@ -444,7 +414,7 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
toast.error(t('sidebarFilesTree.toast.copyFailed'));
|
||||
});
|
||||
}}>
|
||||
<RiFileCopyLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.copyPath')}
|
||||
<Icon name="file-copy" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.copyPath')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -457,19 +427,19 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
toast.error(t('sidebarFilesTree.toast.copyFailed'));
|
||||
});
|
||||
}}>
|
||||
<RiFileCopy2Line className="mr-2 h-4 w-4" /> {t('filesView.tree.menu.copyRelativePath')}
|
||||
<Icon name="file-copy-2" className="mr-2 h-4 w-4" /> {t('filesView.tree.menu.copyRelativePath')}
|
||||
</DropdownMenuItem>
|
||||
{!isDir && downloadFile && (
|
||||
<DropdownMenuItem onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void downloadFile(node.path);
|
||||
}}>
|
||||
<RiDownloadLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.save')}
|
||||
<Icon name="download" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.save')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canReveal && (
|
||||
<DropdownMenuItem onClick={(e) => { e.stopPropagation(); onRevealPath(node.path); }}>
|
||||
<RiFolderReceivedLine className="mr-2 h-4 w-4" /> {t(getRevealLabelKey())}
|
||||
<Icon name="folder-received" className="mr-2 h-4 w-4" /> {t(getRevealLabelKey())}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{isDir && (canCreateFile || canCreateFolder) && (
|
||||
@@ -477,12 +447,12 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
<DropdownMenuSeparator />
|
||||
{canCreateFile && (
|
||||
<DropdownMenuItem onClick={(e) => { e.stopPropagation(); onOpenDialog('createFile', node); }}>
|
||||
<RiFileAddLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.newFile')}
|
||||
<Icon name="file-add" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.newFile')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canCreateFolder && (
|
||||
<DropdownMenuItem onClick={(e) => { e.stopPropagation(); onOpenDialog('createFolder', node); }}>
|
||||
<RiFolderAddLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.newFolder')}
|
||||
<Icon name="folder-add" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.newFolder')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
@@ -494,7 +464,7 @@ const FileRow: React.FC<FileRowProps> = ({
|
||||
onClick={(e) => { e.stopPropagation(); onOpenDialog('delete', node); }}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<RiDeleteBinLine className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.delete')}
|
||||
<Icon name="delete-bin" className="mr-2 h-4 w-4" /> {t('sidebarFilesTree.menu.delete')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
@@ -2418,9 +2388,6 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
}).toString()}`))
|
||||
: '';
|
||||
|
||||
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
@@ -2507,7 +2474,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
onClick={() => void handleDialogSubmit()}
|
||||
disabled={isDialogSubmitting || (activeDialog !== 'delete' && !dialogInputValue.trim())}
|
||||
>
|
||||
{isDialogSubmitting ? <RiLoader4Line className="animate-spin" /> : (
|
||||
{isDialogSubmitting ? <Icon name="loader-4" className="animate-spin" /> : (
|
||||
activeDialog === 'delete' ? t('filesView.dialog.delete.confirm') : t('filesView.dialog.confirm')
|
||||
)}
|
||||
</Button>
|
||||
@@ -2572,12 +2539,12 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
<>
|
||||
{isSaving ? (
|
||||
<span className="flex items-center gap-1 px-1 text-muted-foreground typography-meta">
|
||||
<RiLoader4Line className="h-3.5 w-3.5 animate-spin" />
|
||||
<Icon name="loader-4" className="h-3.5 w-3.5 animate-spin" />
|
||||
{t('filesView.editor.saving')}
|
||||
</span>
|
||||
) : autoSaveEnabled && autoSaveStatus === 'saved' && !isDirty ? (
|
||||
<span className="flex items-center gap-1 px-1 text-[color:var(--status-success)] typography-meta">
|
||||
<RiCheckLine className="h-3.5 w-3.5" />
|
||||
<Icon name="check" className="h-3.5 w-3.5" />
|
||||
{t('filesView.editor.saved')}
|
||||
</span>
|
||||
) : isDirty ? (
|
||||
@@ -2589,7 +2556,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={t(autoSaveEnabled ? 'filesView.editor.saveNowTitle' : 'filesView.editor.saveNowManualTitle', { shortcut: `${getModifierLabel()}+S` })}
|
||||
aria-label={t('filesView.editor.saveAria', { shortcut: `${getModifierLabel()}+S` })}
|
||||
>
|
||||
<RiSave3Line className="h-4 w-4" />
|
||||
<Icon name="save-3" className="h-4 w-4" />
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
@@ -2603,7 +2570,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={autoSaveEnabled ? t('filesView.editor.autoSaveOn') : t('filesView.editor.manualSave')}
|
||||
aria-label={autoSaveEnabled ? t('filesView.editor.autoSaveOn') : t('filesView.editor.manualSave')}
|
||||
>
|
||||
{autoSaveEnabled ? <RiFileCheckFill className="size-4" /> : <RiFileCheckLine className="size-4" />}
|
||||
{autoSaveEnabled ? <Icon name="file-check-fill" className="size-4" /> : <Icon name="file-check" className="size-4" />}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
@@ -2617,7 +2584,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={t('filesView.editor.openInDesktopApp')}
|
||||
aria-label={t('filesView.editor.openInDesktopApp')}
|
||||
>
|
||||
<RiFileTransferLine className="h-4 w-4" />
|
||||
<Icon name="file-transfer" className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56 max-h-[70vh] overflow-y-auto">
|
||||
@@ -2636,7 +2603,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => void loadOpenInApps(true)}
|
||||
>
|
||||
<RiRefreshLine className="h-4 w-4" />
|
||||
<Icon name="refresh" className="h-4 w-4" />
|
||||
<span className="typography-ui-label text-foreground">{t('filesView.editor.refreshApps')}</span>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
@@ -2655,7 +2622,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
)}
|
||||
title={wrapLines ? t('filesView.editor.disableLineWrap') : t('filesView.editor.enableLineWrap')}
|
||||
>
|
||||
<RiTextWrap className="size-4" />
|
||||
<Icon name="text-wrap" className="size-4" />
|
||||
</Button>
|
||||
{textViewMode === 'edit' && (
|
||||
<>
|
||||
@@ -2669,7 +2636,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="h-6 w-6 p-0 text-foreground opacity-100 transition-opacity hover:bg-transparent focus-visible:bg-transparent active:bg-transparent"
|
||||
title={t('filesView.editor.findInFile')}
|
||||
>
|
||||
<RiSearchLine className="size-4" />
|
||||
<Icon name="search" className="size-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -2681,7 +2648,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="h-6 w-6 p-0 text-foreground opacity-100 transition-opacity hover:bg-transparent focus-visible:bg-transparent active:bg-transparent"
|
||||
title={t('filesView.editor.goToLine')}
|
||||
>
|
||||
<RiMenuFold2Line className="size-4" />
|
||||
<Icon name="menu-fold-2" className="size-4" />
|
||||
</Button>
|
||||
<GoToLineDialog
|
||||
open={isGoToLineOpen}
|
||||
@@ -2725,9 +2692,9 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={jsonViewMode === 'tree' ? t('filesView.editor.switchToTextView') : t('filesView.editor.switchToTreeView')}
|
||||
>
|
||||
{jsonViewMode === 'tree' ? (
|
||||
<RiCodeSSlashLine className="size-4" />
|
||||
<Icon name="code-sslash" className="size-4" />
|
||||
) : (
|
||||
<RiNodeTree className="size-4" />
|
||||
<Icon name="node-tree" className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@@ -2755,9 +2722,9 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
aria-label={t('filesView.editor.copyFileContents')}
|
||||
>
|
||||
{copiedContent ? (
|
||||
<RiCheckLine className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
<Icon name="check" className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
) : (
|
||||
<RiClipboardLine className="h-4 w-4" />
|
||||
<Icon name="clipboard" className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@@ -2785,9 +2752,9 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
aria-label={t('filesView.editor.copyFilePathTitle', { path: displaySelectedPath })}
|
||||
>
|
||||
{copiedPath ? (
|
||||
<RiCheckLine className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
<Icon name="check" className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
) : (
|
||||
<RiFileCopy2Line className="h-4 w-4" />
|
||||
<Icon name="file-copy-2" className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@@ -2804,7 +2771,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={t('filesView.editor.saveFile')}
|
||||
aria-label={t('filesView.editor.saveFile')}
|
||||
>
|
||||
<RiDownloadLine className="h-4 w-4" />
|
||||
<Icon name="download" className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -2817,7 +2784,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
title={t('filesView.editor.exitFullscreen')}
|
||||
aria-label={t('filesView.editor.exitFullscreen')}
|
||||
>
|
||||
<RiFullscreenExitLine className="h-4 w-4" />
|
||||
<Icon name="fullscreen-exit" className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (!isMobile && mode === 'full' && (
|
||||
<Button
|
||||
@@ -2829,9 +2796,9 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
aria-label={isFullscreen ? t('filesView.editor.exitFullscreen') : t('filesView.editor.fullscreen')}
|
||||
>
|
||||
{isFullscreen ? (
|
||||
<RiFullscreenExitLine className="h-4 w-4" />
|
||||
<Icon name="fullscreen-exit" className="h-4 w-4" />
|
||||
) : (
|
||||
<RiFullscreenLine className="h-4 w-4" />
|
||||
<Icon name="fullscreen" className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
@@ -2880,7 +2847,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
aria-label={t('filesView.editor.back')}
|
||||
className="inline-flex h-7 w-7 flex-shrink-0 items-center justify-center mr-1 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiArrowLeftSLine className="h-5 w-5" />
|
||||
<Icon name="arrow-left-s" className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -2895,7 +2862,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
>
|
||||
<FileTypeIcon filePath={selectedFile.path} extension={selectedFile.extension} className="h-3.5 w-3.5 flex-shrink-0" />
|
||||
<span className="min-w-0 flex-1 truncate">{selectedFile.name}</span>
|
||||
<RiArrowDownSLine className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="min-w-[16rem]">
|
||||
@@ -2938,7 +2905,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="inline-flex h-6 w-6 items-center justify-center rounded-md text-[var(--surface-muted-foreground)] hover:text-[var(--surface-foreground)]"
|
||||
aria-label={t('filesView.editor.closeFileAria', { name: file.name })}
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5" />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
@@ -2999,7 +2966,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
)}
|
||||
aria-label={t('filesView.editor.closeFileAria', { name: file.name })}
|
||||
>
|
||||
<RiCloseLine size={14} />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -3037,7 +3004,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
aria-label={t('filesView.editor.showControlsAria')}
|
||||
title={t('filesView.editor.controlsTitle')}
|
||||
>
|
||||
<RiMore2Fill className="h-4 w-4" />
|
||||
<Icon name="more-2-fill" className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -3050,7 +3017,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
? <div className="p-3" />
|
||||
: (
|
||||
<div className="p-3 flex items-center gap-2 typography-ui text-muted-foreground">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('filesView.state.loading')}
|
||||
</div>
|
||||
)
|
||||
@@ -3228,7 +3195,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
{shouldMaskEditorForPendingNavigation && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center bg-background">
|
||||
<div className="flex items-center gap-2 typography-ui text-muted-foreground">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('filesView.state.openingFileAtChange')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -3250,7 +3217,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
<div className={cn("flex flex-col gap-2 py-2", isMobile ? "px-3" : "px-2")}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<RiSearchLine className="pointer-events-none absolute left-2 top-2 h-4 w-4 text-muted-foreground" />
|
||||
<Icon name="search" className="pointer-events-none absolute left-2 top-2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
ref={searchInputRef}
|
||||
value={searchQuery}
|
||||
@@ -3268,7 +3235,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
searchInputRef.current?.focus();
|
||||
}}
|
||||
>
|
||||
<RiCloseLine className="h-4 w-4" />
|
||||
<Icon name="close" className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -3279,7 +3246,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="h-8 w-8 p-0 flex-shrink-0"
|
||||
title={t('filesView.tree.actions.newFileTitle')}
|
||||
>
|
||||
<RiFileAddLine className="h-4 w-4" />
|
||||
<Icon name="file-add" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -3288,10 +3255,10 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
className="h-8 w-8 p-0 flex-shrink-0"
|
||||
title={t('filesView.tree.actions.newFolderTitle')}
|
||||
>
|
||||
<RiFolderAddLine className="h-4 w-4" />
|
||||
<Icon name="folder-add" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => void refreshRoot()} className="h-8 w-8 p-0 flex-shrink-0">
|
||||
<RiRefreshLine className="h-4 w-4" />
|
||||
<Icon name="refresh" className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3300,7 +3267,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
<ul className="flex flex-col">
|
||||
{searching ? (
|
||||
<li className="flex items-center gap-1.5 px-2 py-1 typography-meta text-muted-foreground">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('filesView.tree.search.searching')}
|
||||
</li>
|
||||
) : searchResults.length > 0 ? (
|
||||
@@ -3352,7 +3319,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
? <div className="p-4" />
|
||||
: (
|
||||
<div className="p-4 flex items-center gap-2 typography-ui text-muted-foreground">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
Loading…
|
||||
</div>
|
||||
)
|
||||
@@ -3417,7 +3384,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
|
||||
{shouldMaskEditorForPendingNavigation && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center bg-background">
|
||||
<div className="flex items-center gap-2 typography-ui text-muted-foreground">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('filesView.state.openingFileAtChange')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,13 +19,6 @@ import {
|
||||
} from '@/stores/useGitStore';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { ScrollShadow } from '@/components/ui/ScrollShadow';
|
||||
import {
|
||||
RiGitBranchLine,
|
||||
RiGitMergeLine,
|
||||
RiGitCommitLine,
|
||||
RiGitPullRequestLine,
|
||||
RiLoader4Line,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -43,6 +36,7 @@ import {
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/command';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
@@ -84,7 +78,6 @@ const GIT_ACTION_TAB_STORAGE_KEY = 'oc.git.actionTab';
|
||||
const isActionTab = (value: unknown): value is ActionTab =>
|
||||
value === 'commit' || value === 'branch' || value === 'pr';
|
||||
|
||||
|
||||
type GitViewSnapshot = {
|
||||
directory?: string;
|
||||
selectedPaths: string[];
|
||||
@@ -521,9 +514,9 @@ export const GitView: React.FC = () => {
|
||||
const [isHistoryDialogOpen, setIsHistoryDialogOpen] = React.useState(false);
|
||||
|
||||
const actionTabItems = React.useMemo(() => [
|
||||
{ id: 'commit', label: t('gitView.tabs.commit'), icon: <RiGitCommitLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'branch', label: t('gitView.tabs.update'), icon: <RiGitMergeLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'pr', label: t('gitView.tabs.pr'), icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'commit', label: t('gitView.tabs.commit'), icon: <Icon name="git-commit" className="h-3.5 w-3.5" /> },
|
||||
{ id: 'branch', label: t('gitView.tabs.update'), icon: <Icon name="git-merge" className="h-3.5 w-3.5" /> },
|
||||
{ id: 'pr', label: t('gitView.tabs.pr'), icon: <Icon name="git-pull-request" className="h-3.5 w-3.5" /> },
|
||||
], [t]);
|
||||
const [actionTab, setActionTab] = React.useState<ActionTab>(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -884,7 +877,6 @@ export const GitView: React.FC = () => {
|
||||
};
|
||||
}, [changeEntries, currentDirectory, git, prefetchDiffs, selectedPaths, visibleChangePaths]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!status || changeEntries.length === 0) {
|
||||
setSelectedPaths(new Set());
|
||||
@@ -2101,7 +2093,7 @@ export const GitView: React.FC = () => {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
<span className="typography-ui-label">{t('gitView.loading.checkingRepository')}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2112,7 +2104,7 @@ export const GitView: React.FC = () => {
|
||||
if (shouldHideNotGitState) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center px-4 text-center">
|
||||
<RiLoader4Line className="mb-3 size-6 animate-spin text-muted-foreground" />
|
||||
<Icon name="loader-4" className="mb-3 size-6 animate-spin text-muted-foreground" />
|
||||
<p className="typography-ui-label font-semibold text-foreground">
|
||||
{t('gitView.empty.worktreeSetupInProgress')}
|
||||
</p>
|
||||
@@ -2125,7 +2117,7 @@ export const GitView: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center px-4 text-center">
|
||||
<RiGitBranchLine className="mb-3 size-6 text-muted-foreground" />
|
||||
<Icon name="git-branch" className="mb-3 size-6 text-muted-foreground" />
|
||||
<p className="typography-ui-label font-semibold text-foreground">
|
||||
{t('gitView.empty.notGitRepository')}
|
||||
</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Dialog } from '@base-ui/react/dialog';
|
||||
import { RiCloseLine } from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { MultiRunLauncher } from '@/components/multirun';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface MultiRunWindowProps {
|
||||
@@ -65,7 +65,7 @@ export const MultiRunWindow: React.FC<MultiRunWindowProps> = ({
|
||||
aria-label={t('multiRun.window.actions.closeAria')}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-md p-0.5 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiCloseLine className="h-5 w-5" />
|
||||
<Icon name="close" className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
<Dialog.Description id={descriptionId} className="sr-only">
|
||||
|
||||
@@ -20,7 +20,6 @@ import { useThemeSystem } from '@/contexts/useThemeSystem';
|
||||
import { generateSyntaxTheme } from '@/lib/theme/syntaxThemeGenerator';
|
||||
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
|
||||
import { languageByExtension } from '@/lib/codemirror/languageByExtension';
|
||||
import { RiCheckLine, RiClipboardLine, RiCodeAiLine, RiLoopRightAiLine } from '@remixicon/react';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useSessions } from '@/sync/sync-context';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
@@ -39,6 +38,7 @@ import { generateBranchName } from '@/lib/git/branchNameGenerator';
|
||||
import { parseProjectPlanMarkdown } from '@/lib/openchamberConfig';
|
||||
import { createWorktreeSessionForNewBranch } from '@/lib/worktreeSessionCreator';
|
||||
import { TodoSendDialog, type TodoSendExecution } from '@/components/session/TodoSendDialog';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -338,7 +338,6 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
|
||||
};
|
||||
}, [cancel, editingDraftId, isMobile, lineSelection]);
|
||||
|
||||
|
||||
const editorExtensions = React.useMemo(() => {
|
||||
const extensions = [createFlexokiCodeMirrorTheme(currentTheme)];
|
||||
const language = languageByExtension(resolvedPath || 'plan.md');
|
||||
@@ -638,7 +637,7 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
|
||||
aria-label={t('planView.actions.improvePlanAria')}
|
||||
disabled={!content.trim()}
|
||||
>
|
||||
<RiLoopRightAiLine className="size-4" />
|
||||
<Icon name="loop-right-ai" className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
@@ -667,7 +666,7 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
|
||||
aria-label={t('planView.actions.implementPlanAria')}
|
||||
disabled={!content.trim()}
|
||||
>
|
||||
<RiCodeAiLine className="size-4" />
|
||||
<Icon name="code-ai" className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
@@ -711,9 +710,9 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
|
||||
aria-label={t('planView.actions.copyPlanContents')}
|
||||
>
|
||||
{copiedContent ? (
|
||||
<RiCheckLine className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
<Icon name="check" className="h-4 w-4 text-[color:var(--status-success)]" />
|
||||
) : (
|
||||
<RiClipboardLine className="h-4 w-4" />
|
||||
<Icon name="clipboard" className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
import { RiEyeLine, RiEyeOffLine } from '@remixicon/react';
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
|
||||
export type PreviewToggleButtonProps = {
|
||||
/** Current mode - determines which icon is displayed */
|
||||
@@ -37,9 +36,9 @@ export const PreviewToggleButton: React.FC<PreviewToggleButtonProps> = ({
|
||||
className="h-5 w-5 p-0"
|
||||
>
|
||||
{isPreview ? (
|
||||
<RiEyeLine className="size-4" aria-hidden="true" />
|
||||
<Icon name="eye" className="size-4" aria-hidden="true"/>
|
||||
) : (
|
||||
<RiEyeOffLine className="size-4" aria-hidden="true" />
|
||||
<Icon name="eye-off" className="size-4" aria-hidden="true"/>
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -7,31 +7,6 @@ import { useCommandsStore } from '@/stores/useCommandsStore';
|
||||
import { useMcpConfigStore } from '@/stores/useMcpConfigStore';
|
||||
import { useSkillsStore } from '@/stores/useSkillsStore';
|
||||
import { useSkillsCatalogStore } from '@/stores/useSkillsCatalogStore';
|
||||
import {
|
||||
RiAiAgentLine,
|
||||
RiAiGenerate2,
|
||||
RiArrowLeftSLine,
|
||||
RiBarChart2Line,
|
||||
RiBookLine,
|
||||
RiBookOpenLine,
|
||||
RiChatAi3Line,
|
||||
RiChatHistoryLine,
|
||||
RiCloseLine,
|
||||
RiCommandLine,
|
||||
RiCloudLine,
|
||||
RiFoldersLine,
|
||||
RiGitBranchLine,
|
||||
RiGlobalLine,
|
||||
RiMicLine,
|
||||
RiListUnordered,
|
||||
RiNotification3Line,
|
||||
RiPaletteLine,
|
||||
RiRobot2Line,
|
||||
RiRestartLine,
|
||||
RiServerLine,
|
||||
RiSlashCommands2,
|
||||
RiBrainLine,
|
||||
} from '@remixicon/react';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
|
||||
import { AgentsSidebar } from '@/components/sections/agents/AgentsSidebar';
|
||||
@@ -56,10 +31,11 @@ import { MagicPromptsPage } from '@/components/sections/magic-prompts/MagicPromp
|
||||
import { GitPage } from '@/components/sections/git-identities/GitPage';
|
||||
import type { OpenChamberSection } from '@/components/sections/openchamber/types';
|
||||
import { OpenChamberPage } from '@/components/sections/openchamber/OpenChamberPage';
|
||||
import { McpIcon } from '@/components/icons/McpIcon';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { IconName } from "@/components/icon/icons";
|
||||
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
||||
import {
|
||||
SETTINGS_PAGE_METADATA,
|
||||
@@ -125,54 +101,54 @@ function isPageAvailable(page: SettingsPageMeta, ctx: SettingsRuntimeContext): b
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function getSettingsNavIcon(slug: SettingsPageSlug): React.ComponentType<{ className?: string }> | null {
|
||||
export function getSettingsNavIcon(slug: SettingsPageSlug): IconName | null {
|
||||
switch (slug) {
|
||||
case 'projects':
|
||||
return RiFoldersLine;
|
||||
return 'folders';
|
||||
case 'remote-instances':
|
||||
return RiServerLine;
|
||||
return 'server';
|
||||
case 'appearance':
|
||||
return RiPaletteLine;
|
||||
return 'palette';
|
||||
case 'chat':
|
||||
return RiChatAi3Line;
|
||||
return 'chat-ai-3';
|
||||
case 'magic-prompts':
|
||||
return RiAiGenerate2;
|
||||
return 'ai-generate-2';
|
||||
case 'notifications':
|
||||
return RiNotification3Line;
|
||||
return 'notification-3';
|
||||
case 'shortcuts':
|
||||
return RiCommandLine;
|
||||
return 'command';
|
||||
case 'sessions':
|
||||
return RiChatHistoryLine;
|
||||
return 'chat-history';
|
||||
|
||||
case 'providers':
|
||||
return RiCloudLine;
|
||||
return 'cloud';
|
||||
case 'agents':
|
||||
return RiAiAgentLine;
|
||||
return 'ai-agent';
|
||||
case 'behavior':
|
||||
return RiBrainLine;
|
||||
return 'brain';
|
||||
case 'commands':
|
||||
return RiSlashCommands2;
|
||||
return 'slash-commands-2';
|
||||
case 'mcp':
|
||||
return McpIcon;
|
||||
return 'plug-2';
|
||||
|
||||
case 'skills.installed':
|
||||
return RiBookOpenLine;
|
||||
return 'book-open';
|
||||
case 'skills.catalog':
|
||||
return RiBookLine;
|
||||
return 'book';
|
||||
|
||||
case 'git':
|
||||
return RiGitBranchLine;
|
||||
return 'git-branch';
|
||||
|
||||
case 'usage':
|
||||
return RiBarChart2Line;
|
||||
return 'bar-chart-2';
|
||||
case 'voice':
|
||||
return RiMicLine;
|
||||
return 'mic';
|
||||
case 'tunnel':
|
||||
return RiGlobalLine;
|
||||
return 'global';
|
||||
case 'home':
|
||||
return null;
|
||||
default:
|
||||
return RiRobot2Line;
|
||||
return 'robot-2';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,8 +560,8 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
<div className="flex flex-col gap-0.5 pt-4 pb-2 px-2">
|
||||
{sortedFilteredPages.map((page) => {
|
||||
const selected = settingsSlug === page.slug;
|
||||
const Icon = getSettingsNavIcon(page.slug);
|
||||
if (!Icon) return null;
|
||||
const iconName = getSettingsNavIcon(page.slug);
|
||||
if (!iconName) return null;
|
||||
|
||||
return (
|
||||
<Tooltip key={page.slug}>
|
||||
@@ -601,7 +577,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
: 'text-foreground hover:bg-interactive-hover'
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<Icon name={iconName} className="h-4 w-4 shrink-0" />
|
||||
<span className="flex items-center gap-1.5 whitespace-nowrap overflow-hidden transition-opacity duration-150 opacity-100">
|
||||
<span className="typography-ui-label font-normal truncate">{getPageTitle(page.slug)}</span>
|
||||
{(page.slug === 'voice' || page.slug === 'tunnel') && (
|
||||
@@ -633,7 +609,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
)}
|
||||
onClick={() => void reloadOpenCodeConfiguration({ message: 'Restarting OpenCode…', mode: 'projects', scopes: ['all'] })}
|
||||
>
|
||||
<RiRestartLine className="h-4 w-4 shrink-0" />
|
||||
<Icon name="restart" className="h-4 w-4 shrink-0" />
|
||||
<span>{t('settings.view.actions.reloadOpenCode')}</span>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
@@ -734,7 +710,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
aria-label={showBackButton ? t('settings.view.actions.backToSettings') : t('settings.view.actions.closeSettings')}
|
||||
className="inline-flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg p-2 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiArrowLeftSLine className="h-5 w-5" />
|
||||
<Icon name="arrow-left-s" className="h-5 w-5" />
|
||||
</button>
|
||||
|
||||
<div className="min-w-0 flex-1 typography-ui-label font-medium text-foreground truncate">
|
||||
@@ -750,7 +726,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
aria-label={t('settings.view.actions.openSectionList')}
|
||||
className="inline-flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg p-2 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiListUnordered className="h-5 w-5" />
|
||||
<Icon name="list-unordered" className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -762,7 +738,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
title={t('settings.view.actions.closeSettingsWithShortcut', { shortcut: shortcutKey })}
|
||||
className="inline-flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg p-2 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiCloseLine className="h-5 w-5" />
|
||||
<Icon name="close" className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -776,7 +752,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
aria-label={t('settings.view.actions.back')}
|
||||
className="inline-flex h-9 w-9 items-center justify-center rounded-lg p-2 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiArrowLeftSLine className="h-5 w-5" />
|
||||
<Icon name="arrow-left-s" className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -790,7 +766,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
title={t('settings.view.actions.closeSettingsWithShortcut', { shortcut: shortcutKey })}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-md p-0.5 text-muted-foreground hover:text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
<RiCloseLine className="h-5 w-5" />
|
||||
<Icon name="close" className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { RiAddLine, RiArrowDownLine, RiArrowGoBackLine, RiArrowLeftLine, RiArrowRightLine, RiArrowUpLine, RiCloseLine, RiCommandLine, RiFullscreenExitLine, RiFullscreenLine, RiGlobalLine, RiTerminalLine } from '@remixicon/react';
|
||||
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useTerminalStore } from '@/stores/useTerminalStore';
|
||||
@@ -14,6 +13,7 @@ import { cn } from '@/lib/utils';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { primeTerminalInputTransport } from '@/lib/terminalApi';
|
||||
@@ -45,7 +45,6 @@ const MODIFIER_ARROW_SUFFIX: Record<Modifier, string> = {
|
||||
cmd: '3',
|
||||
};
|
||||
|
||||
|
||||
const STREAM_OPTIONS = {
|
||||
retry: {
|
||||
maxRetries: 3,
|
||||
@@ -142,8 +141,8 @@ export const TerminalView: React.FC = () => {
|
||||
const terminalTabItems = React.useMemo(() => {
|
||||
return (directoryTerminalState?.tabs ?? []).map((tab) => ({
|
||||
icon: (() => {
|
||||
const Icon = tab.iconKey ? PROJECT_ACTION_ICON_MAP[tab.iconKey as ProjectActionIconKey] ?? RiTerminalLine : RiTerminalLine;
|
||||
return <Icon className="h-4 w-4" />;
|
||||
const tabIconName = tab.iconKey ? PROJECT_ACTION_ICON_MAP[tab.iconKey as ProjectActionIconKey] ?? 'terminal' : 'terminal';
|
||||
return <Icon name={tabIconName} className="h-4 w-4" />;
|
||||
})(),
|
||||
id: tab.id,
|
||||
label: tab.label,
|
||||
@@ -649,7 +648,6 @@ export const TerminalView: React.FC = () => {
|
||||
[activeTabId, closeTab, disconnectStream, effectiveDirectory]
|
||||
);
|
||||
|
||||
|
||||
const handleViewportInput = React.useCallback(
|
||||
(data: string) => {
|
||||
if (!data || isReconnectPending) {
|
||||
@@ -958,7 +956,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('tab')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowRightLine size={16} />
|
||||
<Icon name="arrow-right" className="h-4 w-4" />
|
||||
<span className="sr-only">{t('terminalView.quickKeys.tabAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -982,7 +980,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleModifierToggle('cmd')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiCommandLine size={16} />
|
||||
<Icon name="command"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.commandModifierAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -993,7 +991,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('arrow-up')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowUpLine size={16} />
|
||||
<Icon name="arrow-up"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.arrowUpAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1004,7 +1002,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('arrow-left')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowLeftLine size={16} />
|
||||
<Icon name="arrow-left"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.arrowLeftAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1015,7 +1013,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('arrow-down')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowDownLine size={16} />
|
||||
<Icon name="arrow-down"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.arrowDownAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1026,7 +1024,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('arrow-right')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowRightLine size={16} />
|
||||
<Icon name="arrow-right"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.arrowRightAria')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1037,7 +1035,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={() => handleMobileKeyPress('enter')}
|
||||
disabled={quickKeysDisabled}
|
||||
>
|
||||
<RiArrowGoBackLine size={16} />
|
||||
<Icon name="arrow-go-back"/>
|
||||
<span className="sr-only">{t('terminalView.quickKeys.enterAria')}</span>
|
||||
</Button>
|
||||
</>
|
||||
@@ -1068,7 +1066,7 @@ export const TerminalView: React.FC = () => {
|
||||
onClick={handleCreateTab}
|
||||
title={t('terminalView.tabs.newTabTitle')}
|
||||
>
|
||||
<RiAddLine size={isTouchTerminal ? 18 : 16} />
|
||||
<Icon name="add" className={`${isTouchTerminal ? 'h-[18px] w-[18px]' : 'h-4 w-4'}`}/>
|
||||
</Button>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-1 overflow-visible">
|
||||
@@ -1084,7 +1082,7 @@ export const TerminalView: React.FC = () => {
|
||||
}}
|
||||
title={t('terminalView.preview.openTitle')}
|
||||
>
|
||||
<RiGlobalLine className="h-3.5 w-3.5 shrink-0" />
|
||||
<Icon name="global" className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="whitespace-nowrap">{t('terminalView.preview.open')}</span>
|
||||
</Button>
|
||||
) : null}
|
||||
@@ -1099,7 +1097,7 @@ export const TerminalView: React.FC = () => {
|
||||
title={isBottomTerminalExpanded ? t('terminalView.bottomDock.restoreTitle') : t('terminalView.bottomDock.expandTitle')}
|
||||
aria-label={isBottomTerminalExpanded ? t('terminalView.bottomDock.restoreAria') : t('terminalView.bottomDock.expandAria')}
|
||||
>
|
||||
{isBottomTerminalExpanded ? <RiFullscreenExitLine className="h-4 w-4" /> : <RiFullscreenLine className="h-4 w-4" />}
|
||||
{isBottomTerminalExpanded ? <Icon name="fullscreen-exit" className="h-4 w-4" /> : <Icon name="fullscreen" className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -1110,7 +1108,7 @@ export const TerminalView: React.FC = () => {
|
||||
title={t('terminalView.bottomDock.closeTitle')}
|
||||
aria-label={t('terminalView.bottomDock.closeAria')}
|
||||
>
|
||||
<RiCloseLine className="h-4 w-4" />
|
||||
<Icon name="close" className="h-4 w-4" />
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiGitBranchLine,
|
||||
RiArrowDownSLine,
|
||||
RiCheckLine,
|
||||
RiMore2Line,
|
||||
RiFileCopyLine,
|
||||
RiLoader4Line,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
@@ -31,6 +23,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface AgentGroupDetailProps {
|
||||
@@ -163,7 +156,7 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h1 className="typography-heading-lg text-foreground truncate">{group.name}</h1>
|
||||
{groupBusy && <RiLoader4Line className="h-4 w-4 animate-spin text-amber-500 flex-shrink-0" />}
|
||||
{groupBusy && <Icon name="loader-4" className="h-4 w-4 animate-spin text-amber-500 flex-shrink-0" />}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-1 typography-meta text-muted-foreground">
|
||||
<span>
|
||||
@@ -173,7 +166,7 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
</span>
|
||||
<span>·</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<RiGitBranchLine className="h-3.5 w-3.5" />
|
||||
<Icon name="git-branch" className="h-3.5 w-3.5" />
|
||||
{selectedSession?.worktreeMetadata?.label || selectedSession?.branch || t('agentManager.detail.header.noBranch')}
|
||||
</span>
|
||||
</div>
|
||||
@@ -209,7 +202,7 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<RiArrowDownSLine className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-[var(--anchor-width)]">
|
||||
@@ -237,13 +230,13 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
</div>
|
||||
{session.branch && (
|
||||
<div className="flex items-center gap-1 typography-micro text-muted-foreground/60">
|
||||
<RiGitBranchLine className="h-3 w-3" />
|
||||
<Icon name="git-branch" className="h-3 w-3" />
|
||||
<span className="truncate">{session.worktreeMetadata?.label || session.branch}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{selectedSession?.id === session.id && (
|
||||
<RiCheckLine className="h-4 w-4 text-primary flex-shrink-0" />
|
||||
<Icon name="check" className="h-4 w-4 text-primary flex-shrink-0" />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
@@ -254,7 +247,7 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="flex-shrink-0" aria-label={t('agentManager.detail.actions.worktreeActionsAria')}>
|
||||
<RiMore2Line className="h-4 w-4" />
|
||||
<Icon name="more-2" className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="min-w-[220px]">
|
||||
@@ -278,7 +271,7 @@ export const AgentGroupDetail: React.FC<AgentGroupDetailProps> = ({
|
||||
}}
|
||||
disabled={!selectedSession?.path}
|
||||
>
|
||||
<RiFileCopyLine className="h-4 w-4 mr-px" />
|
||||
<Icon name="file-copy" className="h-4 w-4 mr-px" />
|
||||
{t('agentManager.detail.actions.copyWorktreePath')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiAddCircleLine,
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiCloseLine,
|
||||
RiFileImageLine,
|
||||
RiFileLine,
|
||||
RiGitBranchLine,
|
||||
RiHourglassFill,
|
||||
RiSendPlane2Line,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -22,6 +11,7 @@ import { BranchSelector, useBranchOptions } from '@/components/multirun/BranchSe
|
||||
import { AgentSelector } from '@/components/multirun/AgentSelector';
|
||||
import { CommandAutocomplete, type CommandAutocompleteHandle, type CommandInfo } from '@/components/chat/CommandAutocomplete';
|
||||
import { FileMentionAutocomplete, type FileMentionHandle } from '@/components/chat/FileMentionAutocomplete';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { isIMECompositionEvent } from '@/lib/ime';
|
||||
import { getWorktreeSetupCommands } from '@/lib/openchamberConfig';
|
||||
import { useThemeSystem } from '@/contexts/useThemeSystem';
|
||||
@@ -438,7 +428,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
{/* Branch Selection */}
|
||||
<div className="space-y-1.5">
|
||||
<label className="typography-ui-label font-medium text-foreground flex items-center gap-1.5">
|
||||
<RiGitBranchLine className="h-4 w-4 text-muted-foreground" />
|
||||
<Icon name="git-branch" className="h-4 w-4 text-muted-foreground" />
|
||||
{t('agentManager.empty.baseBranch.label')}
|
||||
</label>
|
||||
<BranchSelector
|
||||
@@ -465,7 +455,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
) : null;
|
||||
})()}
|
||||
</p>
|
||||
<RiArrowDownSLine className={cn(
|
||||
<Icon name="arrow-down-s" className={cn(
|
||||
'h-4 w-4 text-muted-foreground transition-transform duration-200',
|
||||
isSetupCommandsOpen && 'rotate-180'
|
||||
)} />
|
||||
@@ -500,7 +490,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
className="flex-shrink-0 flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
|
||||
aria-label={t('agentManager.empty.setupCommands.removeCommandAria')}
|
||||
>
|
||||
<RiCloseLine className="h-4 w-4" />
|
||||
<Icon name="close" className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
@@ -509,7 +499,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
onClick={() => setSetupCommands([...setupCommands, ''])}
|
||||
className="flex items-center gap-1.5 typography-meta text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<RiAddLine className="h-3.5 w-3.5" />
|
||||
<Icon name="add" className="h-3.5 w-3.5" />
|
||||
{t('agentManager.empty.setupCommands.addCommand')}
|
||||
</button>
|
||||
</div>
|
||||
@@ -583,9 +573,9 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
className="inline-flex items-center gap-1.5 px-2 py-1 bg-muted/30 border border-border/30 rounded-md typography-meta"
|
||||
>
|
||||
{file.mimeType.startsWith('image/') ? (
|
||||
<RiFileImageLine className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Icon name="file-image" className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
) : (
|
||||
<RiFileLine className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Icon name="file" className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
)}
|
||||
<span className="truncate max-w-[120px]" title={file.filename}>
|
||||
{file.filename}
|
||||
@@ -595,7 +585,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
onClick={() => handleRemoveFile(file.id)}
|
||||
className="text-muted-foreground hover:text-destructive ml-0.5"
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5" />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
@@ -620,7 +610,7 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:text-foreground transition-colors"
|
||||
aria-label={t('agentManager.empty.prompt.addAttachmentAria')}
|
||||
>
|
||||
<RiAddCircleLine className="h-[18px] w-[18px]" />
|
||||
<Icon name="add-circle" className="h-[18px] w-[18px]" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -645,9 +635,9 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
|
||||
aria-label={t('agentManager.empty.actions.startAgentGroupAria')}
|
||||
>
|
||||
{isSubmittingOrCreating ? (
|
||||
<RiHourglassFill className="h-[18px] w-[18px] animate-spin" />
|
||||
<Icon name="hourglass-fill" className="h-[18px] w-[18px] animate-spin" />
|
||||
) : (
|
||||
<RiSendPlane2Line className="h-[18px] w-[18px]" />
|
||||
<Icon name="send-plane-2" className="h-[18px] w-[18px]" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiMore2Line,
|
||||
RiSearchLine,
|
||||
RiGitBranchLine,
|
||||
RiLoader4Line,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -25,6 +17,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useAgentGroupsStore, type AgentGroup } from '@/stores/useAgentGroupsStore';
|
||||
import { useAllSessionStatuses } from '@/sync/sync-context';
|
||||
@@ -92,11 +85,11 @@ const AgentGroupItem: React.FC<AgentGroupItemProps> = ({ group, isSelected, isBu
|
||||
<span className="truncate typography-ui-label font-normal text-foreground">
|
||||
{group.name}
|
||||
</span>
|
||||
{isBusy && <RiLoader4Line className="h-3 w-3 animate-spin text-amber-500 flex-shrink-0" />}
|
||||
{isBusy && <Icon name="loader-4" className="h-3 w-3 animate-spin text-amber-500 flex-shrink-0" />}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="typography-micro text-muted-foreground/60 flex items-center gap-1">
|
||||
<RiGitBranchLine className="h-3 w-3" />
|
||||
<Icon name="git-branch" className="h-3 w-3" />
|
||||
{group.sessionCount === 1
|
||||
? t('agentManager.sidebar.item.modelCountSingle', { count: group.sessionCount })
|
||||
: t('agentManager.sidebar.item.modelCountPlural', { count: group.sessionCount })}
|
||||
@@ -126,7 +119,7 @@ const AgentGroupItem: React.FC<AgentGroupItemProps> = ({ group, isSelected, isBu
|
||||
aria-label={t('agentManager.sidebar.item.groupMenuAria')}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<RiMore2Line className="h-3.5 w-3.5" />
|
||||
<Icon name="more-2" className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="min-w-[140px]">
|
||||
@@ -218,7 +211,7 @@ export const AgentManagerSidebar: React.FC<AgentManagerSidebarProps> = ({
|
||||
{/* Search Input */}
|
||||
<div className="px-2.5 pt-3 pb-2">
|
||||
<div className="relative">
|
||||
<RiSearchLine className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Icon name="search" className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
@@ -235,14 +228,14 @@ export const AgentManagerSidebar: React.FC<AgentManagerSidebarProps> = ({
|
||||
className="w-full justify-start gap-2 h-8"
|
||||
onClick={onNewAgent}
|
||||
>
|
||||
<RiAddLine className="h-4 w-4" />
|
||||
<Icon name="add" className="h-4 w-4" />
|
||||
<span className="typography-ui-label">{t('agentManager.sidebar.actions.newAgentGroup')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Agent Groups Section Header */}
|
||||
<div className="px-2.5 py-1.5 flex items-center gap-1">
|
||||
<RiArrowDownSLine className="h-4 w-4 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="typography-micro font-medium text-muted-foreground uppercase tracking-wider">
|
||||
{t('agentManager.sidebar.section.agentGroups')}
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { RiArrowDownLine } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface AIHighlightsBoxProps {
|
||||
@@ -35,7 +35,7 @@ export const AIHighlightsBox: React.FC<AIHighlightsBoxProps> = ({
|
||||
onClick={handleInsert}
|
||||
aria-label={t('gitView.commit.aiHighlights.insertAria')}
|
||||
>
|
||||
<RiArrowDownLine className="size-4" />
|
||||
<Icon name="arrow-down" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8}>
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiGitMergeLine,
|
||||
RiGitBranchLine,
|
||||
RiLoader4Line,
|
||||
RiArrowDownSLine,
|
||||
RiCheckLine,
|
||||
RiCloseLine,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -31,6 +23,7 @@ import {
|
||||
CommandSeparator,
|
||||
} from '@/components/ui/command';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -188,13 +181,13 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<div className="mt-0.5 shrink-0">
|
||||
{log.status === 'running' && (
|
||||
<RiLoader4Line className="size-3.5 animate-spin text-primary" />
|
||||
<Icon name="loader-4" className="size-3.5 animate-spin text-primary" />
|
||||
)}
|
||||
{log.status === 'done' && (
|
||||
<RiCheckLine className="size-3.5 text-success" />
|
||||
<Icon name="check" className="size-3.5 text-success" />
|
||||
)}
|
||||
{log.status === 'error' && (
|
||||
<RiCloseLine className="size-3.5 text-destructive" />
|
||||
<Icon name="close" className="size-3.5 text-destructive" />
|
||||
)}
|
||||
{log.status === 'pending' && (
|
||||
<div className="size-3.5 rounded-full border border-muted-foreground/30" />
|
||||
@@ -251,7 +244,7 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RiGitMergeLine
|
||||
<Icon name="git-merge"
|
||||
className={cn('size-4', operation === 'merge' ? 'text-primary' : 'text-muted-foreground')}
|
||||
/>
|
||||
<span
|
||||
@@ -279,7 +272,7 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RiGitBranchLine
|
||||
<Icon name="git-branch"
|
||||
className={cn('size-4', operation === 'rebase' ? 'text-primary' : 'text-muted-foreground')}
|
||||
/>
|
||||
<span
|
||||
@@ -311,7 +304,7 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
<span className={cn('truncate', !selectedBranch && 'text-muted-foreground')}>
|
||||
{selectedBranch || t('gitView.branch.selectBranch')}
|
||||
</span>
|
||||
<RiArrowDownSLine className="size-4 opacity-60 shrink-0" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-60 shrink-0" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
@@ -388,12 +381,12 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
>
|
||||
{operation === 'merge' ? (
|
||||
<>
|
||||
<RiGitMergeLine className="size-4" />
|
||||
<Icon name="git-merge" className="size-4" />
|
||||
{t('gitView.operation.merge')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RiGitBranchLine className="size-4" />
|
||||
<Icon name="git-branch" className="size-4" />
|
||||
{t('gitView.operation.rebase')}
|
||||
</>
|
||||
)}
|
||||
@@ -444,9 +437,9 @@ export const BranchIntegrationSection: React.FC<BranchIntegrationSectionProps> =
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{isOperating ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiGitMergeLine className="size-4" />
|
||||
<Icon name="git-merge" className="size-4" />
|
||||
)}
|
||||
<span>{t('gitView.branch.mergeRebase')}</span>
|
||||
</Button>
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiGitBranchLine,
|
||||
RiArrowDownSLine,
|
||||
RiAddLine,
|
||||
RiCloseLine,
|
||||
RiLoader4Line,
|
||||
RiArrowLeftLine,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -23,6 +15,7 @@ import {
|
||||
CommandSeparator,
|
||||
} from '@/components/ui/command';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { GitRemote } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -177,11 +170,11 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
className="h-8 min-w-0 max-w-full justify-start gap-1.5 px-2 py-1"
|
||||
disabled={disabled}
|
||||
>
|
||||
<RiGitBranchLine className="size-4 text-primary" />
|
||||
<Icon name="git-branch" className="size-4 text-primary" />
|
||||
<span className="min-w-0 truncate font-medium text-left">
|
||||
{currentBranch || t('gitView.branch.detachedHead')}
|
||||
</span>
|
||||
<RiArrowDownSLine className="size-4 opacity-60" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-60" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
@@ -214,7 +207,7 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
disabled={isCreating}
|
||||
className="shrink-0 text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
<RiArrowLeftLine className="size-4" />
|
||||
<Icon name="arrow-left" className="size-4" />
|
||||
</button>
|
||||
<span className="typography-meta text-muted-foreground">
|
||||
{t('gitView.branch.pushToPrefix')} <span className="text-foreground font-medium">{sanitizedNewBranch}</span> {t('gitView.branch.pushToSuffix')}
|
||||
@@ -231,7 +224,7 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
>
|
||||
<span className="typography-ui-label text-foreground">
|
||||
{isCreating ? (
|
||||
<RiLoader4Line className="inline size-3 mr-1.5 animate-spin" />
|
||||
<Icon name="loader-4" className="inline size-3 mr-1.5 animate-spin" />
|
||||
) : null}
|
||||
{remote.name}
|
||||
</span>
|
||||
@@ -244,7 +237,7 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
</div>
|
||||
) : !showCreate ? (
|
||||
<CommandItem onSelect={handleShowCreate}>
|
||||
<RiAddLine className="size-4" />
|
||||
<Icon name="add" className="size-4" />
|
||||
<span>{t('gitView.branch.create')}</span>
|
||||
</CommandItem>
|
||||
) : (
|
||||
@@ -274,9 +267,9 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
className="shrink-0 text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
{isCreating ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiAddLine className="size-4" />
|
||||
<Icon name="add" className="size-4" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
@@ -285,7 +278,7 @@ export const BranchSelector: React.FC<BranchSelectorProps> = ({
|
||||
disabled={isCreating}
|
||||
className="shrink-0 text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
<RiCloseLine className="size-4" />
|
||||
<Icon name="close" className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
RiArrowGoBackLine,
|
||||
RiLoader4Line,
|
||||
} from '@remixicon/react';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { GitStatus } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -160,9 +157,9 @@ export const ChangeRow = React.memo<ChangeRowProps>(function ChangeRow({
|
||||
aria-label={t('gitView.changes.revertFileAria', { path: file.path })}
|
||||
>
|
||||
{isReverting ? (
|
||||
<RiLoader4Line className="size-3.5 animate-spin" />
|
||||
<Icon name="loader-4" className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RiArrowGoBackLine className="size-3.5" />
|
||||
<Icon name="arrow-go-back" className="size-3.5" />
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { RiArchiveStackLine, RiFolder3Fill, RiFolderOpenFill } from '@remixicon/react';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { ScrollShadow } from '@/components/ui/ScrollShadow';
|
||||
import { OverlayScrollbar } from '@/components/ui/OverlayScrollbar';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { ChangeRow } from './ChangeRow';
|
||||
import type { GitStatus } from '@/lib/api/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -405,9 +405,9 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
|
||||
: t('gitView.changes.expandDirectoryAria', { path: directory.path })}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<RiFolderOpenFill className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
<Icon name="folder-open-fill" className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
) : (
|
||||
<RiFolder3Fill className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
<Icon name="folder-3-fill" className="h-4 w-4 flex-shrink-0 text-primary/60" />
|
||||
)}
|
||||
<span className="min-w-0 flex-1 truncate typography-ui-label text-foreground" title={directory.path}>
|
||||
{directory.name}
|
||||
@@ -475,7 +475,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
|
||||
aria-label={t('gitView.stashes.title')}
|
||||
title={t('gitView.stashes.title')}
|
||||
>
|
||||
<RiArchiveStackLine className="size-4" />
|
||||
<Icon name="archive-stack" className="size-4" />
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import {
|
||||
RiGitCommitLine,
|
||||
RiArrowUpLine,
|
||||
RiAiGenerate2,
|
||||
RiLoader4Line,
|
||||
RiEmotionHappyLine,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CommitInput } from './CommitInput';
|
||||
import { AIHighlightsBox } from './AIHighlightsBox';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
type CommitAction = 'commit' | 'commitAndPush' | null;
|
||||
@@ -87,7 +81,7 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
className="w-fit"
|
||||
type="button"
|
||||
>
|
||||
<RiEmotionHappyLine className="size-4" />
|
||||
<Icon name="emotion-happy" className="size-4" />
|
||||
{t('gitView.commit.addGitmoji')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -107,9 +101,9 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
className="commit-actions__btn"
|
||||
>
|
||||
{isGeneratingMessage ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiAiGenerate2 className="size-4 text-primary" />
|
||||
<Icon name="ai-generate-2" className="size-4 text-primary" />
|
||||
)}
|
||||
<span className="commit-actions__label">{t('gitView.commit.generate')}</span>
|
||||
</Button>
|
||||
@@ -126,12 +120,12 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
>
|
||||
{commitAction === 'commit' ? (
|
||||
<>
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
<span className="commit-actions__label">{t('gitView.commit.committing')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RiGitCommitLine className="size-4" />
|
||||
<Icon name="git-commit" className="size-4" />
|
||||
<span className="commit-actions__label">{t('gitView.commit.commit')}</span>
|
||||
</>
|
||||
)}
|
||||
@@ -149,9 +143,9 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
aria-label={t('gitView.commit.pushAria')}
|
||||
>
|
||||
{commitAction === 'commitAndPush' ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiArrowUpLine className="size-3.5" />
|
||||
<Icon name="arrow-up" className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
@@ -170,12 +164,12 @@ export const CommitSection: React.FC<CommitSectionProps> = ({
|
||||
>
|
||||
{commitAction === 'commitAndPush' ? (
|
||||
<>
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
<span className="commit-actions__label">{t('gitView.commit.pushing')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RiArrowUpLine className="size-3.5" />
|
||||
<Icon name="arrow-up" className="size-3.5" />
|
||||
<span className="commit-actions__label">{t('gitView.commit.push')}</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
import { RiAlertLine, RiLoader4Line, RiChat1Line, RiAddLine } from '@remixicon/react';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useInputStore } from '@/sync/input-store';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { getConflictDetails, type MergeConflictDetails } from '@/lib/gitApi';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -173,7 +173,7 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
||||
<div className="flex flex-col gap-4 overflow-hidden">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<RiAlertLine className="size-5 shrink-0 text-[var(--status-warning)]" />
|
||||
<Icon name="alert" className="size-5 shrink-0 text-[var(--status-warning)]" />
|
||||
<DialogTitle>{t('gitView.conflict.detectedTitle', { operation: operationLabel })}</DialogTitle>
|
||||
</div>
|
||||
<DialogDescription>
|
||||
@@ -183,7 +183,7 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex items-center justify-center gap-2 py-4 text-muted-foreground">
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
<span className="typography-meta">{t('gitView.conflict.loading')}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -237,9 +237,9 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
||||
className="w-full gap-2"
|
||||
>
|
||||
{isLoading ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiAddLine className="size-4" />
|
||||
<Icon name="add" className="size-4" />
|
||||
)}
|
||||
{t('gitView.conflict.resolveNewSession')}
|
||||
</Button>
|
||||
@@ -250,9 +250,9 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
||||
className="w-full gap-2"
|
||||
>
|
||||
{isLoading ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiChat1Line className="size-4" />
|
||||
<Icon name="chat-1" className="size-4" />
|
||||
)}
|
||||
{t('gitView.conflict.resolveCurrentSession')}
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RiArchiveStackLine, RiGitCommitLine } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface GitEmptyStateProps {
|
||||
@@ -11,7 +11,7 @@ export const GitEmptyState: React.FC<GitEmptyStateProps> = ({ onOpenStashes }) =
|
||||
const { t } = useI18n();
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-10 px-4 text-center">
|
||||
<RiGitCommitLine className="size-10 text-muted-foreground/70 mb-4" />
|
||||
<Icon name="git-commit" className="size-10 text-muted-foreground/70 mb-4" />
|
||||
<p className="typography-ui-label font-semibold text-foreground mb-1">
|
||||
{t('gitView.empty.cleanTitle')}
|
||||
</p>
|
||||
@@ -27,7 +27,7 @@ export const GitEmptyState: React.FC<GitEmptyStateProps> = ({ onOpenStashes }) =
|
||||
onClick={onOpenStashes}
|
||||
className="gap-1.5"
|
||||
>
|
||||
<RiArchiveStackLine className="size-4" />
|
||||
<Icon name="archive-stack" className="size-4" />
|
||||
{t('gitView.stashes.title')}
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
RiCheckLine,
|
||||
RiLoader4Line,
|
||||
RiGitBranchLine,
|
||||
RiBriefcaseLine,
|
||||
RiHomeLine,
|
||||
RiGraduationCapLine,
|
||||
RiCodeLine,
|
||||
RiHeartLine,
|
||||
RiHistoryLine,
|
||||
RiUser3Line,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SortableTabsStrip, type SortableTabsStripItem } from '@/components/ui/sortable-tabs-strip';
|
||||
import {
|
||||
@@ -21,6 +8,8 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { IconName } from "@/components/icon/icons";
|
||||
import { BranchSelector } from './BranchSelector';
|
||||
import { WorktreeBranchDisplay } from './WorktreeBranchDisplay';
|
||||
import { SyncActions } from './SyncActions';
|
||||
@@ -54,17 +43,14 @@ interface GitHeaderProps {
|
||||
onSelectActionTab?: (tabID: string) => void;
|
||||
}
|
||||
|
||||
const IDENTITY_ICON_MAP: Record<
|
||||
string,
|
||||
React.ComponentType<React.ComponentProps<typeof RiGitBranchLine>>
|
||||
> = {
|
||||
branch: RiGitBranchLine,
|
||||
briefcase: RiBriefcaseLine,
|
||||
house: RiHomeLine,
|
||||
graduation: RiGraduationCapLine,
|
||||
code: RiCodeLine,
|
||||
heart: RiHeartLine,
|
||||
user: RiUser3Line,
|
||||
const IDENTITY_ICON_MAP: Record<string, IconName> = {
|
||||
branch: 'git-branch',
|
||||
briefcase: 'briefcase',
|
||||
house: 'home',
|
||||
graduation: 'graduation-cap',
|
||||
code: 'code',
|
||||
heart: 'heart',
|
||||
user: 'user-3',
|
||||
};
|
||||
|
||||
const IDENTITY_COLOR_MAP: Record<string, string> = {
|
||||
@@ -92,9 +78,10 @@ interface IdentityIconProps {
|
||||
}
|
||||
|
||||
const IdentityIcon: React.FC<IdentityIconProps> = ({ icon, className, colorToken }) => {
|
||||
const IconComponent = IDENTITY_ICON_MAP[icon ?? 'branch'] ?? RiUser3Line;
|
||||
const iconName = IDENTITY_ICON_MAP[icon ?? 'branch'] ?? 'user-3';
|
||||
return (
|
||||
<IconComponent
|
||||
<Icon
|
||||
name={iconName}
|
||||
className={className}
|
||||
style={{ color: getIdentityColor(colorToken) }}
|
||||
/>
|
||||
@@ -132,7 +119,7 @@ export const IdentityDropdown: React.FC<IdentityDropdownProps> = ({
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{isApplying ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<IdentityIcon
|
||||
icon={activeProfile?.icon}
|
||||
@@ -145,7 +132,7 @@ export const IdentityDropdown: React.FC<IdentityDropdownProps> = ({
|
||||
{activeProfile?.name || t('gitView.header.noIdentity')}
|
||||
</span>
|
||||
)}
|
||||
<RiArrowDownSLine className="size-4 opacity-60" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-60" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
@@ -178,7 +165,7 @@ export const IdentityDropdown: React.FC<IdentityDropdownProps> = ({
|
||||
</span>
|
||||
</span>
|
||||
{isSelected ? (
|
||||
<RiCheckLine className="ml-auto size-4 text-foreground" />
|
||||
<Icon name="check" className="ml-auto size-4 text-foreground" />
|
||||
) : null}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
@@ -230,7 +217,7 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
||||
className="h-8 w-8 px-0"
|
||||
onClick={onOpenHistory}
|
||||
>
|
||||
<RiHistoryLine className="size-4" />
|
||||
<Icon name="history" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8}>{t('gitView.history.title')}</TooltipContent>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { RiLoader4Line, RiFileCopyLine } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { GitLogEntry, CommitFileEntry } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -99,7 +99,7 @@ export const HistoryCommitRow = React.memo(({
|
||||
onCopyHash(entry.hash);
|
||||
}}
|
||||
>
|
||||
<RiFileCopyLine className="size-3" />
|
||||
<Icon name="file-copy" className="size-3" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8}>{t('gitView.history.copySha')}</TooltipContent>
|
||||
@@ -112,7 +112,7 @@ export const HistoryCommitRow = React.memo(({
|
||||
<div className="px-3 pb-2 pl-8 border-t border-border/40">
|
||||
{isLoadingFiles ? (
|
||||
<div className="flex items-center gap-2 py-2">
|
||||
<RiLoader4Line className="size-4 animate-spin text-muted-foreground" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin text-muted-foreground" />
|
||||
<span className="typography-micro text-muted-foreground">{t('gitView.history.loadingFiles')}</span>
|
||||
</div>
|
||||
) : files.length === 0 ? (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { RiArrowDownSLine, RiArrowUpLine, RiArrowUpSLine } from '@remixicon/react';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { HistoryCommitRow } from './HistoryCommitRow';
|
||||
import type { GitLogEntry, CommitFileEntry } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -78,8 +78,8 @@ export const HistorySection: React.FC<HistorySectionProps> = ({
|
||||
const bottomEntries = hasDivider ? log.all.slice(branchDivider.insertBeforeIndex) : [];
|
||||
|
||||
const dividerIcon = branchDivider?.direction === 'down'
|
||||
? <RiArrowDownSLine className="size-3.5" />
|
||||
: <RiArrowUpLine className="size-3.5" />;
|
||||
? <Icon name="arrow-down-s" className="size-3.5" />
|
||||
: <Icon name="arrow-up" className="size-3.5" />;
|
||||
|
||||
const renderCommitList = (entries: GitLogEntry[]) => (
|
||||
<ul className="divide-y divide-border/60">
|
||||
@@ -182,9 +182,9 @@ export const HistorySection: React.FC<HistorySectionProps> = ({
|
||||
</div>
|
||||
)}
|
||||
{isOpen ? (
|
||||
<RiArrowUpSLine className="size-4 text-muted-foreground" />
|
||||
<Icon name="arrow-up-s" className="size-4 text-muted-foreground" />
|
||||
) : (
|
||||
<RiArrowDownSLine className="size-4 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleTrigger>
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiGitMergeLine,
|
||||
RiGitBranchLine,
|
||||
RiLoader4Line,
|
||||
RiCheckLine,
|
||||
RiCloseLine,
|
||||
RiSparklingLine,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { GitMergeInProgress, GitRebaseInProgress } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -63,7 +56,7 @@ export const InProgressOperationBanner: React.FC<InProgressOperationBannerProps>
|
||||
const isProcessing = processingAction !== null;
|
||||
|
||||
const operationLabel = operation === 'merge' ? t('gitView.operation.merge') : t('gitView.operation.rebase');
|
||||
const OperationIcon = operation === 'merge' ? RiGitMergeLine : RiGitBranchLine;
|
||||
const operationIconName = operation === 'merge' ? 'git-merge' : 'git-branch';
|
||||
|
||||
// Build description
|
||||
let description = '';
|
||||
@@ -81,7 +74,7 @@ export const InProgressOperationBanner: React.FC<InProgressOperationBannerProps>
|
||||
<div className="bg-[var(--status-warning-bg)] border border-[var(--status-warning)] rounded-lg p-3 mx-3 mt-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-start gap-2 min-w-0">
|
||||
<OperationIcon className="size-4 text-[var(--status-warning)] shrink-0" />
|
||||
<Icon name={operationIconName} className="size-4 text-[var(--status-warning)] shrink-0" />
|
||||
<div className="min-w-0">
|
||||
<p className="typography-label text-[var(--status-warning)]">
|
||||
{t('gitView.operation.inProgressTitle', { operation: operationLabel })}
|
||||
@@ -103,7 +96,7 @@ export const InProgressOperationBanner: React.FC<InProgressOperationBannerProps>
|
||||
disabled={isProcessing || isLoading}
|
||||
className="gap-1.5"
|
||||
>
|
||||
<RiSparklingLine className="size-4" />
|
||||
<Icon name="sparkling" className="size-4" />
|
||||
{t('gitView.operation.resolveWithAi')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -117,9 +110,9 @@ export const InProgressOperationBanner: React.FC<InProgressOperationBannerProps>
|
||||
className="gap-1.5"
|
||||
>
|
||||
{processingAction === 'abort' ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiCloseLine className="size-4" />
|
||||
<Icon name="close" className="size-4" />
|
||||
)}
|
||||
{t('gitView.operation.abort')}
|
||||
</Button>
|
||||
@@ -134,9 +127,9 @@ export const InProgressOperationBanner: React.FC<InProgressOperationBannerProps>
|
||||
className="gap-1.5"
|
||||
>
|
||||
{processingAction === 'continue' ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiCheckLine className="size-4" />
|
||||
<Icon name="check" className="size-4" />
|
||||
)}
|
||||
{t('gitView.operation.continue')}
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { RiArrowDownSLine, RiLoader4Line, RiSplitCellsHorizontal, RiSparklingLine } from '@remixicon/react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
CommandList,
|
||||
} from '@/components/ui/command';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useInputStore } from '@/sync/input-store';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
@@ -344,7 +344,7 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
<section className={containerClassName}>
|
||||
<div className={headerClassName}>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<RiSplitCellsHorizontal className="size-4 text-muted-foreground" />
|
||||
<Icon name="split-cells-horizontal" className="size-4 text-muted-foreground" />
|
||||
<h3 className="typography-ui-header font-semibold text-foreground truncate">{t('gitView.integrate.title')}</h3>
|
||||
{ui.kind === 'ready' && ui.plan.commits.length > 0 ? (
|
||||
<span className="typography-meta text-muted-foreground truncate">
|
||||
@@ -354,7 +354,7 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{ui.kind === 'loading' || ui.kind === 'running' ? (
|
||||
<RiLoader4Line className="size-4 animate-spin text-muted-foreground" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin text-muted-foreground" />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@@ -375,7 +375,7 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
<Button variant="outline" size="sm" className="gap-1.5">
|
||||
{t('gitView.integrate.target')}
|
||||
<span className="max-w-[160px] truncate font-mono text-xs text-muted-foreground">{targetBranch}</span>
|
||||
<RiArrowDownSLine className="size-4 opacity-60" />
|
||||
<Icon name="arrow-down-s" className="size-4 opacity-60" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
@@ -495,7 +495,7 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
disabled={!currentSessionId}
|
||||
onClick={() => void handleResolveWithAi({ state: ui.state, details: ui.details }, false)}
|
||||
>
|
||||
<RiSparklingLine className="size-3.5" />
|
||||
<Icon name="sparkling" className="size-3.5" />
|
||||
{t('gitView.integrate.currentSession')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -504,7 +504,7 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
className="typography-meta gap-1"
|
||||
onClick={() => void handleResolveWithAi({ state: ui.state, details: ui.details }, true)}
|
||||
>
|
||||
<RiSparklingLine className="size-3.5" />
|
||||
<Icon name="sparkling" className="size-3.5" />
|
||||
{t('gitView.integrate.newSession')}
|
||||
</Button>
|
||||
<Button size="sm" className="typography-meta" onClick={() => void handleContinue()}>
|
||||
|
||||
@@ -1,23 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiChat4Line,
|
||||
RiCheckLine,
|
||||
RiCheckboxCircleLine,
|
||||
RiAiGenerate2,
|
||||
RiArrowDownSLine,
|
||||
RiArrowRightSLine,
|
||||
RiCloseLine,
|
||||
RiEditLine,
|
||||
RiErrorWarningLine,
|
||||
RiExternalLinkLine,
|
||||
RiGitClosePullRequestLine,
|
||||
RiGitMergeLine,
|
||||
RiGitPrDraftLine,
|
||||
RiGitPullRequestLine,
|
||||
RiInformationLine,
|
||||
RiLoader4Line,
|
||||
RiRefreshLine,
|
||||
} from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -45,6 +26,7 @@ import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
|
||||
import { SimpleMarkdownRenderer } from '@/components/chat/MarkdownRenderer';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useSelectionStore } from '@/sync/selection-store';
|
||||
@@ -770,7 +752,7 @@ export const PullRequestSection: React.FC<{
|
||||
{run.detailsUrl ? (
|
||||
<Button variant="outline" size="sm" asChild className="flex-shrink-0">
|
||||
<a href={run.detailsUrl} target="_blank" rel="noopener noreferrer">
|
||||
<RiExternalLinkLine className="size-4" />
|
||||
<Icon name="external-link" className="size-4" />
|
||||
Open
|
||||
</a>
|
||||
</Button>
|
||||
@@ -859,7 +841,7 @@ export const PullRequestSection: React.FC<{
|
||||
(isFail ? 'bg-destructive/10 text-destructive' : 'text-muted-foreground')
|
||||
}
|
||||
>
|
||||
{stepExpanded ? <RiArrowDownSLine className="size-4" /> : <RiArrowRightSLine className="size-4" />}
|
||||
{stepExpanded ? <Icon name="arrow-down-s" className="size-4" /> : <Icon name="arrow-right-s" className="size-4" />}
|
||||
<span className="truncate">{step.name}</span>
|
||||
{step.conclusion ? <span className="ml-auto flex-shrink-0">{step.conclusion}</span> : null}
|
||||
</button>
|
||||
@@ -1385,13 +1367,13 @@ export const PullRequestSection: React.FC<{
|
||||
const shouldShowConnectionNotice = githubAuthChecked && status?.connected === false;
|
||||
const prVisualState = getPrVisualState(status);
|
||||
const prColorVar = prVisualState ? `var(--pr-${prVisualState})` : 'var(--status-info)';
|
||||
const PrStateIcon = prVisualState === 'draft'
|
||||
? RiGitPrDraftLine
|
||||
const prStateIconName = prVisualState === 'draft'
|
||||
? 'git-pr-draft'
|
||||
: prVisualState === 'merged'
|
||||
? RiGitMergeLine
|
||||
? 'git-merge'
|
||||
: prVisualState === 'closed'
|
||||
? RiGitClosePullRequestLine
|
||||
: RiGitPullRequestLine;
|
||||
? 'git-close-pull-request'
|
||||
: 'git-pull-request';
|
||||
const prStatusText = pr
|
||||
? [
|
||||
`${pr.state}${pr.draft ? ' (draft)' : ''}`,
|
||||
@@ -1424,13 +1406,13 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={() => void openExternal(pr.url)}
|
||||
aria-label={t('gitView.pr.actions.openOnGitHubAria')}
|
||||
>
|
||||
<PrStateIcon className="size-4 shrink-0" style={{ color: prColorVar }} />
|
||||
<Icon name={prStateIconName} className="size-4 shrink-0" style={{ color: prColorVar }} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.openOnGitHub')}</p></TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<PrStateIcon className="size-4 shrink-0" style={{ color: 'var(--surface-muted-foreground)' }} />
|
||||
<Icon name={prStateIconName} className="size-4 shrink-0" style={{ color: 'var(--surface-muted-foreground)' }} />
|
||||
)}
|
||||
<h3 className="typography-ui-header font-semibold text-foreground truncate">{t('gitView.pullRequest.title')}</h3>
|
||||
{pr ? (
|
||||
@@ -1438,7 +1420,7 @@ export const PullRequestSection: React.FC<{
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
{isLoading ? <RiLoader4Line className="size-4 animate-spin text-muted-foreground" /> : null}
|
||||
{isLoading ? <Icon name="loader-4" className="size-4 animate-spin text-muted-foreground" /> : null}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
@@ -1448,7 +1430,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={() => void refresh({ force: true })}
|
||||
aria-label={t('gitView.pr.actions.refreshAria')}
|
||||
>
|
||||
<RiRefreshLine className="size-3.5 text-muted-foreground" />
|
||||
<Icon name="refresh" className="size-3.5 text-muted-foreground" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.refresh')}</p></TooltipContent>
|
||||
@@ -1493,7 +1475,7 @@ export const PullRequestSection: React.FC<{
|
||||
{repoUrl ? (
|
||||
<Button variant="outline" size="sm" asChild className="w-fit">
|
||||
<a href={repoUrl} target="_blank" rel="noopener noreferrer">
|
||||
<RiExternalLinkLine className="size-4" />
|
||||
<Icon name="external-link" className="size-4" />
|
||||
Open Repo
|
||||
</a>
|
||||
</Button>
|
||||
@@ -1503,7 +1485,7 @@ export const PullRequestSection: React.FC<{
|
||||
|
||||
{!pr && !isInitialStatusResolved && !error && !shouldShowConnectionNotice ? (
|
||||
<div className="flex items-center gap-2 typography-micro text-muted-foreground">
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
{t('gitView.pr.checkingStatus')}
|
||||
</div>
|
||||
) : pr ? (
|
||||
@@ -1574,7 +1556,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isUpdating}
|
||||
aria-label={t('gitView.pr.actions.cancelEditingAria')}
|
||||
>
|
||||
<RiCloseLine className="size-4" />
|
||||
<Icon name="close" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.cancelEditing')}</p></TooltipContent>
|
||||
@@ -1588,7 +1570,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isUpdating || !editTitle.trim()}
|
||||
aria-label={t('gitView.pr.actions.savePrAria')}
|
||||
>
|
||||
{isUpdating ? <RiLoader4Line className="size-4 animate-spin" /> : <RiCheckLine className="size-4" />}
|
||||
{isUpdating ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="check" className="size-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.savePr')}</p></TooltipContent>
|
||||
@@ -1604,7 +1586,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={() => setIsEditingPr(true)}
|
||||
aria-label={t('gitView.pr.actions.editPrAria')}
|
||||
>
|
||||
<RiEditLine className="size-4" />
|
||||
<Icon name="edit" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.editPr')}</p></TooltipContent>
|
||||
@@ -1623,7 +1605,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isLoadingCheckDetails}
|
||||
aria-label={t('gitView.pr.actions.openChecksAria')}
|
||||
>
|
||||
{isLoadingCheckDetails ? <RiLoader4Line className="size-4 animate-spin" /> : <RiInformationLine className="size-4" />}
|
||||
{isLoadingCheckDetails ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="information" className="size-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.openChecks')}</p></TooltipContent>
|
||||
@@ -1640,7 +1622,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={sendFailedChecksToChat}
|
||||
aria-label={t('gitView.pr.actions.resolveFailedChecksAria')}
|
||||
>
|
||||
<RiErrorWarningLine className="size-4" />
|
||||
<Icon name="error-warning" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.resolveFailedChecks')}</p></TooltipContent>
|
||||
@@ -1656,7 +1638,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={openCommentsDialog}
|
||||
aria-label={t('gitView.pr.actions.openCommentsAria')}
|
||||
>
|
||||
<RiChat4Line className="size-4" />
|
||||
<Icon name="chat-4" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.openComments')}</p></TooltipContent>
|
||||
@@ -1671,7 +1653,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={sendCommentsToChat}
|
||||
aria-label={t('gitView.pr.actions.shareCommentsAria')}
|
||||
>
|
||||
<RiAiGenerate2 className="size-4" />
|
||||
<Icon name="ai-generate-2" className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.shareComments')}</p></TooltipContent>
|
||||
@@ -1688,7 +1670,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isMarkingReady || isMerging || isUpdating || isEditingPr}
|
||||
aria-label={t('gitView.pr.actions.markReadyAria')}
|
||||
>
|
||||
{isMarkingReady ? <RiLoader4Line className="size-4 animate-spin" /> : <RiCheckboxCircleLine className="size-4" />}
|
||||
{isMarkingReady ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="checkbox-circle" className="size-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.markReady')}</p></TooltipContent>
|
||||
@@ -1722,7 +1704,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isMerging || isMarkingReady || pr.state !== 'open' || pr.draft || isUpdating || isEditingPr}
|
||||
aria-label={t('gitView.pr.actions.mergePrAria')}
|
||||
>
|
||||
{isMerging ? <RiLoader4Line className="size-4 animate-spin" /> : <RiGitMergeLine className="size-4" />}
|
||||
{isMerging ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="git-merge" className="size-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent><p>{t('gitView.pr.actions.mergePr')}</p></TooltipContent>
|
||||
@@ -1745,7 +1727,7 @@ export const PullRequestSection: React.FC<{
|
||||
{repoUrl ? (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={repoUrl} target="_blank" rel="noopener noreferrer">
|
||||
<RiExternalLinkLine className="size-4" />
|
||||
<Icon name="external-link" className="size-4" />
|
||||
{t('gitView.pr.actions.repo')}
|
||||
</a>
|
||||
</Button>
|
||||
@@ -1906,7 +1888,7 @@ export const PullRequestSection: React.FC<{
|
||||
onClick={generateDescription}
|
||||
disabled={isGenerating || isCreating}
|
||||
>
|
||||
{isGenerating ? <RiLoader4Line className="size-4 animate-spin" /> : <RiAiGenerate2 className="size-4 text-primary" />}
|
||||
{isGenerating ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="ai-generate-2" className="size-4 text-primary" />}
|
||||
{t('gitView.commit.generate')}
|
||||
</Button>
|
||||
<div className="flex-1" />
|
||||
@@ -1917,7 +1899,7 @@ export const PullRequestSection: React.FC<{
|
||||
disabled={isCreating || !isConnected || !targetBaseBranch.trim() || (!useDetectedUpstream && targetBaseBranch.trim() === branch)}
|
||||
>
|
||||
<span className="inline-flex size-4 items-center justify-center">
|
||||
{isCreating ? <RiLoader4Line className="size-4 animate-spin" /> : <RiGitPullRequestLine className="size-4" />}
|
||||
{isCreating ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="git-pull-request" className="size-4" />}
|
||||
</span>
|
||||
<span>{t('gitView.pr.actions.createPr')}</span>
|
||||
</Button>
|
||||
@@ -1930,7 +1912,7 @@ export const PullRequestSection: React.FC<{
|
||||
<DialogContent className="max-w-2xl max-h-[70vh] flex flex-col min-h-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<RiGitPullRequestLine className="h-5 w-5" />
|
||||
<Icon name="git-pull-request" className="h-5 w-5" />
|
||||
{t('gitView.pr.checkDetails.title')}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
@@ -1941,7 +1923,7 @@ export const PullRequestSection: React.FC<{
|
||||
<div className="flex-1 min-h-0 overflow-y-auto mt-2">
|
||||
{isLoadingCheckDetails ? (
|
||||
<div className="text-center text-muted-foreground py-8 flex items-center justify-center gap-2">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('gitView.loading.loading')}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -1971,7 +1953,7 @@ export const PullRequestSection: React.FC<{
|
||||
<DialogContent className="max-w-2xl max-h-[82vh] min-h-[38rem] flex flex-col gap-2">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<RiGitPullRequestLine className="h-5 w-5" />
|
||||
<Icon name="git-pull-request" className="h-5 w-5" />
|
||||
{t('gitView.pr.comments.title')}
|
||||
{pr ? (
|
||||
<span className="typography-meta text-muted-foreground">{t('gitView.pr.numberLabel', { number: pr.number })}</span>
|
||||
@@ -1982,7 +1964,7 @@ export const PullRequestSection: React.FC<{
|
||||
<ScrollShadow className="mt-2 max-h-[66vh] overflow-y-auto overlay-scrollbar-target overlay-scrollbar-container">
|
||||
{isLoadingCommentsDetails ? (
|
||||
<div className="text-center text-muted-foreground py-8 flex items-center justify-center gap-2">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('gitView.loading.loading')}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -2023,7 +2005,7 @@ export const PullRequestSection: React.FC<{
|
||||
}}
|
||||
aria-label={t('gitView.pr.actions.sendCommentToAgentAria')}
|
||||
>
|
||||
<RiAiGenerate2 className="size-3.5" />
|
||||
<Icon name="ai-generate-2" className="size-3.5" />
|
||||
{t('gitView.pr.actions.sendToAgent')}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from '@/components/ui';
|
||||
import { RiAlertLine, RiLoader4Line } from '@remixicon/react';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface StashDialogProps {
|
||||
@@ -59,7 +59,7 @@ export const StashDialog: React.FC<StashDialogProps> = ({
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<RiAlertLine className="size-5 text-[var(--status-warning)]" />
|
||||
<Icon name="alert" className="size-5 text-[var(--status-warning)]" />
|
||||
<DialogTitle>{t('gitView.stash.title')}</DialogTitle>
|
||||
</div>
|
||||
<DialogDescription>
|
||||
@@ -115,7 +115,7 @@ export const StashDialog: React.FC<StashDialogProps> = ({
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
{t('gitView.common.processing')}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { RiArchiveStackLine, RiDeleteBinLine, RiInboxArchiveLine, RiInboxUnarchiveFill, RiInboxUnarchiveLine, RiLoader4Line, RiSearchLine } from '@remixicon/react';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { GitStashEntry } from '@/lib/api/types';
|
||||
@@ -124,7 +124,7 @@ export const StashesDialog: React.FC<StashesDialogProps> = ({
|
||||
<DialogContent className="max-w-2xl max-h-[70vh] flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<RiArchiveStackLine className="h-5 w-5" />
|
||||
<Icon name="archive-stack" className="h-5 w-5" />
|
||||
{t('gitView.stashes.title')}
|
||||
</DialogTitle>
|
||||
<DialogDescription>{t('gitView.stashes.description')}</DialogDescription>
|
||||
@@ -140,7 +140,7 @@ export const StashesDialog: React.FC<StashesDialogProps> = ({
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button onClick={handleCreate} disabled={!hasUncommittedChanges || isOperating || !directory}>
|
||||
{operation === 'create' ? <RiLoader4Line className="size-4 animate-spin" /> : <RiInboxArchiveLine className="size-4" />}
|
||||
{operation === 'create' ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="inbox-archive" className="size-4" />}
|
||||
{t('gitView.stashes.actions.stashCurrentWithCount', { count: uncommittedFileCount })}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -148,13 +148,13 @@ export const StashesDialog: React.FC<StashesDialogProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="relative mt-2">
|
||||
<RiSearchLine className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Icon name="search" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={t('gitView.stashes.searchPlaceholder')} className="pl-9" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-10 text-muted-foreground"><RiLoader4Line className="size-5 animate-spin" /></div>
|
||||
<div className="flex items-center justify-center py-10 text-muted-foreground"><Icon name="loader-4" className="size-5 animate-spin" /></div>
|
||||
) : filtered.length === 0 ? (
|
||||
<div className="py-8 text-center text-muted-foreground">{query ? t('gitView.stashes.empty.search') : t('gitView.stashes.empty.list')}</div>
|
||||
) : filtered.map((stash, index) => (
|
||||
@@ -172,9 +172,9 @@ export const StashesDialog: React.FC<StashesDialogProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
<div className="mr-2 flex shrink-0 items-center gap-1 opacity-100 sm:opacity-0 sm:group-hover:opacity-100">
|
||||
<StashIconButton label={t('gitView.stashes.actions.apply')} loading={operation === `apply:${stash.ref}`} onClick={() => runStashAction(stash, 'apply')} disabled={isOperating}><RiInboxUnarchiveLine className="size-4" /></StashIconButton>
|
||||
<StashIconButton label={t('gitView.stashes.actions.pop')} loading={operation === `pop:${stash.ref}`} onClick={() => runStashAction(stash, 'pop')} disabled={isOperating}><RiInboxUnarchiveFill className="size-4" /></StashIconButton>
|
||||
<StashIconButton label={t('gitView.stashes.actions.drop')} loading={operation === `drop:${stash.ref}`} onClick={() => runStashAction(stash, 'drop')} disabled={isOperating} destructive><RiDeleteBinLine className="size-4" /></StashIconButton>
|
||||
<StashIconButton label={t('gitView.stashes.actions.apply')} loading={operation === `apply:${stash.ref}`} onClick={() => runStashAction(stash, 'apply')} disabled={isOperating}><Icon name="inbox-unarchive" className="size-4" /></StashIconButton>
|
||||
<StashIconButton label={t('gitView.stashes.actions.pop')} loading={operation === `pop:${stash.ref}`} onClick={() => runStashAction(stash, 'pop')} disabled={isOperating}><Icon name="inbox-unarchive-fill" className="size-4" /></StashIconButton>
|
||||
<StashIconButton label={t('gitView.stashes.actions.drop')} loading={operation === `drop:${stash.ref}`} onClick={() => runStashAction(stash, 'drop')} disabled={isOperating} destructive><Icon name="delete-bin" className="size-4" /></StashIconButton>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -188,7 +188,7 @@ const StashIconButton: React.FC<{ label: string; loading: boolean; disabled: boo
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button type="button" aria-label={label} aria-busy={loading || undefined} className={cn('flex h-6 w-6 items-center justify-center transition-colors disabled:opacity-50', destructive ? 'text-[var(--status-error)] hover:text-[var(--status-error)]' : 'text-muted-foreground hover:text-foreground')} onClick={onClick} disabled={disabled}>
|
||||
{loading ? <RiLoader4Line className="size-4 animate-spin" /> : children}
|
||||
{loading ? <Icon name="loader-4" className="size-4 animate-spin" /> : children}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={6}>{label}</TooltipContent>
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
RiCloseLine,
|
||||
RiLoader4Line,
|
||||
RiRefreshLine,
|
||||
} from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import {
|
||||
@@ -13,6 +7,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { GitRemote } from '@/lib/gitApi';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -90,9 +85,9 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
|
||||
aria-label={t('gitView.sync.syncChanges')}
|
||||
>
|
||||
{syncAction === 'sync' ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiRefreshLine className="size-4" />
|
||||
<Icon name="refresh" className="size-4" />
|
||||
)}
|
||||
<span className="whitespace-nowrap tabular-nums">{primaryLabel}</span>
|
||||
</button>
|
||||
@@ -111,7 +106,7 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
|
||||
disabled={isDropdownDisabled}
|
||||
aria-label={t('gitView.sync.moreActionsAria')}
|
||||
>
|
||||
<RiArrowDownSLine className="size-4" />
|
||||
<Icon name="arrow-down-s" className="size-4" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" alignOffset={-40} className="w-[min(360px,calc(100vw-2rem))] max-h-[320px] overflow-y-auto">
|
||||
@@ -128,7 +123,7 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<RiRefreshLine className="size-4 text-muted-foreground" />
|
||||
<Icon name="refresh" className="size-4 text-muted-foreground" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-col">
|
||||
<span className="typography-ui-label text-foreground">
|
||||
@@ -161,9 +156,9 @@ export const SyncActions: React.FC<SyncActionsProps> = ({
|
||||
title={t('gitView.header.removeRemoteTitle', { name: remote.name })}
|
||||
>
|
||||
{removingRemoteName === remote.name ? (
|
||||
<RiLoader4Line className="size-3.5 animate-spin" />
|
||||
<Icon name="loader-4" className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<RiCloseLine className="size-3.5" />
|
||||
<Icon name="close" className="size-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RiGitBranchLine, RiEditLine, RiCheckLine, RiCloseLine, RiLoader4Line } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface WorktreeBranchDisplayProps {
|
||||
@@ -86,7 +86,7 @@ export const WorktreeBranchDisplay: React.FC<WorktreeBranchDisplayProps> = ({
|
||||
handleSaveEdit();
|
||||
}}
|
||||
>
|
||||
<RiGitBranchLine className="size-4 text-primary" />
|
||||
<Icon name="git-branch" className="size-4 text-primary" />
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={editBranchName}
|
||||
@@ -104,9 +104,9 @@ export const WorktreeBranchDisplay: React.FC<WorktreeBranchDisplayProps> = ({
|
||||
className="shrink-0 text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
{isRenaming ? (
|
||||
<RiLoader4Line className="size-4 animate-spin" />
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
) : (
|
||||
<RiCheckLine className="size-4" />
|
||||
<Icon name="check" className="size-4" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
@@ -117,7 +117,7 @@ export const WorktreeBranchDisplay: React.FC<WorktreeBranchDisplayProps> = ({
|
||||
title={t('gitView.branch.renameCancel')}
|
||||
className="shrink-0 text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
<RiCloseLine className="size-4" />
|
||||
<Icon name="close" className="size-4" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -126,7 +126,7 @@ export const WorktreeBranchDisplay: React.FC<WorktreeBranchDisplayProps> = ({
|
||||
|
||||
return (
|
||||
<div className="flex w-full min-w-0 items-center gap-1.5 px-2 py-1 h-8">
|
||||
<RiGitBranchLine className="size-4 text-primary shrink-0" />
|
||||
<Icon name="git-branch" className="size-4 text-primary shrink-0" />
|
||||
<div className="inline-flex min-w-0 max-w-full items-center gap-1">
|
||||
<span className="truncate typography-ui-label font-normal text-foreground">
|
||||
{currentBranch || t('gitView.branch.detachedHead')}
|
||||
@@ -139,7 +139,7 @@ export const WorktreeBranchDisplay: React.FC<WorktreeBranchDisplayProps> = ({
|
||||
onClick={handleStartEdit}
|
||||
title={t('gitView.branch.renameTitle')}
|
||||
>
|
||||
<RiEditLine className="size-4" />
|
||||
<Icon name="edit" className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user