mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-23 00:54:43 +00:00
ui: avoid session review header clipping
Move the session review header outside the scroll viewport and drop strict containment so shadows can render without being cropped.
This commit is contained in:
@@ -3,11 +3,10 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0px;
|
gap: 0px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: none;
|
[data-slot="session-review-scroll"] {
|
||||||
contain: strict;
|
flex: 1 1 auto;
|
||||||
&::-webkit-scrollbar {
|
min-height: 0;
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-view__viewport {
|
.scroll-view__viewport {
|
||||||
@@ -21,8 +20,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="session-review-header"] {
|
[data-slot="session-review-header"] {
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 120;
|
z-index: 120;
|
||||||
background-color: var(--background-stronger);
|
background-color: var(--background-stronger);
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -63,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-component="sticky-accordion-header"] {
|
[data-component="sticky-accordion-header"] {
|
||||||
--sticky-accordion-top: 40px;
|
--sticky-accordion-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="session-review-accordion-item"][data-selected]
|
[data-slot="session-review-accordion-item"][data-selected]
|
||||||
|
|||||||
@@ -554,20 +554,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<div data-component="session-review" class={props.class} classList={props.classList}>
|
||||||
data-component="session-review"
|
|
||||||
viewportRef={(el) => {
|
|
||||||
scroll = el
|
|
||||||
props.scrollRef?.(el)
|
|
||||||
}}
|
|
||||||
onScroll={props.onScroll as any}
|
|
||||||
onKeyDown={handleReviewKeyDown}
|
|
||||||
classList={{
|
|
||||||
...(props.classList ?? {}),
|
|
||||||
[props.classes?.root ?? ""]: !!props.classes?.root,
|
|
||||||
[props.class ?? ""]: !!props.class,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div data-slot="session-review-header" class={props.classes?.header}>
|
<div data-slot="session-review-header" class={props.classes?.header}>
|
||||||
<div data-slot="session-review-title">{props.title ?? i18n.t("ui.sessionReview.title")}</div>
|
<div data-slot="session-review-title">{props.title ?? i18n.t("ui.sessionReview.title")}</div>
|
||||||
<div data-slot="session-review-actions">
|
<div data-slot="session-review-actions">
|
||||||
@@ -599,301 +586,317 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
{props.actions}
|
{props.actions}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={searchOpen()}>
|
|
||||||
<FileSearchBar
|
|
||||||
pos={searchPos}
|
|
||||||
query={searchQuery}
|
|
||||||
index={() => (searchHits().length ? Math.min(searchActive(), searchHits().length - 1) : 0)}
|
|
||||||
count={() => searchHits().length}
|
|
||||||
setInput={(el) => {
|
|
||||||
searchInput = el
|
|
||||||
}}
|
|
||||||
onInput={(value) => {
|
|
||||||
setSearchQuery(value)
|
|
||||||
setSearchActive(0)
|
|
||||||
}}
|
|
||||||
onKeyDown={(event) => handleSearchInputKeyDown(event)}
|
|
||||||
onClose={closeSearch}
|
|
||||||
onPrev={() => navigateSearch(-1)}
|
|
||||||
onNext={() => navigateSearch(1)}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<div data-slot="session-review-container" class={props.classes?.container}>
|
|
||||||
<Show when={hasDiffs()} fallback={props.empty}>
|
|
||||||
<Accordion multiple value={open()} onChange={handleChange}>
|
|
||||||
<For each={files()}>
|
|
||||||
{(file) => {
|
|
||||||
let wrapper: HTMLDivElement | undefined
|
|
||||||
|
|
||||||
const diff = createMemo(() => diffs().get(file))
|
<ScrollView
|
||||||
const item = () => diff()!
|
data-slot="session-review-scroll"
|
||||||
|
viewportRef={(el) => {
|
||||||
|
scroll = el
|
||||||
|
props.scrollRef?.(el)
|
||||||
|
}}
|
||||||
|
onScroll={props.onScroll as any}
|
||||||
|
onKeyDown={handleReviewKeyDown}
|
||||||
|
classList={{
|
||||||
|
[props.classes?.root ?? ""]: !!props.classes?.root,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Show when={searchOpen()}>
|
||||||
|
<FileSearchBar
|
||||||
|
pos={searchPos}
|
||||||
|
query={searchQuery}
|
||||||
|
index={() => (searchHits().length ? Math.min(searchActive(), searchHits().length - 1) : 0)}
|
||||||
|
count={() => searchHits().length}
|
||||||
|
setInput={(el) => {
|
||||||
|
searchInput = el
|
||||||
|
}}
|
||||||
|
onInput={(value) => {
|
||||||
|
setSearchQuery(value)
|
||||||
|
setSearchActive(0)
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => handleSearchInputKeyDown(event)}
|
||||||
|
onClose={closeSearch}
|
||||||
|
onPrev={() => navigateSearch(-1)}
|
||||||
|
onNext={() => navigateSearch(1)}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
|
||||||
const expanded = createMemo(() => open().includes(file))
|
<div data-slot="session-review-container" class={props.classes?.container}>
|
||||||
const force = () => !!store.force[file]
|
<Show when={hasDiffs()} fallback={props.empty}>
|
||||||
|
<Accordion multiple value={open()} onChange={handleChange}>
|
||||||
|
<For each={files()}>
|
||||||
|
{(file) => {
|
||||||
|
let wrapper: HTMLDivElement | undefined
|
||||||
|
|
||||||
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === file))
|
const diff = createMemo(() => diffs().get(file))
|
||||||
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
const item = () => diff()!
|
||||||
|
|
||||||
const beforeText = () => (typeof item().before === "string" ? item().before : "")
|
const expanded = createMemo(() => open().includes(file))
|
||||||
const afterText = () => (typeof item().after === "string" ? item().after : "")
|
const force = () => !!store.force[file]
|
||||||
const changedLines = () => item().additions + item().deletions
|
|
||||||
const mediaKind = createMemo(() => mediaKindFromPath(file))
|
|
||||||
|
|
||||||
const tooLarge = createMemo(() => {
|
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === file))
|
||||||
if (!expanded()) return false
|
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
||||||
if (force()) return false
|
|
||||||
if (mediaKind()) return false
|
|
||||||
return changedLines() > MAX_DIFF_CHANGED_LINES
|
|
||||||
})
|
|
||||||
|
|
||||||
const isAdded = () => item().status === "added" || (beforeText().length === 0 && afterText().length > 0)
|
const beforeText = () => (typeof item().before === "string" ? item().before : "")
|
||||||
const isDeleted = () =>
|
const afterText = () => (typeof item().after === "string" ? item().after : "")
|
||||||
item().status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
|
const changedLines = () => item().additions + item().deletions
|
||||||
|
const mediaKind = createMemo(() => mediaKindFromPath(file))
|
||||||
|
|
||||||
const selectedLines = createMemo(() => {
|
const tooLarge = createMemo(() => {
|
||||||
const current = selection()
|
if (!expanded()) return false
|
||||||
if (!current || current.file !== file) return null
|
if (force()) return false
|
||||||
return current.range
|
if (mediaKind()) return false
|
||||||
})
|
return changedLines() > MAX_DIFF_CHANGED_LINES
|
||||||
|
})
|
||||||
|
|
||||||
const draftRange = createMemo(() => {
|
const isAdded = () =>
|
||||||
const current = commenting()
|
item().status === "added" || (beforeText().length === 0 && afterText().length > 0)
|
||||||
if (!current || current.file !== file) return null
|
const isDeleted = () =>
|
||||||
return current.range
|
item().status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
|
||||||
})
|
|
||||||
|
|
||||||
const commentsUi = createLineCommentController<SessionReviewComment>({
|
const selectedLines = createMemo(() => {
|
||||||
comments,
|
const current = selection()
|
||||||
label: i18n.t("ui.lineComment.submit"),
|
if (!current || current.file !== file) return null
|
||||||
draftKey: () => file,
|
return current.range
|
||||||
state: {
|
})
|
||||||
opened: () => {
|
|
||||||
const current = opened()
|
const draftRange = createMemo(() => {
|
||||||
if (!current || current.file !== file) return null
|
const current = commenting()
|
||||||
return current.id
|
if (!current || current.file !== file) return null
|
||||||
|
return current.range
|
||||||
|
})
|
||||||
|
|
||||||
|
const commentsUi = createLineCommentController<SessionReviewComment>({
|
||||||
|
comments,
|
||||||
|
label: i18n.t("ui.lineComment.submit"),
|
||||||
|
draftKey: () => file,
|
||||||
|
state: {
|
||||||
|
opened: () => {
|
||||||
|
const current = opened()
|
||||||
|
if (!current || current.file !== file) return null
|
||||||
|
return current.id
|
||||||
|
},
|
||||||
|
setOpened: (id) => setOpened(id ? { file, id } : null),
|
||||||
|
selected: selectedLines,
|
||||||
|
setSelected: (range) => setSelection(range ? { file, range } : null),
|
||||||
|
commenting: draftRange,
|
||||||
|
setCommenting: (range) => setCommenting(range ? { file, range } : null),
|
||||||
},
|
},
|
||||||
setOpened: (id) => setOpened(id ? { file, id } : null),
|
getSide: selectionSide,
|
||||||
selected: selectedLines,
|
clearSelectionOnSelectionEndNull: false,
|
||||||
setSelected: (range) => setSelection(range ? { file, range } : null),
|
onSubmit: ({ comment, selection }) => {
|
||||||
commenting: draftRange,
|
props.onLineComment?.({
|
||||||
setCommenting: (range) => setCommenting(range ? { file, range } : null),
|
file,
|
||||||
},
|
selection,
|
||||||
getSide: selectionSide,
|
comment,
|
||||||
clearSelectionOnSelectionEndNull: false,
|
preview: selectionPreview(item(), selection),
|
||||||
onSubmit: ({ comment, selection }) => {
|
})
|
||||||
props.onLineComment?.({
|
},
|
||||||
file,
|
onUpdate: ({ id, comment, selection }) => {
|
||||||
selection,
|
props.onLineCommentUpdate?.({
|
||||||
comment,
|
id,
|
||||||
preview: selectionPreview(item(), selection),
|
file,
|
||||||
})
|
selection,
|
||||||
},
|
comment,
|
||||||
onUpdate: ({ id, comment, selection }) => {
|
preview: selectionPreview(item(), selection),
|
||||||
props.onLineCommentUpdate?.({
|
})
|
||||||
id,
|
},
|
||||||
file,
|
onDelete: (comment) => {
|
||||||
selection,
|
props.onLineCommentDelete?.({
|
||||||
comment,
|
id: comment.id,
|
||||||
preview: selectionPreview(item(), selection),
|
file,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDelete: (comment) => {
|
editSubmitLabel: props.lineCommentActions?.saveLabel,
|
||||||
props.onLineCommentDelete?.({
|
renderCommentActions: props.lineCommentActions
|
||||||
id: comment.id,
|
? (comment, controls) => (
|
||||||
file,
|
<ReviewCommentMenu
|
||||||
})
|
labels={props.lineCommentActions!}
|
||||||
},
|
onEdit={controls.edit}
|
||||||
editSubmitLabel: props.lineCommentActions?.saveLabel,
|
onDelete={controls.remove}
|
||||||
renderCommentActions: props.lineCommentActions
|
/>
|
||||||
? (comment, controls) => (
|
)
|
||||||
<ReviewCommentMenu
|
: undefined,
|
||||||
labels={props.lineCommentActions!}
|
})
|
||||||
onEdit={controls.edit}
|
|
||||||
onDelete={controls.remove}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
})
|
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
anchors.delete(file)
|
anchors.delete(file)
|
||||||
readyFiles.delete(file)
|
readyFiles.delete(file)
|
||||||
searchHandles.delete(file)
|
searchHandles.delete(file)
|
||||||
if (highlightedFile === file) highlightedFile = undefined
|
if (highlightedFile === file) highlightedFile = undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleLineSelected = (range: SelectedLineRange | null) => {
|
const handleLineSelected = (range: SelectedLineRange | null) => {
|
||||||
if (!props.onLineComment) return
|
if (!props.onLineComment) return
|
||||||
commentsUi.onLineSelected(range)
|
commentsUi.onLineSelected(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
|
const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
|
||||||
if (!props.onLineComment) return
|
if (!props.onLineComment) return
|
||||||
commentsUi.onLineSelectionEnd(range)
|
commentsUi.onLineSelectionEnd(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion.Item
|
<Accordion.Item
|
||||||
value={file}
|
value={file}
|
||||||
id={diffId(file)}
|
id={diffId(file)}
|
||||||
data-file={file}
|
data-file={file}
|
||||||
data-slot="session-review-accordion-item"
|
data-slot="session-review-accordion-item"
|
||||||
data-selected={props.focusedFile === file ? "" : undefined}
|
data-selected={props.focusedFile === file ? "" : undefined}
|
||||||
>
|
>
|
||||||
<StickyAccordionHeader>
|
<StickyAccordionHeader>
|
||||||
<Accordion.Trigger>
|
<Accordion.Trigger>
|
||||||
<div data-slot="session-review-trigger-content">
|
<div data-slot="session-review-trigger-content">
|
||||||
<div data-slot="session-review-file-info">
|
<div data-slot="session-review-file-info">
|
||||||
<FileIcon node={{ path: file, type: "file" }} />
|
<FileIcon node={{ path: file, type: "file" }} />
|
||||||
<div data-slot="session-review-file-name-container">
|
<div data-slot="session-review-file-name-container">
|
||||||
<Show when={file.includes("/")}>
|
<Show when={file.includes("/")}>
|
||||||
<span data-slot="session-review-directory">{`\u202A${getDirectory(file)}\u202C`}</span>
|
<span data-slot="session-review-directory">{`\u202A${getDirectory(file)}\u202C`}</span>
|
||||||
</Show>
|
</Show>
|
||||||
<span data-slot="session-review-filename">{getFilename(file)}</span>
|
<span data-slot="session-review-filename">{getFilename(file)}</span>
|
||||||
<Show when={props.onViewFile}>
|
<Show when={props.onViewFile}>
|
||||||
<Tooltip value={openFileLabel()} placement="top" gutter={4}>
|
<Tooltip value={openFileLabel()} placement="top" gutter={4}>
|
||||||
<button
|
<button
|
||||||
data-slot="session-review-view-button"
|
data-slot="session-review-view-button"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={openFileLabel()}
|
aria-label={openFileLabel()}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
props.onViewFile?.(file)
|
props.onViewFile?.(file)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name="open-file" size="small" />
|
<Icon name="open-file" size="small" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Show>
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-slot="session-review-trigger-actions">
|
||||||
|
<Switch>
|
||||||
|
<Match when={isAdded()}>
|
||||||
|
<div data-slot="session-review-change-group" data-type="added">
|
||||||
|
<span data-slot="session-review-change" data-type="added">
|
||||||
|
{i18n.t("ui.sessionReview.change.added")}
|
||||||
|
</span>
|
||||||
|
<DiffChanges changes={item()} />
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
<Match when={isDeleted()}>
|
||||||
|
<span data-slot="session-review-change" data-type="removed">
|
||||||
|
{i18n.t("ui.sessionReview.change.removed")}
|
||||||
|
</span>
|
||||||
|
</Match>
|
||||||
|
<Match when={!!mediaKind()}>
|
||||||
|
<span data-slot="session-review-change" data-type="modified">
|
||||||
|
{i18n.t("ui.sessionReview.change.modified")}
|
||||||
|
</span>
|
||||||
|
</Match>
|
||||||
|
<Match when={true}>
|
||||||
|
<DiffChanges changes={item()} />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
<span data-slot="session-review-diff-chevron">
|
||||||
|
<Icon name="chevron-down" size="small" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div data-slot="session-review-trigger-actions">
|
</Accordion.Trigger>
|
||||||
|
</StickyAccordionHeader>
|
||||||
|
<Accordion.Content data-slot="session-review-accordion-content">
|
||||||
|
<div
|
||||||
|
data-slot="session-review-diff-wrapper"
|
||||||
|
ref={(el) => {
|
||||||
|
wrapper = el
|
||||||
|
anchors.set(file, el)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Show when={expanded()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={isAdded()}>
|
<Match when={tooLarge()}>
|
||||||
<div data-slot="session-review-change-group" data-type="added">
|
<div data-slot="session-review-large-diff">
|
||||||
<span data-slot="session-review-change" data-type="added">
|
<div data-slot="session-review-large-diff-title">
|
||||||
{i18n.t("ui.sessionReview.change.added")}
|
{i18n.t("ui.sessionReview.largeDiff.title")}
|
||||||
</span>
|
</div>
|
||||||
<DiffChanges changes={item()} />
|
<div data-slot="session-review-large-diff-meta">
|
||||||
|
{i18n.t("ui.sessionReview.largeDiff.meta", {
|
||||||
|
limit: MAX_DIFF_CHANGED_LINES.toLocaleString(),
|
||||||
|
current: changedLines().toLocaleString(),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div data-slot="session-review-large-diff-actions">
|
||||||
|
<Button
|
||||||
|
size="normal"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => setStore("force", file, true)}
|
||||||
|
>
|
||||||
|
{i18n.t("ui.sessionReview.largeDiff.renderAnyway")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={isDeleted()}>
|
|
||||||
<span data-slot="session-review-change" data-type="removed">
|
|
||||||
{i18n.t("ui.sessionReview.change.removed")}
|
|
||||||
</span>
|
|
||||||
</Match>
|
|
||||||
<Match when={!!mediaKind()}>
|
|
||||||
<span data-slot="session-review-change" data-type="modified">
|
|
||||||
{i18n.t("ui.sessionReview.change.modified")}
|
|
||||||
</span>
|
|
||||||
</Match>
|
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<DiffChanges changes={item()} />
|
<Dynamic
|
||||||
|
component={fileComponent}
|
||||||
|
mode="diff"
|
||||||
|
preloadedDiff={item().preloaded}
|
||||||
|
diffStyle={diffStyle()}
|
||||||
|
expansionLineCount={searchExpanded() ? Number.MAX_SAFE_INTEGER : 20}
|
||||||
|
onRendered={() => {
|
||||||
|
readyFiles.add(file)
|
||||||
|
props.onDiffRendered?.()
|
||||||
|
}}
|
||||||
|
enableLineSelection={props.onLineComment != null}
|
||||||
|
enableHoverUtility={props.onLineComment != null}
|
||||||
|
onLineSelected={handleLineSelected}
|
||||||
|
onLineSelectionEnd={handleLineSelectionEnd}
|
||||||
|
onLineNumberSelectionEnd={commentsUi.onLineNumberSelectionEnd}
|
||||||
|
annotations={commentsUi.annotations()}
|
||||||
|
renderAnnotation={commentsUi.renderAnnotation}
|
||||||
|
renderHoverUtility={props.onLineComment ? commentsUi.renderHoverUtility : undefined}
|
||||||
|
selectedLines={selectedLines()}
|
||||||
|
commentedLines={commentedLines()}
|
||||||
|
search={{
|
||||||
|
shortcuts: "disabled",
|
||||||
|
showBar: false,
|
||||||
|
disableVirtualization: searchExpanded(),
|
||||||
|
register: (handle: FileSearchHandle | null) => {
|
||||||
|
if (!handle) {
|
||||||
|
searchHandles.delete(file)
|
||||||
|
readyFiles.delete(file)
|
||||||
|
if (highlightedFile === file) highlightedFile = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
searchHandles.set(file, handle)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
before={{
|
||||||
|
name: file,
|
||||||
|
contents: typeof item().before === "string" ? item().before : "",
|
||||||
|
}}
|
||||||
|
after={{
|
||||||
|
name: file,
|
||||||
|
contents: typeof item().after === "string" ? item().after : "",
|
||||||
|
}}
|
||||||
|
media={{
|
||||||
|
mode: "auto",
|
||||||
|
path: file,
|
||||||
|
before: item().before,
|
||||||
|
after: item().after,
|
||||||
|
readFile: props.readFile,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<span data-slot="session-review-diff-chevron">
|
</Show>
|
||||||
<Icon name="chevron-down" size="small" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Accordion.Trigger>
|
</Accordion.Content>
|
||||||
</StickyAccordionHeader>
|
</Accordion.Item>
|
||||||
<Accordion.Content data-slot="session-review-accordion-content">
|
)
|
||||||
<div
|
}}
|
||||||
data-slot="session-review-diff-wrapper"
|
</For>
|
||||||
ref={(el) => {
|
</Accordion>
|
||||||
wrapper = el
|
</Show>
|
||||||
anchors.set(file, el)
|
</div>
|
||||||
}}
|
</ScrollView>
|
||||||
>
|
</div>
|
||||||
<Show when={expanded()}>
|
|
||||||
<Switch>
|
|
||||||
<Match when={tooLarge()}>
|
|
||||||
<div data-slot="session-review-large-diff">
|
|
||||||
<div data-slot="session-review-large-diff-title">
|
|
||||||
{i18n.t("ui.sessionReview.largeDiff.title")}
|
|
||||||
</div>
|
|
||||||
<div data-slot="session-review-large-diff-meta">
|
|
||||||
{i18n.t("ui.sessionReview.largeDiff.meta", {
|
|
||||||
limit: MAX_DIFF_CHANGED_LINES.toLocaleString(),
|
|
||||||
current: changedLines().toLocaleString(),
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div data-slot="session-review-large-diff-actions">
|
|
||||||
<Button
|
|
||||||
size="normal"
|
|
||||||
variant="secondary"
|
|
||||||
onClick={() => setStore("force", file, true)}
|
|
||||||
>
|
|
||||||
{i18n.t("ui.sessionReview.largeDiff.renderAnyway")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Match>
|
|
||||||
<Match when={true}>
|
|
||||||
<Dynamic
|
|
||||||
component={fileComponent}
|
|
||||||
mode="diff"
|
|
||||||
preloadedDiff={item().preloaded}
|
|
||||||
diffStyle={diffStyle()}
|
|
||||||
expansionLineCount={searchExpanded() ? Number.MAX_SAFE_INTEGER : 20}
|
|
||||||
onRendered={() => {
|
|
||||||
readyFiles.add(file)
|
|
||||||
props.onDiffRendered?.()
|
|
||||||
}}
|
|
||||||
enableLineSelection={props.onLineComment != null}
|
|
||||||
enableHoverUtility={props.onLineComment != null}
|
|
||||||
onLineSelected={handleLineSelected}
|
|
||||||
onLineSelectionEnd={handleLineSelectionEnd}
|
|
||||||
onLineNumberSelectionEnd={commentsUi.onLineNumberSelectionEnd}
|
|
||||||
annotations={commentsUi.annotations()}
|
|
||||||
renderAnnotation={commentsUi.renderAnnotation}
|
|
||||||
renderHoverUtility={props.onLineComment ? commentsUi.renderHoverUtility : undefined}
|
|
||||||
selectedLines={selectedLines()}
|
|
||||||
commentedLines={commentedLines()}
|
|
||||||
search={{
|
|
||||||
shortcuts: "disabled",
|
|
||||||
showBar: false,
|
|
||||||
disableVirtualization: searchExpanded(),
|
|
||||||
register: (handle: FileSearchHandle | null) => {
|
|
||||||
if (!handle) {
|
|
||||||
searchHandles.delete(file)
|
|
||||||
readyFiles.delete(file)
|
|
||||||
if (highlightedFile === file) highlightedFile = undefined
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
searchHandles.set(file, handle)
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
before={{
|
|
||||||
name: file,
|
|
||||||
contents: typeof item().before === "string" ? item().before : "",
|
|
||||||
}}
|
|
||||||
after={{
|
|
||||||
name: file,
|
|
||||||
contents: typeof item().after === "string" ? item().after : "",
|
|
||||||
}}
|
|
||||||
media={{
|
|
||||||
mode: "auto",
|
|
||||||
path: file,
|
|
||||||
before: item().before,
|
|
||||||
after: item().after,
|
|
||||||
readFile: props.readFile,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Accordion.Content>
|
|
||||||
</Accordion.Item>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</Accordion>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</ScrollView>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user