Files
openchamber/packages/ui/src/components/views/git/CommitSection.tsx
T
Paolo InsognaandBohdan Triapitsyn e16097b05d feat: Redesign git changes to split stage/unstaged files. (#1359)
* feat: Redesign git changes to split stage/unstaged files.

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* fixup

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* fixup

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* refactor: streamline git changes panel

* fix: label staged and working diff tabs

* fix: isolate staged and working diff files

* fix: scope staged and working diff updates

* fix: scope git row revert to working changes

---------

Signed-off-by: Paolo Insogna <paolo@cowtech.it>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-24 00:49:38 +03:00

185 lines
6.2 KiB
TypeScript

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;
interface CommitSectionProps {
stagedCount: number;
commitMessage: string;
onCommitMessageChange: (value: string) => void;
generatedHighlights: string[];
onInsertHighlights: (highlights: string[]) => void;
onGenerateMessage: () => void;
isGeneratingMessage: boolean;
onCommit: () => void;
onCommitAndPush: () => void;
commitAction: CommitAction;
hasPendingIndexMutation?: boolean;
gitmojiEnabled: boolean;
onOpenGitmojiPicker: () => void;
}
export const CommitSection: React.FC<CommitSectionProps> = ({
stagedCount,
commitMessage,
onCommitMessageChange,
generatedHighlights,
onInsertHighlights,
onGenerateMessage,
isGeneratingMessage,
onCommit,
onCommitAndPush,
commitAction,
hasPendingIndexMutation = false,
gitmojiEnabled,
onOpenGitmojiPicker,
}) => {
const { t } = useI18n();
const hasStagedFiles = stagedCount > 0;
const canCommit = commitMessage.trim() && hasStagedFiles && commitAction === null && !hasPendingIndexMutation;
const { isMobile, hasTouchInput } = useDeviceInfo();
const containerClassName = 'border-0 bg-transparent rounded-none';
const headerClassName = 'flex w-full items-baseline gap-2 px-0 pt-2 pb-1';
const contentClassName = 'flex flex-col gap-3 px-0 pt-1 pb-3';
return (
<section className={containerClassName}>
<div className={headerClassName}>
<h3 className="typography-ui-header font-semibold text-foreground">{t('gitView.commit.title')}</h3>
{!hasStagedFiles ? (
<span className="min-w-0 truncate typography-meta text-muted-foreground">
{t('gitView.commit.stageFilesHint')}
</span>
) : null}
</div>
<div className={contentClassName}>
<AIHighlightsBox
highlights={generatedHighlights}
onInsert={onInsertHighlights}
/>
<CommitInput
value={commitMessage}
onChange={onCommitMessageChange}
placeholder={t('gitView.commit.messagePlaceholder')}
disabled={commitAction !== null}
hasTouchInput={hasTouchInput}
isMobile={isMobile}
/>
{gitmojiEnabled && (
<Button
variant="outline"
size="sm"
onClick={onOpenGitmojiPicker}
className="w-fit"
type="button"
>
<Icon name="emotion-happy" className="size-4" />
{t('gitView.commit.addGitmoji')}
</Button>
)}
<div className="@container/commit-actions flex items-center gap-2 min-w-0">
<Button
variant="outline"
size="sm"
onClick={onGenerateMessage}
disabled={
isGeneratingMessage ||
commitAction !== null ||
hasPendingIndexMutation ||
stagedCount === 0
}
type="button"
aria-label={t('gitView.commit.generateAria')}
className="commit-actions__btn"
>
{isGeneratingMessage ? (
<Icon name="loader-4" className="size-4 animate-spin" />
) : (
<Icon name="ai-generate-2" className="size-4 text-primary" />
)}
<span className="commit-actions__label">{t('gitView.commit.generate')}</span>
</Button>
<div className="flex-1" />
<Button
size="sm"
variant="outline"
onClick={onCommit}
disabled={!canCommit || isGeneratingMessage}
className="commit-actions__btn whitespace-nowrap"
aria-label={t('gitView.commit.commitAria')}
>
{commitAction === 'commit' ? (
<>
<Icon name="loader-4" className="size-4 animate-spin" />
<span className="commit-actions__label">{t('gitView.commit.committing')}</span>
</>
) : (
<>
<Icon name="git-commit" className="size-4" />
<span className="commit-actions__label">{t('gitView.commit.commit')}</span>
</>
)}
</Button>
{isMobile ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
size="sm"
onClick={() => onCommitAndPush()}
disabled={!canCommit || isGeneratingMessage}
className="h-7 w-7 p-0"
aria-label={t('gitView.commit.pushAria')}
>
{commitAction === 'commitAndPush' ? (
<Icon name="loader-4" className="size-4 animate-spin" />
) : (
<Icon name="arrow-up" className="size-3.5" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="top">
<p>{t('gitView.commit.push')}</p>
</TooltipContent>
</Tooltip>
) : (
<Button
size="sm"
variant="default"
onClick={() => onCommitAndPush()}
disabled={!canCommit || isGeneratingMessage}
className="commit-actions__btn"
aria-label={t('gitView.commit.pushAria')}
>
{commitAction === 'commitAndPush' ? (
<>
<Icon name="loader-4" className="size-4 animate-spin" />
<span className="commit-actions__label commit-actions__label--push">{t('gitView.commit.pushing')}</span>
</>
) : (
<>
<Icon name="arrow-up" className="size-3.5" />
<span className="commit-actions__label commit-actions__label--push">{t('gitView.commit.push')}</span>
</>
)}
</Button>
)}
</div>
</div>
</section>
);
};