fix(app): cleanup comment component usage
This commit is contained in:
@@ -15,7 +15,7 @@ import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
|||||||
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
||||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||||
import { useCodeComponent } from "@opencode-ai/ui/context/code"
|
import { useCodeComponent } from "@opencode-ai/ui/context/code"
|
||||||
import { LineCommentAnchor } from "@opencode-ai/ui/line-comment"
|
import { LineComment as LineCommentView, LineCommentEditor } from "@opencode-ai/ui/line-comment"
|
||||||
import { SessionTurn } from "@opencode-ai/ui/session-turn"
|
import { SessionTurn } from "@opencode-ai/ui/session-turn"
|
||||||
import { createAutoScroll } from "@opencode-ai/ui/hooks"
|
import { createAutoScroll } from "@opencode-ai/ui/hooks"
|
||||||
import { SessionReview } from "@opencode-ai/ui/session-review"
|
import { SessionReview } from "@opencode-ai/ui/session-review"
|
||||||
@@ -1885,7 +1885,6 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let wrap: HTMLDivElement | undefined
|
let wrap: HTMLDivElement | undefined
|
||||||
let textarea: HTMLTextAreaElement | undefined
|
|
||||||
|
|
||||||
const fileComments = createMemo(() => {
|
const fileComments = createMemo(() => {
|
||||||
const p = path()
|
const p = path()
|
||||||
@@ -1898,7 +1897,6 @@ export default function Page() {
|
|||||||
const [openedComment, setOpenedComment] = createSignal<string | null>(null)
|
const [openedComment, setOpenedComment] = createSignal<string | null>(null)
|
||||||
const [commenting, setCommenting] = createSignal<SelectedLineRange | null>(null)
|
const [commenting, setCommenting] = createSignal<SelectedLineRange | null>(null)
|
||||||
const [draft, setDraft] = createSignal("")
|
const [draft, setDraft] = createSignal("")
|
||||||
const [draftError, setDraftError] = createSignal(false)
|
|
||||||
const [positions, setPositions] = createSignal<Record<string, number>>({})
|
const [positions, setPositions] = createSignal<Record<string, number>>({})
|
||||||
const [draftTop, setDraftTop] = createSignal<number | undefined>(undefined)
|
const [draftTop, setDraftTop] = createSignal<number | undefined>(undefined)
|
||||||
|
|
||||||
@@ -1986,7 +1984,6 @@ export default function Page() {
|
|||||||
const range = commenting()
|
const range = commenting()
|
||||||
if (!range) return
|
if (!range) return
|
||||||
setDraft("")
|
setDraft("")
|
||||||
requestAnimationFrame(() => textarea?.focus())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@@ -2047,7 +2044,7 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
<For each={fileComments()}>
|
<For each={fileComments()}>
|
||||||
{(comment) => (
|
{(comment) => (
|
||||||
<LineCommentAnchor
|
<LineCommentView
|
||||||
id={comment.id}
|
id={comment.id}
|
||||||
top={positions()[comment.id]}
|
top={positions()[comment.id]}
|
||||||
open={openedComment() === comment.id}
|
open={openedComment() === comment.id}
|
||||||
@@ -2063,26 +2060,31 @@ export default function Page() {
|
|||||||
setOpenedComment((current) => (current === comment.id ? null : comment.id))
|
setOpenedComment((current) => (current === comment.id ? null : comment.id))
|
||||||
file.setSelectedLines(p, comment.selection)
|
file.setSelectedLines(p, comment.selection)
|
||||||
}}
|
}}
|
||||||
>
|
comment={comment.comment}
|
||||||
<div class="flex flex-col gap-1.5">
|
selection={commentLabel(comment.selection)}
|
||||||
<div class="text-14-regular text-text-strong whitespace-pre-wrap">
|
/>
|
||||||
{comment.comment}
|
|
||||||
</div>
|
|
||||||
<div class="text-12-medium text-text-weak whitespace-nowrap">
|
|
||||||
Comment on {commentLabel(comment.selection)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LineCommentAnchor>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<Show when={commenting()}>
|
<Show when={commenting()}>
|
||||||
{(range) => (
|
{(range) => (
|
||||||
<Show when={draftTop() !== undefined}>
|
<Show when={draftTop() !== undefined}>
|
||||||
<LineCommentAnchor
|
<LineCommentEditor
|
||||||
top={draftTop()}
|
top={draftTop()}
|
||||||
open={true}
|
value={draft()}
|
||||||
variant="editor"
|
selection={commentLabel(range())}
|
||||||
onClick={() => textarea?.focus()}
|
onInput={setDraft}
|
||||||
|
onCancel={() => setCommenting(null)}
|
||||||
|
onSubmit={(comment) => {
|
||||||
|
const p = path()
|
||||||
|
if (!p) return
|
||||||
|
addCommentToContext({
|
||||||
|
file: p,
|
||||||
|
selection: range(),
|
||||||
|
comment,
|
||||||
|
origin: "file",
|
||||||
|
})
|
||||||
|
setCommenting(null)
|
||||||
|
}}
|
||||||
onPopoverFocusOut={(e) => {
|
onPopoverFocusOut={(e) => {
|
||||||
const target = e.relatedTarget as Node | null
|
const target = e.relatedTarget as Node | null
|
||||||
if (target && e.currentTarget.contains(target)) return
|
if (target && e.currentTarget.contains(target)) return
|
||||||
@@ -2093,79 +2095,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<textarea
|
|
||||||
ref={textarea}
|
|
||||||
classList={{
|
|
||||||
"w-full resize-vertical p-2 rounded-[6px] bg-surface-base text-text-strong text-12-regular leading-5 focus:outline-none": true,
|
|
||||||
"focus:shadow-xs-border-select": !draftError(),
|
|
||||||
"shadow-xs-border-critical-base": draftError(),
|
|
||||||
}}
|
|
||||||
rows={3}
|
|
||||||
placeholder="Add comment"
|
|
||||||
value={draft()}
|
|
||||||
onInput={(e) => {
|
|
||||||
setDraft(e.currentTarget.value)
|
|
||||||
setDraftError(false)
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
setCommenting(null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (e.key !== "Enter") return
|
|
||||||
if (e.shiftKey) return
|
|
||||||
e.preventDefault()
|
|
||||||
const value = draft().trim()
|
|
||||||
if (!value) {
|
|
||||||
setDraftError(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const p = path()
|
|
||||||
if (!p) return
|
|
||||||
addCommentToContext({
|
|
||||||
file: p,
|
|
||||||
selection: range(),
|
|
||||||
comment: value,
|
|
||||||
origin: "file",
|
|
||||||
})
|
|
||||||
setCommenting(null)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<div class="text-12-medium text-text-weak ml-1">
|
|
||||||
Commenting on {commentLabel(range())}
|
|
||||||
</div>
|
|
||||||
<div class="flex-1" />
|
|
||||||
<Button size="small" variant="ghost" onClick={() => setCommenting(null)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="primary"
|
|
||||||
onClick={() => {
|
|
||||||
const value = draft().trim()
|
|
||||||
if (!value) {
|
|
||||||
setDraftError(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const p = path()
|
|
||||||
if (!p) return
|
|
||||||
addCommentToContext({
|
|
||||||
file: p,
|
|
||||||
selection: range(),
|
|
||||||
comment: value,
|
|
||||||
origin: "file",
|
|
||||||
})
|
|
||||||
setCommenting(null)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Comment
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LineCommentAnchor>
|
|
||||||
</Show>
|
</Show>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -52,3 +52,64 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-text"] {
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
line-height: var(--line-height-x-large);
|
||||||
|
letter-spacing: var(--letter-spacing-normal);
|
||||||
|
color: var(--text-strong);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-label"],
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-editor-label"] {
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
line-height: var(--line-height-large);
|
||||||
|
letter-spacing: var(--letter-spacing-normal);
|
||||||
|
color: var(--text-weak);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-editor"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-textarea"] {
|
||||||
|
width: 100%;
|
||||||
|
resize: vertical;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--surface-base);
|
||||||
|
border: 1px solid var(--border-base);
|
||||||
|
color: var(--text-strong);
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
line-height: var(--line-height-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-textarea"]:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: var(--shadow-xs-border-select);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-actions"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="line-comment"] [data-slot="line-comment-editor-label"] {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Show, type JSX } from "solid-js"
|
import { onMount, Show, splitProps, type JSX } from "solid-js"
|
||||||
|
import { Button } from "./button"
|
||||||
import { Icon } from "./icon"
|
import { Icon } from "./icon"
|
||||||
|
|
||||||
export type LineCommentVariant = "default" | "editor"
|
export type LineCommentVariant = "default" | "editor"
|
||||||
@@ -52,3 +53,105 @@ export const LineCommentAnchor = (props: LineCommentAnchorProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LineCommentProps = Omit<LineCommentAnchorProps, "children" | "variant"> & {
|
||||||
|
comment: JSX.Element
|
||||||
|
selection: JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LineComment = (props: LineCommentProps) => {
|
||||||
|
const [split, rest] = splitProps(props, ["comment", "selection"])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LineCommentAnchor {...rest} variant="default">
|
||||||
|
<div data-slot="line-comment-content">
|
||||||
|
<div data-slot="line-comment-text">{split.comment}</div>
|
||||||
|
<div data-slot="line-comment-label">Comment on {split.selection}</div>
|
||||||
|
</div>
|
||||||
|
</LineCommentAnchor>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LineCommentEditorProps = Omit<LineCommentAnchorProps, "children" | "open" | "variant" | "onClick"> & {
|
||||||
|
value: string
|
||||||
|
selection: JSX.Element
|
||||||
|
onInput: (value: string) => void
|
||||||
|
onCancel: VoidFunction
|
||||||
|
onSubmit: (value: string) => void
|
||||||
|
placeholder?: string
|
||||||
|
rows?: number
|
||||||
|
autofocus?: boolean
|
||||||
|
cancelLabel?: string
|
||||||
|
submitLabel?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LineCommentEditor = (props: LineCommentEditorProps) => {
|
||||||
|
const [split, rest] = splitProps(props, [
|
||||||
|
"value",
|
||||||
|
"selection",
|
||||||
|
"onInput",
|
||||||
|
"onCancel",
|
||||||
|
"onSubmit",
|
||||||
|
"placeholder",
|
||||||
|
"rows",
|
||||||
|
"autofocus",
|
||||||
|
"cancelLabel",
|
||||||
|
"submitLabel",
|
||||||
|
])
|
||||||
|
|
||||||
|
const refs = {
|
||||||
|
textarea: undefined as HTMLTextAreaElement | undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
const focus = () => refs.textarea?.focus()
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
const value = split.value.trim()
|
||||||
|
if (!value) return
|
||||||
|
split.onSubmit(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (split.autofocus === false) return
|
||||||
|
requestAnimationFrame(focus)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LineCommentAnchor {...rest} open={true} variant="editor" onClick={() => focus()}>
|
||||||
|
<div data-slot="line-comment-editor">
|
||||||
|
<textarea
|
||||||
|
ref={(el) => {
|
||||||
|
refs.textarea = el
|
||||||
|
}}
|
||||||
|
data-slot="line-comment-textarea"
|
||||||
|
rows={split.rows ?? 3}
|
||||||
|
placeholder={split.placeholder ?? "Add comment"}
|
||||||
|
value={split.value}
|
||||||
|
onInput={(e) => split.onInput(e.currentTarget.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
split.onCancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key !== "Enter") return
|
||||||
|
if (e.shiftKey) return
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
submit()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div data-slot="line-comment-actions">
|
||||||
|
<div data-slot="line-comment-editor-label">Commenting on {split.selection}</div>
|
||||||
|
<Button size="small" variant="ghost" onClick={split.onCancel}>
|
||||||
|
{split.cancelLabel ?? "Cancel"}
|
||||||
|
</Button>
|
||||||
|
<Button size="small" variant="primary" disabled={split.value.trim().length === 0} onClick={submit}>
|
||||||
|
{split.submitLabel ?? "Comment"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LineCommentAnchor>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,68 +75,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="session-review-comment-content"] {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-text"] {
|
|
||||||
font-family: var(--font-family-sans);
|
|
||||||
font-size: var(--font-size-base);
|
|
||||||
font-weight: var(--font-weight-regular);
|
|
||||||
line-height: var(--line-height-x-large);
|
|
||||||
letter-spacing: var(--letter-spacing-normal);
|
|
||||||
color: var(--text-strong);
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-label"],
|
|
||||||
[data-slot="session-review-comment-draft-label"] {
|
|
||||||
font-family: var(--font-family-sans);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
line-height: var(--line-height-large);
|
|
||||||
letter-spacing: var(--letter-spacing-normal);
|
|
||||||
color: var(--text-weak);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-draft"] {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-textarea"] {
|
|
||||||
width: 100%;
|
|
||||||
max-width: min(380px, calc(100vw - 48px));
|
|
||||||
resize: vertical;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
background: var(--surface-base);
|
|
||||||
border: 1px solid var(--border-base);
|
|
||||||
color: var(--text-strong);
|
|
||||||
font-family: var(--font-family-sans);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
line-height: var(--line-height-large);
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: var(--shadow-xs-border-select);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-actions"] {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-comment-draft-label"] {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-slot="session-review-trigger-content"] {
|
[data-slot="session-review-trigger-content"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { RadioGroup } from "./radio-group"
|
|||||||
import { DiffChanges } from "./diff-changes"
|
import { DiffChanges } from "./diff-changes"
|
||||||
import { FileIcon } from "./file-icon"
|
import { FileIcon } from "./file-icon"
|
||||||
import { Icon } from "./icon"
|
import { Icon } from "./icon"
|
||||||
import { LineCommentAnchor } from "./line-comment"
|
import { LineComment, LineCommentEditor } from "./line-comment"
|
||||||
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
||||||
import { useDiffComponent } from "../context/diff"
|
import { useDiffComponent } from "../context/diff"
|
||||||
import { useI18n } from "../context/i18n"
|
import { useI18n } from "../context/i18n"
|
||||||
@@ -305,7 +305,6 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
<For each={props.diffs}>
|
<For each={props.diffs}>
|
||||||
{(diff) => {
|
{(diff) => {
|
||||||
let wrapper: HTMLDivElement | undefined
|
let wrapper: HTMLDivElement | undefined
|
||||||
let textarea: HTMLTextAreaElement | undefined
|
|
||||||
|
|
||||||
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === diff.file))
|
const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === diff.file))
|
||||||
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
const commentedLines = createMemo(() => comments().map((c) => c.selection))
|
||||||
@@ -396,7 +395,6 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
if (!range) return
|
if (!range) return
|
||||||
setDraft("")
|
setDraft("")
|
||||||
scheduleAnchors()
|
scheduleAnchors()
|
||||||
requestAnimationFrame(() => textarea?.focus())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@@ -565,7 +563,7 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
|
|
||||||
<For each={comments()}>
|
<For each={comments()}>
|
||||||
{(comment) => (
|
{(comment) => (
|
||||||
<LineCommentAnchor
|
<LineComment
|
||||||
id={comment.id}
|
id={comment.id}
|
||||||
top={positions()[comment.id]}
|
top={positions()[comment.id]}
|
||||||
onMouseEnter={() => setSelection({ file: comment.file, range: comment.selection })}
|
onMouseEnter={() => setSelection({ file: comment.file, range: comment.selection })}
|
||||||
@@ -578,83 +576,31 @@ export const SessionReview = (props: SessionReviewProps) => {
|
|||||||
openComment(comment)
|
openComment(comment)
|
||||||
}}
|
}}
|
||||||
open={isCommentOpen(comment)}
|
open={isCommentOpen(comment)}
|
||||||
>
|
comment={comment.comment}
|
||||||
<div data-slot="session-review-comment-content">
|
selection={selectionLabel(comment.selection)}
|
||||||
<div data-slot="session-review-comment-text">{comment.comment}</div>
|
/>
|
||||||
<div data-slot="session-review-comment-label">
|
|
||||||
Comment on {selectionLabel(comment.selection)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LineCommentAnchor>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|
||||||
<Show when={draftRange()}>
|
<Show when={draftRange()}>
|
||||||
{(range) => (
|
{(range) => (
|
||||||
<Show when={draftTop() !== undefined}>
|
<Show when={draftTop() !== undefined}>
|
||||||
<LineCommentAnchor
|
<LineCommentEditor
|
||||||
top={draftTop()}
|
top={draftTop()}
|
||||||
onClick={() => textarea?.focus()}
|
value={draft()}
|
||||||
open={true}
|
selection={selectionLabel(range())}
|
||||||
variant="editor"
|
onInput={setDraft}
|
||||||
>
|
onCancel={() => setCommenting(null)}
|
||||||
<div data-slot="session-review-comment-draft">
|
onSubmit={(comment) => {
|
||||||
<textarea
|
props.onLineComment?.({
|
||||||
ref={textarea}
|
file: diff.file,
|
||||||
data-slot="session-review-comment-textarea"
|
selection: range(),
|
||||||
rows={3}
|
comment,
|
||||||
placeholder="Add comment"
|
preview: selectionPreview(diff, range()),
|
||||||
value={draft()}
|
})
|
||||||
onInput={(e) => setDraft(e.currentTarget.value)}
|
setCommenting(null)
|
||||||
onKeyDown={(e) => {
|
}}
|
||||||
if (e.key === "Escape") {
|
/>
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
setCommenting(null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (e.key !== "Enter") return
|
|
||||||
if (e.shiftKey) return
|
|
||||||
e.preventDefault()
|
|
||||||
const value = draft().trim()
|
|
||||||
if (!value) return
|
|
||||||
props.onLineComment?.({
|
|
||||||
file: diff.file,
|
|
||||||
selection: range(),
|
|
||||||
comment: value,
|
|
||||||
preview: selectionPreview(diff, range()),
|
|
||||||
})
|
|
||||||
setCommenting(null)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div data-slot="session-review-comment-actions">
|
|
||||||
<div data-slot="session-review-comment-draft-label">
|
|
||||||
Commenting on {selectionLabel(range())}
|
|
||||||
</div>
|
|
||||||
<Button size="small" variant="ghost" onClick={() => setCommenting(null)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="primary"
|
|
||||||
disabled={draft().trim().length === 0}
|
|
||||||
onClick={() => {
|
|
||||||
const value = draft().trim()
|
|
||||||
if (!value) return
|
|
||||||
props.onLineComment?.({
|
|
||||||
file: diff.file,
|
|
||||||
selection: range(),
|
|
||||||
comment: value,
|
|
||||||
preview: selectionPreview(diff, range()),
|
|
||||||
})
|
|
||||||
setCommenting(null)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Comment
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LineCommentAnchor>
|
|
||||||
</Show>
|
</Show>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
Reference in New Issue
Block a user