mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-02 08:33:16 +00:00
fix: Simplify review explanation: full-width, plain prose, no diff links (#1547)
* Simplify review explanation: full-width, plain prose, no diff links * Update _build_prompt test for plain diff fences --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
parent
17f0ca585f
commit
e58b609b2f
7 changed files with 33 additions and 190 deletions
|
|
@ -52,10 +52,9 @@ class _DiffGroupModel(BaseModel):
|
||||||
)
|
)
|
||||||
summary: str = Field(
|
summary: str = Field(
|
||||||
description=(
|
description=(
|
||||||
"GitHub-flavored markdown walkthrough of this group: a few short "
|
"A short, plain explanation of what this group changes and why, in a "
|
||||||
"sentences or bullets, code identifiers in `backticks`, optionally one "
|
"few sentences a reviewer can skim. Wrap code identifiers, symbols, "
|
||||||
"short fenced code block of the key snippet, and concrete locations "
|
"types, flags, and paths in `backticks`. No code blocks or links."
|
||||||
"linked as [path:start-end](#loc=path:start-end)."
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
files: list[str] = Field(
|
files: list[str] = Field(
|
||||||
|
|
@ -82,24 +81,18 @@ Rules:
|
||||||
- Use at most {max_groups} groups. Prefer fewer, larger groups over many tiny ones.
|
- Use at most {max_groups} groups. Prefer fewer, larger groups over many tiny ones.
|
||||||
- title: a short headline naming the change, roughly 4-10 words, no trailing \
|
- title: a short headline naming the change, roughly 4-10 words, no trailing \
|
||||||
punctuation. Wrap code identifiers (symbols, flags, file names) in `backticks`.
|
punctuation. Wrap code identifiers (symbols, flags, file names) in `backticks`.
|
||||||
- summary: GitHub-flavored markdown explaining what the group changes and why, \
|
- summary: a short, plain explanation of what the group changes and why, that a \
|
||||||
written as a short walkthrough a reviewer can skim:
|
reviewer can skim:
|
||||||
- Keep it focused — a few short sentences or bullets. Do not restate the diff line by line.
|
- Keep it focused — a few short sentences. Do not restate the diff line by line.
|
||||||
- Wrap every code identifier, symbol, type, flag, and path in `backticks`.
|
- Wrap every code identifier, symbol, type, flag, and path in `backticks`.
|
||||||
- When one change is central, include at most ONE short fenced code block \
|
- Plain prose only — no code blocks and no links.
|
||||||
(```lang, <= ~8 lines) of the key snippet — not the whole hunk.
|
|
||||||
- Reference concrete locations as markdown links of the EXACT form \
|
|
||||||
[path:start-end](#loc=path:start-end), where path is the verbatim changed-file \
|
|
||||||
path and start/end are line numbers from the "lines X-Y" annotations below \
|
|
||||||
(use [path:line](#loc=path:line) when start == end). Prefer these links over \
|
|
||||||
describing locations in prose — they let the reader jump straight to the hunk.
|
|
||||||
- files: the exact file paths (copied verbatim from the list below) in this \
|
- files: the exact file paths (copied verbatim from the list below) in this \
|
||||||
group, in the order a reviewer should read them.
|
group, in the order a reviewer should read them.
|
||||||
|
|
||||||
Changed files:
|
Changed files:
|
||||||
{file_list}
|
{file_list}
|
||||||
|
|
||||||
Diffs (each hunk is annotated with its line range in the new file):
|
Diffs:
|
||||||
{diffs}
|
{diffs}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -122,9 +115,7 @@ def _build_prompt(diff_text: str, files: list[str]) -> str:
|
||||||
body = hunk.body
|
body = hunk.body
|
||||||
if len(body) > MAX_FILE_HUNK_CHARS:
|
if len(body) > MAX_FILE_HUNK_CHARS:
|
||||||
body = body[:MAX_FILE_HUNK_CHARS] + "\n... (truncated)"
|
body = body[:MAX_FILE_HUNK_CHARS] + "\n... (truncated)"
|
||||||
# Surface the new-file line range so the model can cite accurate
|
segments.append(f"```diff\n{body}\n```")
|
||||||
# locations in its summary links.
|
|
||||||
segments.append(f"lines {hunk.new_start}-{hunk.new_end}:\n```diff\n{body}\n```")
|
|
||||||
block = f"### {file_diff.file}\n" + "\n".join(segments) + "\n"
|
block = f"### {file_diff.file}\n" + "\n".join(segments) + "\n"
|
||||||
if budget - len(block) < 0:
|
if budget - len(block) < 0:
|
||||||
parts.append(f"### {file_diff.file}\n(diff omitted — prompt budget reached)\n")
|
parts.append(f"### {file_diff.file}\n(diff omitted — prompt budget reached)\n")
|
||||||
|
|
|
||||||
|
|
@ -97,14 +97,13 @@ async def test_generate_diff_groups_llm_failure_returns_none() -> None:
|
||||||
assert groups is None
|
assert groups is None
|
||||||
|
|
||||||
|
|
||||||
def test_build_prompt_annotates_hunk_line_ranges() -> None:
|
def test_build_prompt_includes_files_and_diffs() -> None:
|
||||||
prompt = _build_prompt(_DIFF, ["foo.py", "bar.py"])
|
prompt = _build_prompt(_DIFF, ["foo.py", "bar.py"])
|
||||||
assert "### foo.py" in prompt
|
assert "### foo.py" in prompt
|
||||||
assert "### bar.py" in prompt
|
assert "### bar.py" in prompt
|
||||||
# New-file line ranges from the @@ headers are surfaced so the model can
|
# Hunks are rendered as plain diff fences (no line-range annotations).
|
||||||
# cite accurate locations: foo.py @@ -1,2 +1,3 @@ and bar.py @@ -1 +1,2 @@.
|
assert "lines 1-3:" not in prompt
|
||||||
assert "lines 1-3:" in prompt
|
assert "```diff" in prompt
|
||||||
assert "lines 1-2:" in prompt
|
|
||||||
# The verbatim file list is included so every file can be assigned.
|
# The verbatim file list is included so every file can be assigned.
|
||||||
assert "- foo.py" in prompt
|
assert "- foo.py" in prompt
|
||||||
assert "- bar.py" in prompt
|
assert "- bar.py" in prompt
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ export interface ReviewSidebarData {
|
||||||
view: ReviewSidebarView
|
view: ReviewSidebarView
|
||||||
onViewChange: (view: ReviewSidebarView) => void
|
onViewChange: (view: ReviewSidebarView) => void
|
||||||
onSelectGroup: (index: number) => void
|
onSelectGroup: (index: number) => void
|
||||||
onLocationClick?: (file: string, startLine: number, endLine: number) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewSidebarContext = createContext<{
|
const ReviewSidebarContext = createContext<{
|
||||||
|
|
@ -105,7 +104,6 @@ export function ReviewSidebarPanel({ data }: { data: ReviewSidebarData }) {
|
||||||
groups={data.groups ?? []}
|
groups={data.groups ?? []}
|
||||||
onSelectGroup={data.onSelectGroup}
|
onSelectGroup={data.onSelectGroup}
|
||||||
onSelectFile={data.onSelect}
|
onSelectFile={data.onSelect}
|
||||||
onLocationClick={data.onLocationClick}
|
|
||||||
/>
|
/>
|
||||||
) : !data.files ? (
|
) : !data.files ? (
|
||||||
<div className="px-4 pt-1">
|
<div className="px-4 pt-1">
|
||||||
|
|
@ -183,12 +181,10 @@ function ReviewGroupList({
|
||||||
groups,
|
groups,
|
||||||
onSelectGroup,
|
onSelectGroup,
|
||||||
onSelectFile,
|
onSelectFile,
|
||||||
onLocationClick,
|
|
||||||
}: {
|
}: {
|
||||||
groups: Array<ReviewSidebarGroup>
|
groups: Array<ReviewSidebarGroup>
|
||||||
onSelectGroup: (index: number) => void
|
onSelectGroup: (index: number) => void
|
||||||
onSelectFile: (path: string) => void
|
onSelectFile: (path: string) => void
|
||||||
onLocationClick?: (file: string, startLine: number, endLine: number) => void
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-0 flex-1 divide-y divide-[var(--ui-border-subtle)] overflow-y-auto">
|
<div className="min-h-0 flex-1 divide-y divide-[var(--ui-border-subtle)] overflow-y-auto">
|
||||||
|
|
@ -198,7 +194,6 @@ function ReviewGroupList({
|
||||||
group={group}
|
group={group}
|
||||||
onSelect={() => onSelectGroup(group.index)}
|
onSelect={() => onSelectGroup(group.index)}
|
||||||
onSelectFile={onSelectFile}
|
onSelectFile={onSelectFile}
|
||||||
onLocationClick={onLocationClick}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -211,6 +206,12 @@ function splitPath(path: string): { dir: string; base: string } {
|
||||||
return { dir: path.slice(0, idx), base: path.slice(idx + 1) }
|
return { dir: path.slice(0, idx), base: path.slice(idx + 1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Older stored summaries embed `[label](#loc=path:line)` diff links. Render the
|
||||||
|
// label as inline code instead so no stale jump-links leak into the explanation.
|
||||||
|
function stripLocationLinks(summary: string): string {
|
||||||
|
return summary.replace(/\[([^\]]+)\]\(#loc=[^)]*\)/g, "`$1`")
|
||||||
|
}
|
||||||
|
|
||||||
// Render a title with `backtick`-delimited spans as inline code chips, matching
|
// Render a title with `backtick`-delimited spans as inline code chips, matching
|
||||||
// the Markdown component's inline-code styling, without pulling in the full
|
// the Markdown component's inline-code styling, without pulling in the full
|
||||||
// block renderer for a single line.
|
// block renderer for a single line.
|
||||||
|
|
@ -234,12 +235,10 @@ function ReviewGroupRow({
|
||||||
group,
|
group,
|
||||||
onSelect,
|
onSelect,
|
||||||
onSelectFile,
|
onSelectFile,
|
||||||
onLocationClick,
|
|
||||||
}: {
|
}: {
|
||||||
group: ReviewSidebarGroup
|
group: ReviewSidebarGroup
|
||||||
onSelect: () => void
|
onSelect: () => void
|
||||||
onSelectFile: (path: string) => void
|
onSelectFile: (path: string) => void
|
||||||
onLocationClick?: (file: string, startLine: number, endLine: number) => void
|
|
||||||
}) {
|
}) {
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
return (
|
return (
|
||||||
|
|
@ -297,7 +296,7 @@ function ReviewGroupRow({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{group.summary && (
|
{group.summary && (
|
||||||
<div className="mt-2 pl-7">
|
<div className="mt-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setExpanded((value) => !value)}
|
onClick={() => setExpanded((value) => !value)}
|
||||||
|
|
@ -313,10 +312,7 @@ function ReviewGroupRow({
|
||||||
</button>
|
</button>
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<div className="mt-1.5">
|
<div className="mt-1.5">
|
||||||
<Markdown
|
<Markdown content={stripLocationLinks(group.summary)} />
|
||||||
content={group.summary}
|
|
||||||
onLocationClick={onLocationClick}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { memo, useMemo } from "react";
|
import { memo, useMemo } from "react";
|
||||||
import { Streamdown } from "streamdown";
|
import { Streamdown } from "streamdown";
|
||||||
import { parseLocationHref } from "./markdownLocation";
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import "streamdown/styles.css";
|
import "streamdown/styles.css";
|
||||||
|
|
||||||
|
|
@ -8,12 +7,6 @@ interface MarkdownProps {
|
||||||
content: string;
|
content: string;
|
||||||
/** When true, keep Streamdown in streaming mode for the duration of the run. */
|
/** When true, keep Streamdown in streaming mode for the duration of the run. */
|
||||||
isLive?: boolean;
|
isLive?: boolean;
|
||||||
/**
|
|
||||||
* When set, `#loc=path:start-end` links render as in-page buttons that invoke
|
|
||||||
* this callback instead of navigating. Used by the review view to jump the
|
|
||||||
* diff to a referenced hunk.
|
|
||||||
*/
|
|
||||||
onLocationClick?: (file: string, startLine: number, endLine: number) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -86,37 +79,22 @@ const SHIKI_THEME: ["github-light", "github-dark"] = ["github-light", "github-da
|
||||||
export const Markdown = memo(function Markdown({
|
export const Markdown = memo(function Markdown({
|
||||||
content,
|
content,
|
||||||
isLive = false,
|
isLive = false,
|
||||||
onLocationClick,
|
|
||||||
}: MarkdownProps) {
|
}: MarkdownProps) {
|
||||||
const components = useMemo(
|
const components = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...STREAMDOWN_COMPONENTS,
|
...STREAMDOWN_COMPONENTS,
|
||||||
a: ({ href, children }: { href?: string; children?: ReactNode }) => {
|
a: ({ href, children }: { href?: string; children?: ReactNode }) => (
|
||||||
const loc = onLocationClick ? parseLocationHref(href) : null;
|
<a
|
||||||
if (loc && onLocationClick) {
|
className="text-[color:var(--ui-accent)] underline decoration-[color:var(--ui-accent)]/50 break-words [overflow-wrap:anywhere]"
|
||||||
return (
|
href={href}
|
||||||
<button
|
target="_blank"
|
||||||
type="button"
|
rel="noreferrer"
|
||||||
onClick={() => onLocationClick(loc.file, loc.startLine, loc.endLine)}
|
>
|
||||||
className="font-mono text-[0.85em] text-[color:var(--ui-accent)] underline decoration-[color:var(--ui-accent)]/50 break-words [overflow-wrap:anywhere]"
|
{children}
|
||||||
>
|
</a>
|
||||||
{children}
|
),
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
className="text-[color:var(--ui-accent)] underline decoration-[color:var(--ui-accent)]/50 break-words [overflow-wrap:anywhere]"
|
|
||||||
href={href}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
[onLocationClick]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import { describe, expect, it } from "vitest";
|
|
||||||
|
|
||||||
import { parseLocationHref } from "./markdownLocation";
|
|
||||||
|
|
||||||
describe("parseLocationHref", () => {
|
|
||||||
it("parses a path with a start-end range", () => {
|
|
||||||
expect(parseLocationHref("#loc=src/foo.rs:231-232")).toEqual({
|
|
||||||
file: "src/foo.rs",
|
|
||||||
startLine: 231,
|
|
||||||
endLine: 232,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("parses a single-line ref (no end)", () => {
|
|
||||||
expect(parseLocationHref("#loc=mod.rs:42")).toEqual({
|
|
||||||
file: "mod.rs",
|
|
||||||
startLine: 42,
|
|
||||||
endLine: 42,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("tolerates an origin prefix the URL transform may prepend", () => {
|
|
||||||
expect(parseLocationHref("http://localhost/#loc=a/b.ts:5-9")).toEqual({
|
|
||||||
file: "a/b.ts",
|
|
||||||
startLine: 5,
|
|
||||||
endLine: 9,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("decodes percent-encoded hrefs", () => {
|
|
||||||
expect(parseLocationHref("#loc=a%20b/c.ts:1-2")).toEqual({
|
|
||||||
file: "a b/c.ts",
|
|
||||||
startLine: 1,
|
|
||||||
endLine: 2,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("clamps an end below the start to a single line", () => {
|
|
||||||
expect(parseLocationHref("#loc=x.ts:10-3")).toEqual({
|
|
||||||
file: "x.ts",
|
|
||||||
startLine: 10,
|
|
||||||
endLine: 10,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns null for non-location and malformed hrefs", () => {
|
|
||||||
expect(parseLocationHref(undefined)).toBeNull();
|
|
||||||
expect(parseLocationHref("https://example.com")).toBeNull();
|
|
||||||
expect(parseLocationHref("#loc=noline")).toBeNull();
|
|
||||||
expect(parseLocationHref("#loc=:5")).toBeNull();
|
|
||||||
expect(parseLocationHref("#loc=x.ts:abc")).toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
export interface MarkdownLocation {
|
|
||||||
file: string;
|
|
||||||
startLine: number;
|
|
||||||
endLine: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a `#loc=<path>:<start>[-<end>]` sentinel href emitted by the reviewer
|
|
||||||
* diff-grouping pass into a file + line range. Tolerant of an origin prefix the
|
|
||||||
* markdown URL transform may prepend, and of percent-encoding. Returns null for
|
|
||||||
* any href that isn't a location link.
|
|
||||||
*/
|
|
||||||
export function parseLocationHref(href?: string): MarkdownLocation | null {
|
|
||||||
if (!href) return null;
|
|
||||||
const marker = href.indexOf("#loc=");
|
|
||||||
if (marker === -1) return null;
|
|
||||||
let raw = href.slice(marker + "#loc=".length);
|
|
||||||
try {
|
|
||||||
raw = decodeURIComponent(raw);
|
|
||||||
} catch {
|
|
||||||
// Keep raw as-is if it isn't valid percent-encoding.
|
|
||||||
}
|
|
||||||
const colon = raw.lastIndexOf(":");
|
|
||||||
if (colon <= 0) return null;
|
|
||||||
const file = raw.slice(0, colon);
|
|
||||||
const [startStr, endStr] = raw.slice(colon + 1).split("-");
|
|
||||||
const start = Number.parseInt(startStr ?? "", 10);
|
|
||||||
if (!Number.isFinite(start) || start <= 0) return null;
|
|
||||||
const end = endStr !== undefined ? Number.parseInt(endStr, 10) : start;
|
|
||||||
return {
|
|
||||||
file,
|
|
||||||
startLine: start,
|
|
||||||
endLine: Number.isFinite(end) && end >= start ? end : start,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -222,11 +222,6 @@ function ReviewBody({
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
||||||
const scrollRef = useRef<HTMLDivElement | null>(null)
|
const scrollRef = useRef<HTMLDivElement | null>(null)
|
||||||
const groupRefs = useRef<Record<number, HTMLDivElement | null>>({})
|
const groupRefs = useRef<Record<number, HTMLDivElement | null>>({})
|
||||||
const [selectedRange, setSelectedRange] = useState<{
|
|
||||||
file: string
|
|
||||||
start: number
|
|
||||||
end: number
|
|
||||||
} | null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void warmDiffHighlighter()
|
void warmDiffHighlighter()
|
||||||
|
|
@ -405,21 +400,6 @@ function ReviewBody({
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const scrollToLineRange = useCallback(
|
|
||||||
(file: string, start: number, end: number) => {
|
|
||||||
setSelectedFile(file)
|
|
||||||
setSelectedRange({ file, start, end })
|
|
||||||
setExpandedFiles((prev) => ({ ...prev, [file]: true }))
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
fileRefs.current[file]?.scrollIntoView({
|
|
||||||
block: "start",
|
|
||||||
behavior: "smooth",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const openFinding = useCallback(
|
const openFinding = useCallback(
|
||||||
(finding: ReviewFinding) => {
|
(finding: ReviewFinding) => {
|
||||||
markRead(finding.id)
|
markRead(finding.id)
|
||||||
|
|
@ -447,7 +427,6 @@ function ReviewBody({
|
||||||
file={file}
|
file={file}
|
||||||
findings={findingsByFile.get(file.path) ?? []}
|
findings={findingsByFile.get(file.path) ?? []}
|
||||||
focused={focused}
|
focused={focused}
|
||||||
selectedRange={selectedRange}
|
|
||||||
viewed={viewed.has(file.path)}
|
viewed={viewed.has(file.path)}
|
||||||
onToggleViewed={() => {
|
onToggleViewed={() => {
|
||||||
const becomingViewed = !viewed.has(file.path)
|
const becomingViewed = !viewed.has(file.path)
|
||||||
|
|
@ -485,7 +464,6 @@ function ReviewBody({
|
||||||
view,
|
view,
|
||||||
onViewChange: setView,
|
onViewChange: setView,
|
||||||
onSelectGroup: scrollToGroup,
|
onSelectGroup: scrollToGroup,
|
||||||
onLocationClick: scrollToLineRange,
|
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
detail.number,
|
detail.number,
|
||||||
|
|
@ -497,7 +475,6 @@ function ReviewBody({
|
||||||
view,
|
view,
|
||||||
setView,
|
setView,
|
||||||
scrollToGroup,
|
scrollToGroup,
|
||||||
scrollToLineRange,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
useRegisterReviewSidebar(sidebarData)
|
useRegisterReviewSidebar(sidebarData)
|
||||||
|
|
@ -689,7 +666,6 @@ function FileDiffCard({
|
||||||
file,
|
file,
|
||||||
findings,
|
findings,
|
||||||
focused,
|
focused,
|
||||||
selectedRange,
|
|
||||||
viewed,
|
viewed,
|
||||||
onToggleViewed,
|
onToggleViewed,
|
||||||
expanded,
|
expanded,
|
||||||
|
|
@ -701,7 +677,6 @@ function FileDiffCard({
|
||||||
file: ReviewDiffFile
|
file: ReviewDiffFile
|
||||||
findings: Array<ReviewFinding>
|
findings: Array<ReviewFinding>
|
||||||
focused: ReviewFinding | null
|
focused: ReviewFinding | null
|
||||||
selectedRange: { file: string; start: number; end: number } | null
|
|
||||||
viewed: boolean
|
viewed: boolean
|
||||||
onToggleViewed: () => void
|
onToggleViewed: () => void
|
||||||
expanded: boolean
|
expanded: boolean
|
||||||
|
|
@ -728,16 +703,8 @@ function FileDiffCard({
|
||||||
if (focused?.file === file.path && isAnchored(focused)) {
|
if (focused?.file === file.path && isAnchored(focused)) {
|
||||||
return findingSelectedRange(focused)
|
return findingSelectedRange(focused)
|
||||||
}
|
}
|
||||||
if (selectedRange?.file === file.path) {
|
|
||||||
return {
|
|
||||||
start: selectedRange.start,
|
|
||||||
end: selectedRange.end,
|
|
||||||
side: "additions",
|
|
||||||
endSide: "additions",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}, [focused, selectedRange, file.path])
|
}, [focused, file.path])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue