Files
orca/src/renderer/src/components/editor/EditorPanelHeader.tsx
T
Jinjing 22263a23b0 Open check run details in an editor tab instead of a modal (#5447)
* Open check run full details in an editor tab instead of a modal

- Display check run output, annotations, and job lists within the main editor workspace.
- Replace the right-sidebar checks panel detail dialog with a tab-opening action.
- Suppress local file-system actions (like path copying and revealing) on virtual check-run detail tabs.

* Extract path and rename UI to EditorPanelHeaderPath

- Offloads path rendering, renaming, and context-menu behaviors from
  EditorPanelHeader into a new dedicated component.
- Improves code maintainability and readability by decoupling distinct
  responsibilities within the editor panel headers.
2026-06-15 20:24:28 -07:00

259 lines
9.8 KiB
TypeScript

import { useMemo } from 'react'
import { Columns2, Eye, FileText, ListTree, Rows2 } from 'lucide-react'
import { useAppStore } from '@/store'
import type { MarkdownViewMode, OpenFile } from '@/store/slices/editor'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import EditorViewToggle, {
CSV_VIEW_MODE_METADATA,
NOTEBOOK_VIEW_MODE_METADATA
} from './EditorViewToggle'
import type { EditorToggleValue } from './EditorViewToggle'
import type { EditorHeaderOpenFileState } from './editor-header'
import { DiffNotesSendMenu } from './DiffNotesSendMenu'
import { EditorPanelMarkdownActionsMenu } from './EditorPanelMarkdownActionsMenu'
import { translate } from '@/i18n/i18n'
import { EditorPanelHeaderPath } from './EditorPanelHeaderPath'
type EditorPanelHeaderProps = {
activeFile: OpenFile
copiedPathVisible: boolean
isSingleDiff: boolean
isDiffSurface: boolean
isMarkdown: boolean
isCsv: boolean
isNotebook: boolean
hasEditorToggle: boolean
availableEditorToggleModes: readonly EditorToggleValue[]
effectiveToggleValue: EditorToggleValue
mdViewMode: MarkdownViewMode
hasViewModeToggle: boolean
canOpenPreviewToSide: boolean
canShowMarkdownPreview: boolean
canShowMarkdownTableOfContents: boolean
isMarkdownTableOfContentsDisabled: boolean
showMarkdownTableOfContents: boolean
canShowMarkdownFrontmatterToggle: boolean
markdownFrontmatterVisible: boolean
sideBySide: boolean
openFileState: EditorHeaderOpenFileState
onCopyPath: () => void
onOpenDiffTargetFile: (preferredMarkdownViewMode?: 'rich') => void
onOpenPreviewToSide: () => void
onOpenMarkdownPreview: () => void
onOpenContainingFolder: () => void
onToggleSideBySide: () => void
onEditorToggleChange: (next: EditorToggleValue) => void
onToggleMarkdownTableOfContents: () => void
onToggleMarkdownFrontmatter: () => void
onExportMarkdownToPdf: () => void
}
export function EditorPanelHeader({
activeFile,
copiedPathVisible,
isSingleDiff,
isDiffSurface,
isMarkdown,
isCsv,
isNotebook,
hasEditorToggle,
availableEditorToggleModes,
effectiveToggleValue,
mdViewMode,
hasViewModeToggle,
canOpenPreviewToSide,
canShowMarkdownPreview,
canShowMarkdownTableOfContents,
isMarkdownTableOfContentsDisabled,
showMarkdownTableOfContents,
canShowMarkdownFrontmatterToggle,
markdownFrontmatterVisible,
sideBySide,
openFileState,
onCopyPath,
onOpenDiffTargetFile,
onOpenPreviewToSide,
onOpenMarkdownPreview,
onOpenContainingFolder,
onToggleSideBySide,
onEditorToggleChange,
onToggleMarkdownTableOfContents,
onToggleMarkdownFrontmatter,
onExportMarkdownToPdf
}: EditorPanelHeaderProps): React.JSX.Element {
const diffComments = useAppStore((s) => s.getDiffComments(activeFile.worktreeId))
const activeGroupId = useAppStore((s) => s.activeGroupIdByWorktree[activeFile.worktreeId])
const fileDiffComments = useMemo(
() => diffComments.filter((comment) => comment.filePath === activeFile.relativePath),
[activeFile.relativePath, diffComments]
)
return (
<div className="editor-header">
<EditorPanelHeaderPath
activeFile={activeFile}
copiedPathVisible={copiedPathVisible}
canShowMarkdownPreview={canShowMarkdownPreview}
onCopyPath={onCopyPath}
onOpenMarkdownPreview={onOpenMarkdownPreview}
onOpenContainingFolder={onOpenContainingFolder}
/>
{isSingleDiff && (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
className="p-1 rounded hover:bg-accent text-muted-foreground hover:text-foreground transition-colors flex-shrink-0 disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-muted-foreground"
onClick={() => onOpenDiffTargetFile(isMarkdown ? 'rich' : undefined)}
aria-label={translate(
'auto.components.editor.EditorPanelHeader.a10d9b8337',
'Open file'
)}
disabled={!openFileState.canOpen}
>
<FileText size={14} />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}>
{openFileState.canOpen
? isMarkdown
? translate(
'auto.components.editor.EditorPanelHeader.f0fd4174b5',
'Open file tab to use rich markdown editing'
)
: translate(
'auto.components.editor.EditorPanelHeader.9b80bbe1de',
'Open file tab'
)
: translate(
'auto.components.editor.EditorPanelHeader.c98ce191da',
'This diff has no modified-side file to open'
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{isSingleDiff && fileDiffComments.length > 0 && (
<DiffNotesSendMenu
worktreeId={activeFile.worktreeId}
groupId={activeGroupId ?? activeFile.worktreeId}
comments={diffComments}
filePath={activeFile.relativePath}
showFileScope
triggerLabel="AI notes"
triggerCount={fileDiffComments.length}
triggerClassName="h-6 shrink-0 gap-1 rounded-full border border-border/70 bg-muted/40 px-2 text-[11px] font-medium leading-none text-foreground/80 hover:bg-accent hover:text-foreground"
iconClassName="size-3"
/>
)}
{canOpenPreviewToSide && (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
className="p-1 rounded hover:bg-accent text-muted-foreground hover:text-foreground transition-colors flex-shrink-0"
onClick={onOpenPreviewToSide}
aria-label={translate(
'auto.components.editor.EditorPanelHeader.fb8331694e',
'Open Preview to the Side'
)}
>
<Eye size={14} />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}>
{translate(
'auto.components.editor.EditorPanelHeader.fb8331694e',
'Open Preview to the Side'
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{isDiffSurface && (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
className="p-1 rounded hover:bg-accent text-muted-foreground hover:text-foreground transition-colors flex-shrink-0"
onClick={onToggleSideBySide}
>
{sideBySide ? <Rows2 size={14} /> : <Columns2 size={14} />}
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}>
{sideBySide
? translate(
'auto.components.editor.EditorPanelHeader.94756f08ba',
'Switch to inline diff'
)
: translate(
'auto.components.editor.EditorPanelHeader.e836faacfa',
'Switch to side-by-side diff'
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{hasEditorToggle && (
<EditorViewToggle
value={effectiveToggleValue}
modes={availableEditorToggleModes}
onChange={onEditorToggleChange}
metadataOverride={
isCsv ? CSV_VIEW_MODE_METADATA : isNotebook ? NOTEBOOK_VIEW_MODE_METADATA : undefined
}
/>
)}
{canShowMarkdownTableOfContents && (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
className={`p-1 rounded hover:bg-accent hover:text-foreground transition-colors flex-shrink-0 disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-muted-foreground ${
showMarkdownTableOfContents && !isMarkdownTableOfContentsDisabled
? 'bg-accent text-foreground'
: 'text-muted-foreground'
}`}
onClick={onToggleMarkdownTableOfContents}
disabled={isMarkdownTableOfContentsDisabled}
aria-label={translate(
'auto.components.editor.EditorPanelHeader.5447c4f68f',
'Table of Contents'
)}
aria-pressed={showMarkdownTableOfContents}
>
<ListTree size={14} />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}>
{isMarkdownTableOfContentsDisabled
? translate(
'auto.components.editor.EditorPanelHeader.146cb5473c',
'Table of Contents is available in rich or preview mode'
)
: translate(
'auto.components.editor.EditorPanelHeader.5447c4f68f',
'Table of Contents'
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<EditorPanelMarkdownActionsMenu
isMarkdown={isMarkdown}
hasViewModeToggle={hasViewModeToggle}
mdViewMode={mdViewMode}
canShowMarkdownFrontmatterToggle={canShowMarkdownFrontmatterToggle}
markdownFrontmatterVisible={markdownFrontmatterVisible}
onToggleMarkdownFrontmatter={onToggleMarkdownFrontmatter}
onExportMarkdownToPdf={onExportMarkdownToPdf}
/>
</div>
)
}