Add HTML preview-to-side support for single unstaged/staged diffs (#8018)
- Move the preview button before the single-diff tooltip in the header - Extend canOpenPreviewToSide to allow single diffs (not commit diffs) when the modified file still exists on disk, since the preview renders the working-tree file rather than diff content - Add tests covering HTML edit tabs, unstaged diffs, deleted files, commit diffs, and non-HTML diffs
This commit is contained in:
parent
79abeeaa2a
commit
008ad62d3f
|
|
@ -100,6 +100,31 @@ export function EditorPanelHeader({
|
|||
onOpenMarkdownPreview={onOpenMarkdownPreview}
|
||||
onOpenContainingFolder={onOpenContainingFolder}
|
||||
/>
|
||||
{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>
|
||||
)}
|
||||
{isSingleDiff && (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<Tooltip>
|
||||
|
|
@ -149,31 +174,6 @@ export function EditorPanelHeader({
|
|||
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>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import type { OpenFile } from '@/store/slices/editor'
|
||||
import { RICH_MARKDOWN_MAX_SIZE_BYTES } from '../../../../shared/constants'
|
||||
import type { GitStatusEntry } from '../../../../shared/types'
|
||||
import type { FileContent } from './editor-panel-content-types'
|
||||
import { getEditorPanelRenderModel } from './editor-panel-render-model'
|
||||
|
||||
|
|
@ -31,18 +32,82 @@ function renderModel(args: {
|
|||
editorDrafts?: Record<string, string>
|
||||
markdownViewMode?: Record<string, 'source' | 'rich' | 'preview'>
|
||||
isChangesMode?: boolean
|
||||
gitStatusByWorktree?: Record<string, GitStatusEntry[]>
|
||||
}) {
|
||||
return getEditorPanelRenderModel({
|
||||
activeFile: args.activeFile ?? markdownFile(),
|
||||
fileContents: args.fileContents ?? { '/repo/README.md': textContent() },
|
||||
editorDrafts: args.editorDrafts ?? {},
|
||||
gitStatusByWorktree: {},
|
||||
gitStatusByWorktree: args.gitStatusByWorktree ?? {},
|
||||
gitBranchChangesByWorktree: {},
|
||||
markdownViewMode: args.markdownViewMode ?? {},
|
||||
isChangesMode: args.isChangesMode ?? false
|
||||
})
|
||||
}
|
||||
|
||||
function htmlFile(overrides: Partial<OpenFile> = {}): OpenFile {
|
||||
return {
|
||||
id: '/repo/mock.html',
|
||||
filePath: '/repo/mock.html',
|
||||
relativePath: 'mock.html',
|
||||
worktreeId: 'wt-1',
|
||||
language: 'html',
|
||||
mode: 'edit',
|
||||
isDirty: false,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('getEditorPanelRenderModel HTML preview affordance', () => {
|
||||
it('enables preview for HTML edit tabs', () => {
|
||||
expect(renderModel({ activeFile: htmlFile(), fileContents: {} }).canOpenPreviewToSide).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('enables preview for single HTML diffs whose file exists on disk', () => {
|
||||
const model = renderModel({
|
||||
activeFile: htmlFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
||||
fileContents: {},
|
||||
gitStatusByWorktree: {
|
||||
'wt-1': [{ path: 'mock.html', status: 'modified', area: 'unstaged' }]
|
||||
}
|
||||
})
|
||||
|
||||
expect(model.canOpenPreviewToSide).toBe(true)
|
||||
})
|
||||
|
||||
it('disables preview for diffs of deleted HTML files', () => {
|
||||
const model = renderModel({
|
||||
activeFile: htmlFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
||||
fileContents: {},
|
||||
gitStatusByWorktree: {
|
||||
'wt-1': [{ path: 'mock.html', status: 'deleted', area: 'unstaged' }]
|
||||
}
|
||||
})
|
||||
|
||||
expect(model.canOpenPreviewToSide).toBe(false)
|
||||
})
|
||||
|
||||
it('disables preview for commit diffs whose content may not match disk', () => {
|
||||
const model = renderModel({
|
||||
activeFile: htmlFile({ mode: 'diff', diffSource: 'commit' } as Partial<OpenFile>),
|
||||
fileContents: {}
|
||||
})
|
||||
|
||||
expect(model.canOpenPreviewToSide).toBe(false)
|
||||
})
|
||||
|
||||
it('disables preview for non-HTML diffs', () => {
|
||||
const model = renderModel({
|
||||
activeFile: markdownFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
||||
fileContents: {}
|
||||
})
|
||||
|
||||
expect(model.canOpenPreviewToSide).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getEditorPanelRenderModel markdown export affordance', () => {
|
||||
it('enables export for rendered markdown edit tabs', () => {
|
||||
expect(renderModel({}).canExportMarkdownToPdf).toBe(true)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ export function getEditorPanelRenderModel({
|
|||
activeFile.mode === 'diff' && activeFile.diffSource === 'branch'
|
||||
? (branchEntries.find((entry) => entry.path === activeFile.relativePath) ?? null)
|
||||
: null
|
||||
const openFileState = getEditorHeaderOpenFileState(
|
||||
activeFile,
|
||||
matchingWorktreeEntry,
|
||||
matchingBranchEntry
|
||||
)
|
||||
const markdownViewModes = getMarkdownViewModes({
|
||||
language: resolvedLanguage,
|
||||
mode: activeFile.mode,
|
||||
|
|
@ -141,16 +146,17 @@ export function getEditorPanelRenderModel({
|
|||
isCombinedDiff,
|
||||
worktreeEntries,
|
||||
resolvedLanguage,
|
||||
openFileState: getEditorHeaderOpenFileState(
|
||||
activeFile,
|
||||
matchingWorktreeEntry,
|
||||
matchingBranchEntry
|
||||
),
|
||||
openFileState,
|
||||
isMarkdown: resolvedLanguage === 'markdown',
|
||||
isMermaid: resolvedLanguage === 'mermaid',
|
||||
isCsv: resolvedLanguage === 'csv' || resolvedLanguage === 'tsv',
|
||||
isNotebook: resolvedLanguage === 'notebook',
|
||||
canOpenPreviewToSide: activeFile.mode === 'edit' && canPreviewLanguage(resolvedLanguage),
|
||||
// Why: the preview renders the on-disk file, so diff surfaces only get it
|
||||
// when the modified side still exists on disk (canOpen excludes deleted
|
||||
// files and commit diffs whose content may not match the working tree).
|
||||
canOpenPreviewToSide:
|
||||
canPreviewLanguage(resolvedLanguage) &&
|
||||
(activeFile.mode === 'edit' || (isSingleDiff && openFileState.canOpen)),
|
||||
mdViewMode,
|
||||
hasViewModeToggle,
|
||||
availableEditorToggleModes,
|
||||
|
|
|
|||
Loading…
Reference in New Issue