From 6d8e9943837a73944911728a698138e23601d556 Mon Sep 17 00:00:00 2001 From: adamelmore <2363879+adamdottv@users.noreply.github.com> Date: Sat, 24 Jan 2026 09:09:23 -0600 Subject: fix(app): line selection fixes --- packages/ui/src/components/line-comment.css | 49 ++++++++ packages/ui/src/components/line-comment.tsx | 53 ++++++++ packages/ui/src/components/session-review.css | 171 ++++++++++---------------- packages/ui/src/components/session-review.tsx | 141 ++++++++++----------- packages/ui/src/styles/index.css | 1 + 5 files changed, 227 insertions(+), 188 deletions(-) create mode 100644 packages/ui/src/components/line-comment.css create mode 100644 packages/ui/src/components/line-comment.tsx (limited to 'packages/ui/src') diff --git a/packages/ui/src/components/line-comment.css b/packages/ui/src/components/line-comment.css new file mode 100644 index 000000000..1cdf78189 --- /dev/null +++ b/packages/ui/src/components/line-comment.css @@ -0,0 +1,49 @@ +[data-component="line-comment"] { + position: absolute; + right: 24px; + z-index: var(--line-comment-z, 30); +} + +[data-component="line-comment"] [data-slot="line-comment-button"] { + width: 20px; + height: 20px; + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + background: var(--icon-interactive-base); + box-shadow: var(--shadow-xs); + cursor: pointer; + border: none; +} + +[data-component="line-comment"] [data-component="icon"] { + color: var(--white); +} + +[data-component="line-comment"] [data-slot="line-comment-button"]:focus { + outline: none; +} + +[data-component="line-comment"] [data-slot="line-comment-button"]:focus-visible { + box-shadow: var(--shadow-xs-border-focus); +} + +[data-component="line-comment"] [data-slot="line-comment-popover"] { + position: absolute; + top: calc(100% + 4px); + right: -8px; + z-index: var(--line-comment-popover-z, 40); + min-width: 200px; + max-width: min(320px, calc(100vw - 48px)); + border-radius: 14px; + background: var(--surface-raised-stronger-non-alpha); + box-shadow: var(--shadow-lg-border-base); + padding: 12px; +} + +[data-component="line-comment"][data-variant="editor"] [data-slot="line-comment-popover"] { + width: 380px; + max-width: min(380px, calc(100vw - 48px)); + padding: 8px; +} diff --git a/packages/ui/src/components/line-comment.tsx b/packages/ui/src/components/line-comment.tsx new file mode 100644 index 000000000..a9e22036b --- /dev/null +++ b/packages/ui/src/components/line-comment.tsx @@ -0,0 +1,53 @@ +import { Show, type JSX } from "solid-js" +import { Icon } from "./icon" + +export type LineCommentVariant = "default" | "editor" + +export type LineCommentAnchorProps = { + id?: string + top?: number + open: boolean + variant?: LineCommentVariant + onClick?: JSX.EventHandlerUnion + onMouseEnter?: JSX.EventHandlerUnion + onPopoverFocusOut?: JSX.EventHandlerUnion + class?: string + popoverClass?: string + children: JSX.Element +} + +export const LineCommentAnchor = (props: LineCommentAnchorProps) => { + const hidden = () => props.top === undefined + const variant = () => props.variant ?? "default" + + return ( +
+ + +
+ {props.children} +
+
+
+ ) +} diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 746e6a139..b61248fec 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -75,17 +75,66 @@ overflow: hidden; } - [data-slot="session-review-comment-popover-content"] { - position: absolute; - top: calc(100% + 4px); - right: -8px; - z-index: 6; - min-width: 200px; - max-width: min(320px, calc(100vw - 48px)); - border-radius: 10px; - background-color: var(--surface-raised-stronger-non-alpha); - box-shadow: var(--shadow-lg-border-base); - padding: 12px; + [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"] { @@ -217,103 +266,7 @@ [data-slot="session-review-diff-wrapper"] { position: relative; overflow: hidden; - } - - [data-slot="session-review-comment-anchor"] { - position: absolute; - right: 12px; - z-index: 5; - } - - [data-slot="session-review-comment-button"] { - width: 20px; - height: 20px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - background: var(--icon-interactive-base); - box-shadow: var(--shadow-xs); - cursor: pointer; - - [data-slot="icon-svg"] { - color: var(--white); - } - - &:focus { - outline: none; - } - - &:focus-visible { - box-shadow: var(--shadow-xs-border-focus); - } - } - - [data-slot="session-review-comment-hover"] { - display: flex; - flex-direction: column; - gap: 6px; - max-width: 320px; - } - - [data-slot="session-review-comment-popover"] { - display: flex; - flex-direction: column; - gap: 6px; - } - - [data-slot="session-review-comment-hover-label"], - [data-slot="session-review-comment-popover-label"] { - font-family: var(--font-family-sans); - font-size: var(--font-size-small); - font-weight: var(--font-weight-medium); - color: var(--text-weak); - } - - [data-slot="session-review-comment-hover-text"], - [data-slot="session-review-comment-popover-text"] { - font-family: var(--font-family-sans); - font-size: var(--font-size-base); - font-weight: var(--font-weight-regular); - color: var(--text-strong); - white-space: pre-wrap; - } - - [data-slot="session-review-comment-preview"] { - margin: 0; - padding: 8px; - border-radius: var(--radius-sm); - background: var(--surface-base); - border: 1px solid color-mix(in oklch, var(--border-base) 55%, transparent); - color: var(--text-base); - font-family: var(--font-family-mono); - font-size: var(--font-size-small); - line-height: 1.4; - white-space: pre-wrap; - } - - [data-slot="session-review-comment-textarea"] { - width: 320px; - max-width: calc(100vw - 48px); - resize: vertical; - padding: 8px; - border-radius: var(--radius-sm); - background: var(--surface-base); - border: 1px solid color-mix(in oklch, var(--border-base) 55%, transparent); - color: var(--text-strong); - font-family: var(--font-family-sans); - font-size: var(--font-size-small); - line-height: 1.4; - - &:focus { - outline: none; - box-shadow: var(--shadow-xs-border-focus); - } - } - - [data-slot="session-review-comment-actions"] { - display: flex; - justify-content: flex-end; - gap: 8px; + --line-comment-z: 5; + --line-comment-popover-z: 6; } } diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 6891836ff..2b6d93e2d 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -4,6 +4,7 @@ import { RadioGroup } from "./radio-group" import { DiffChanges } from "./diff-changes" import { FileIcon } from "./file-icon" import { Icon } from "./icon" +import { LineCommentAnchor } from "./line-comment" import { StickyAccordionHeader } from "./sticky-accordion-header" import { useDiffComponent } from "../context/diff" import { useI18n } from "../context/i18n" @@ -559,71 +560,74 @@ export const SessionReview = (props: SessionReviewProps) => { {(comment) => ( -
setSelection({ file: comment.file, range: comment.selection })} + onClick={() => { + if (isCommentOpen(comment)) { + setOpened(null) + return + } + + openComment(comment) }} + open={isCommentOpen(comment)} > - - -
-
-
{comment.comment}
-
- Comment on {selectionLabel(comment.selection)} -
-
+
+
{comment.comment}
+
+ Comment on {selectionLabel(comment.selection)}
- -
+
+ )} {(range) => ( -
- -
-
-
+ textarea?.focus()} + open={true} + variant="editor" + > +
+