Files
openchamber/packages/ui/src/components/comments/InlineCommentCard.tsx
T
Bohdan Triapitsyn 7d7285655d Add i18n foundation and translations (#1027)
* feat: add i18n foundation

* feat: localize sessions sidebar

* Localize multirun/scheduled tasks and fix dialog dropdown interactions

* localize git sidebar surface and add zh-CN keys

* feat(ui): localize context panel, diff/plan views, and context sidebar content

* fix(config): resolve user config home via fs/home before embedded home

* localize header/chat UI and complete model/worktree panel strings

* localize worktree + github issue/pr dialog flows

* localize settings sections and split settings i18n dictionaries

* localize additional settings sections and sidebars

* localize more settings pages and dialogs

* fix settings select trigger localization

* localize tunnel settings ui surface

* localize additional settings sections

* localize keyboard shortcuts labels in settings

* localize terminal and utility dialogs surfaces

* feat(i18n): localize remaining UI strings

* Add Ukrainian locale

* Add Spanish locale

* Add Brazilian Portuguese locale

* Polish locale translations
2026-04-26 14:03:39 +03:00

123 lines
4.6 KiB
TypeScript

import React, { useState } from 'react';
import { RiMoreLine, RiDeleteBinLine, RiEditLine, RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react';
import type { InlineCommentDraft } from '@/stores/useInlineCommentDraftStore';
import { useOptionalThemeSystem } from '@/contexts/useThemeSystem';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { useI18n } from '@/lib/i18n';
interface InlineCommentCardProps {
draft: InlineCommentDraft;
onEdit: () => void;
onDelete: () => void;
className?: string;
maxWidth?: number;
}
export function InlineCommentCard({
draft,
onEdit,
onDelete,
className,
maxWidth,
}: InlineCommentCardProps) {
const { t } = useI18n();
const themeContext = useOptionalThemeSystem();
const currentTheme = themeContext?.currentTheme;
const [isOpen, setIsOpen] = useState(false);
const draftText = typeof draft.text === 'string' ? draft.text : '';
// Check if content is long enough to warrant collapsing (rough estimate)
// In a real app we might measure line height, but length check is a good proxy for now
const isLongContent = draftText.length > 150 || draftText.split('\n').length > 3;
return (
<div
className={cn(
"rounded-lg border shadow-none w-full max-w-[min(100%,calc(var(--oc-context-panel-width,100vw)-var(--oc-editor-gutter-width,0px)))] overflow-hidden transition-all duration-200",
className
)}
style={{
backgroundColor: currentTheme?.colors?.surface?.elevated,
borderColor: currentTheme?.colors?.interactive?.border,
maxWidth: maxWidth ? `${Math.max(200, Math.floor(maxWidth))}px` : undefined,
}}
data-comment-card="true"
>
<div className="flex items-start justify-between gap-2 p-3">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground mb-1.5">
<span className="truncate max-w-[200px]" title={draft.fileLabel}>
{draft.fileLabel}
</span>
<span></span>
<span>{t('inlineComment.range.lines', { start: draft.startLine, end: draft.endLine })}</span>
{draft.side && <span>({draft.side})</span>}
</div>
<Collapsible open={isOpen || !isLongContent} onOpenChange={setIsOpen}>
<div className={cn("text-sm whitespace-pre-wrap break-words leading-relaxed", !isOpen && isLongContent && "line-clamp-3")}>
{draftText}
</div>
{isLongContent && (
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-6 px-0 mt-1 text-xs text-muted-foreground hover:text-foreground w-full justify-start"
>
{isOpen ? (
<>
<RiArrowUpSLine className="size-3 mr-1" />
{t('inlineComment.actions.showLess')}
</>
) : (
<>
<RiArrowDownSLine className="size-3 mr-1" />
{t('inlineComment.actions.showMore')}
</>
)}
</Button>
</CollapsibleTrigger>
)}
<CollapsibleContent>
{/* Used for animation purposes if we want to animate height */}
</CollapsibleContent>
</Collapsible>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 -mr-1 text-muted-foreground hover:text-foreground"
>
<RiMoreLine className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={onEdit}>
<RiEditLine className="size-4 mr-2" />
{t('inlineComment.actions.editComment')}
</DropdownMenuItem>
<DropdownMenuItem onClick={onDelete} className="text-destructive">
<RiDeleteBinLine className="size-4 mr-2" />
{t('inlineComment.actions.deleteComment')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
}