From a66dc12cb99b31cb10fbaee0db62f73b768dbe46 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Fri, 29 May 2026 22:02:44 -0700 Subject: [PATCH] fix: address review findings (#3461) --- AGENTS.md | 5 +- src/renderer/src/assets/main.css | 332 ++++++------ src/renderer/src/assets/markdown-preview.css | 241 ++++----- .../src/assets/rich-markdown-editor.css | 52 +- .../diff-comments/DiffCommentCard.tsx | 257 ++++----- .../diff-comments/DiffCommentPopover.tsx | 108 ++-- .../diff-comments/useDiffCommentDecorator.tsx | 14 +- .../src/components/editor/MarkdownPreview.tsx | 367 ++++++------- .../components/editor/RichMarkdownEditor.tsx | 492 ++++++++++++++---- .../rich-markdown-annotation-highlight.ts | 13 +- .../rich-markdown-review-note-layout.test.ts | 61 +++ .../rich-markdown-review-note-layout.ts | 65 +++ .../src/lib/markdown-review-notes.test.ts | 12 + src/renderer/src/lib/markdown-review-notes.ts | 19 + 14 files changed, 1265 insertions(+), 773 deletions(-) create mode 100644 src/renderer/src/components/editor/rich-markdown-review-note-layout.test.ts create mode 100644 src/renderer/src/components/editor/rich-markdown-review-note-layout.ts diff --git a/AGENTS.md b/AGENTS.md index fba60208e6b..21f7476b993 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Design System -All UI work — layout, color, typography, spacing, component selection, UX behavior — must follow [`docs/STYLEGUIDE.md`](./docs/STYLEGUIDE.md). Use the tokens defined in `src/renderer/src/assets/main.css` (the canonical source) and the shadcn primitives in `src/renderer/src/components/ui/`. Don't invent new color values, font sizes, or shadow tiers when a documented one already covers the role. When STYLEGUIDE.md is silent, follow the resolution order in its final section. +All UI work — layout, color, typography, spacing, component selection, UX behavior — must follow `[docs/STYLEGUIDE.md](./docs/STYLEGUIDE.md)`. Use the tokens defined in `src/renderer/src/assets/main.css` (the canonical source) and the shadcn primitives in `src/renderer/src/components/ui/`. Don't invent new color values, font sizes, or shadow tiers when a documented one already covers the role. When STYLEGUIDE.md is silent, follow the rewe solution order in its final section. ## Code Comments: Document the "Why", Briefly @@ -37,8 +37,9 @@ Source-control and review changes must consider GitLab and other supported git p ## GitHub CLI Usage Be mindful of the user's `gh` CLI API rate limit — batch requests where possible and avoid unnecessary calls. All code, commands, and scripts must be compatible with macOS, Linux, and Windows. + Never commit PR evidence images; attach them to the PR conversation instead. ## Type Declarations: Prefer `.ts` Over `.d.ts` -Project-owned type declarations belong in `.ts` files. `.d.ts` is reserved for ambient shims (e.g., `env.d.ts`, `vite/client.d.ts`). TypeScript's `skipLibCheck: true` setting applies globally, including to our own `.d.ts` files, which means any unresolved type reference in a `.d.ts` silently becomes `any` at its call sites. Write your types in `.ts` files so the compiler actually checks them. CI enforces this for `src/preload/` and `src/shared/` — see `docs/preload-typecheck-hole.md`. +Project-owned type declarations belong in `.ts` files. `.d.ts` is reserved for ambient shims (e.g., `env.d.ts`, `vite/client.d.ts`). TypeScript's `skipLibCheck: true` setting applies globally, including to our own `.d.ts` files, which means any unresolved type reference in a `.d.ts` silently becomes `any` at its call sites. Write your types in `.ts` files so the compiler actually checks them. CI enforces this for `src/preload/` and `src/shared/` — see `docs/preload-typecheck-hole.md`. \ No newline at end of file diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 8032f870617..4df13c3060b 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -2385,194 +2385,218 @@ html.onboarding-tour-start-transition::view-transition-new(root) { overlay first and never reach our buttons. Lifting the whole card creates a stacking context that keeps every nested control clickable. */ z-index: 1; - border: 1px solid color-mix(in srgb, var(--foreground) 22%, transparent); - border-left: 3px solid color-mix(in srgb, var(--foreground) 55%, transparent); - border-radius: 6px; - background: color-mix(in srgb, var(--foreground) 5%, var(--editor-surface)); - padding: 6px 8px 6px 10px; - font-family: var(--font-sans, system-ui, sans-serif); - box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 15%, transparent); + display: flex; + gap: 12px; + border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border)); + border-radius: 8px; + background: var(--background); + padding: 12px 14px; + font-family: var(--font-sans, system-ui, -apple-system, sans-serif); + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.04), + 0 4px 12px rgba(0, 0, 0, 0.03); + transition: + border-color 150ms ease, + box-shadow 150ms ease, + background-color 150ms ease; } .dark .orca-diff-comment-card { - background: color-mix(in srgb, var(--foreground) 6%, var(--editor-surface)); + border-color: color-mix(in srgb, var(--foreground) 12%, var(--border)); + background: color-mix(in srgb, var(--foreground) 2%, var(--editor-surface)); + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.2), + 0 4px 16px rgba(0, 0, 0, 0.15); } +/* Avatar Column */ +.orca-diff-comment-avatar-col { + flex: 0 0 auto; + display: flex; + flex-direction: column; + align-items: center; +} + +/* Content Column */ +.orca-diff-comment-content-col { + flex: 1 1 auto; + min-width: 0; + display: flex; + flex-direction: column; + gap: 6px; +} + +/* Header Row */ .orca-diff-comment-header { display: flex; align-items: center; justify-content: space-between; - gap: 8px; - margin-bottom: 4px; -} - -.orca-diff-comment-meta { - font-size: 11px; - color: var(--foreground); - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.03em; -} - -.orca-diff-comment-author-row { - display: flex; + gap: 12px; min-width: 0; +} + +/* Meta Group (Name, line, timestamp) */ +.orca-diff-comment-meta-group { + display: flex; align-items: center; + flex-wrap: wrap; gap: 6px; - margin: 0 0 6px; - color: var(--muted-foreground); - font-size: 11px; - line-height: 1.2; + min-width: 0; + font-size: 13px; + line-height: 1.4; + font-weight: 600; + color: var(--foreground); } -.orca-diff-comment-avatar { - width: 18px; - height: 18px; - flex: 0 0 auto; - border-radius: 50%; - border: 1px solid color-mix(in srgb, var(--foreground) 15%, transparent); - background: var(--muted); +/* Action button pill: segmented control like Notion's Resolve / ... */ +.orca-diff-comment-actions-pill { + display: inline-flex; + align-items: center; + border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border)); + border-radius: 6px; + background: var(--background); + overflow: hidden; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02); } -.orca-diff-comment-avatar-fallback { +.dark .orca-diff-comment-actions-pill { + border-color: color-mix(in srgb, var(--foreground) 14%, var(--border)); + background: color-mix(in srgb, var(--foreground) 4%, var(--editor-surface)); +} + +.orca-diff-comment-pill-btn { display: inline-flex; align-items: center; justify-content: center; + padding: 4px 8px; + font-size: 11px; + font-weight: 500; color: var(--muted-foreground); - font-size: 10px; + border: none; + background: transparent; + cursor: pointer; + height: 24px; + transition: + background-color 100ms ease, + color 100ms ease; +} + +.orca-diff-comment-pill-btn:hover { + background: color-mix(in srgb, var(--foreground) 6%, transparent); + color: var(--foreground); +} + +.orca-diff-comment-pill-btn-danger:hover { + background: color-mix(in srgb, var(--destructive) 10%, transparent); + color: var(--destructive); +} + +.orca-diff-comment-pill-divider { + width: 1px; + height: 12px; + background: color-mix(in srgb, var(--foreground) 12%, var(--border)); + flex-shrink: 0; +} + +/* Avatar design */ +.orca-diff-comment-avatar { + width: 24px; + height: 24px; + border-radius: 50%; + border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); + object-fit: cover; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.orca-diff-comment-avatar-fallback, +.orca-diff-comment-avatar-local { + width: 24px; + height: 24px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: color-mix(in srgb, var(--primary) 8%, var(--muted)); + border: 1px solid color-mix(in srgb, var(--primary) 20%, transparent); + color: var(--primary); + font-size: 11px; font-weight: 600; } -.orca-diff-comment-author { +.orca-diff-comment-avatar-local { + background: color-mix(in srgb, var(--foreground) 6%, var(--muted)); + border-color: color-mix(in srgb, var(--foreground) 12%, transparent); + color: var(--muted-foreground); +} + +/* Quote/Reference block: Warm, gorgeous Notion-like callout */ +.orca-diff-comment-quote { + display: flex; + align-items: flex-start; + gap: 8px; + margin: 4px 0 6px 0; + padding-left: 8px; + border-left: 2px solid color-mix(in srgb, #e7b10a 35%, transparent); + color: var(--muted-foreground); + font-size: 12.5px; + line-height: 1.4; +} + +.orca-diff-comment-quote-text { + flex: 1; min-width: 0; overflow: hidden; - color: var(--foreground); - font-weight: 600; text-overflow: ellipsis; - white-space: nowrap; -} - -.orca-diff-comment-created-at { - flex: 0 0 auto; -} - -.orca-diff-comment-link { - margin-left: auto; - flex: 0 0 auto; - border: 0; - background: transparent; - color: var(--muted-foreground); - font: inherit; - cursor: pointer; -} - -.orca-diff-comment-link:hover { - color: var(--foreground); - text-decoration: underline; -} - -/* Why: keep edit + delete close together but visually distinct from the meta - label. Both share the same subtle button styling; only delete recolours on - hover (destructive intent). */ -.orca-diff-comment-actions { - display: inline-flex; - align-items: center; - gap: 2px; -} - -.orca-diff-comment-edit { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 3px; - border-radius: 4px; - border: 1px solid transparent; - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - pointer-events: auto; - position: relative; - z-index: 1; -} - -.orca-diff-comment-edit:hover { - color: var(--foreground); - border-color: color-mix(in srgb, var(--foreground) 20%, transparent); - background: color-mix(in srgb, var(--foreground) 6%, transparent); -} - -/* Why: the trash button is always visible (not hover-only) because the saved - note is a persistent, action-bearing card — users should be able to see and - target the delete affordance without guessing that hovering reveals it. - Pointer-events are explicit since the card lives inside a Monaco view zone - where stray handlers higher in the tree have swallowed pointer events in - the past. z-index keeps it above the note body for click reliability. */ -.orca-diff-comment-delete { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 3px; - border-radius: 4px; - border: 1px solid transparent; - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - pointer-events: auto; - position: relative; - z-index: 1; -} - -.orca-diff-comment-delete:hover { - color: var(--destructive); - border-color: color-mix(in srgb, var(--destructive) 40%, transparent); - background: color-mix(in srgb, var(--destructive) 10%, transparent); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-all; } +/* Comment Body */ .orca-diff-comment-body { - font-size: 12px; - line-height: 1.4; + font-size: 13.5px; + line-height: 1.5; color: var(--foreground); white-space: pre-wrap; word-break: break-word; + margin-top: 2px; } -.orca-diff-comment-review-body { - padding-left: 24px; -} - -/* Why: mirror the saved-note card treatment so the "entering" and "saved" - states read as the same object at two stages of the same flow. Same - foreground-tinted fill over --editor-surface, same border + left accent - bar, same shadow. Kept one level stronger on the shadow because the - popover floats above the editor instead of being laid into a view zone. */ +/* Popover for adding new comment */ .orca-diff-comment-popover { position: absolute; left: 56px; right: 24px; max-width: 420px; z-index: 1000; - padding: 8px 8px 8px 10px; - border: 1px solid color-mix(in srgb, var(--foreground) 22%, transparent); - border-left: 3px solid color-mix(in srgb, var(--foreground) 55%, transparent); - border-radius: 6px; - background: color-mix(in srgb, var(--foreground) 5%, var(--editor-surface)); + padding: 12px 14px; + border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border)); + border-radius: 8px; + background: var(--background); color: var(--foreground); - box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18); + box-shadow: + 0 10px 32px rgba(0, 0, 0, 0.12), + 0 4px 12px rgba(0, 0, 0, 0.08); display: flex; - flex-direction: column; - gap: 6px; + gap: 12px; } .dark .orca-diff-comment-popover { - background: color-mix(in srgb, var(--foreground) 6%, var(--editor-surface)); + border-color: color-mix(in srgb, var(--foreground) 14%, var(--border)); + background: color-mix(in srgb, var(--foreground) 2%, var(--editor-surface)); + box-shadow: + 0 10px 32px rgba(0, 0, 0, 0.3), + 0 4px 16px rgba(0, 0, 0, 0.2); } .orca-diff-comment-popover-label { - font-size: 10px; + font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); + margin-bottom: 2px; } .orca-diff-comment-popover-textarea { @@ -2581,19 +2605,28 @@ html.onboarding-tour-start-transition::view-transition-new(root) { max-height: 240px; overflow-y: auto; resize: none; - padding: 6px 8px; - border: 1px solid color-mix(in srgb, var(--foreground) 18%, transparent); - border-radius: 4px; - /* Why: match the editor surface so the input reads as embedded in the card - rather than a pure-white/pure-black cutout pasted on top. */ - background: var(--editor-surface); + padding: 8px 12px; + border: 1px solid color-mix(in srgb, var(--foreground) 12%, var(--border)); + border-radius: 6px; + background: var(--background); color: var(--foreground); font-family: inherit; - font-size: 12px; - line-height: 1.4; + font-size: 13px; + line-height: 1.45; outline: none; - scrollbar-width: thin; - scrollbar-color: color-mix(in srgb, var(--muted-foreground, #737373) 34%, transparent) transparent; + transition: + border-color 150ms ease, + box-shadow 150ms ease; +} + +.dark .orca-diff-comment-popover-textarea { + border-color: color-mix(in srgb, var(--foreground) 16%, var(--border)); + background: color-mix(in srgb, var(--foreground) 3%, var(--editor-surface)); +} + +.orca-diff-comment-popover-textarea:focus { + border-color: var(--primary); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 15%, transparent); } .orca-diff-comment-popover-textarea::-webkit-scrollbar { @@ -2610,13 +2643,10 @@ html.onboarding-tour-start-transition::view-transition-new(root) { background-clip: padding-box; } -.orca-diff-comment-popover-textarea:focus { - border-color: color-mix(in srgb, var(--primary) 60%, var(--border)); -} - .orca-diff-comment-popover-footer { display: flex; align-items: center; justify-content: flex-end; - gap: 6px; + gap: 8px; + margin-top: 4px; } diff --git a/src/renderer/src/assets/markdown-preview.css b/src/renderer/src/assets/markdown-preview.css index ac0ca68f29f..671d01893d8 100644 --- a/src/renderer/src/assets/markdown-preview.css +++ b/src/renderer/src/assets/markdown-preview.css @@ -211,8 +211,7 @@ } .markdown-review-toolbar { - position: sticky; - top: 0; + position: relative; z-index: 15; display: flex; width: fit-content; @@ -254,7 +253,7 @@ padding: 0; } -.markdown-review-toolbar-button:hover, +.markdown-review-toolbar-button:hover:not(:disabled), .markdown-review-toolbar-button[aria-expanded='true'], .markdown-review-icon-button:hover:not(:disabled) { border-color: color-mix(in srgb, var(--border) 82%, transparent); @@ -262,6 +261,11 @@ color: var(--foreground); } +.markdown-review-toolbar-button:disabled { + cursor: default; + opacity: 0.48; +} + .markdown-review-icon-button:disabled { cursor: default; opacity: 0.45; @@ -281,157 +285,49 @@ line-height: 1; } -.markdown-review-panel { - display: flex; - width: 310px; - min-width: 260px; - max-width: 36%; - flex-shrink: 0; - flex-direction: column; - border-left: 1px solid color-mix(in srgb, var(--border) 72%, transparent); - background: color-mix(in srgb, var(--background) 90%, var(--editor-surface)); -} - -.markdown-review-panel-header { - display: flex; - min-height: 40px; - align-items: center; - justify-content: space-between; - gap: 8px; - border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent); - padding: 0 8px 0 12px; -} - -.markdown-review-panel-title, -.markdown-review-panel-actions { - display: inline-flex; - min-width: 0; - align-items: center; - gap: 6px; -} - -.markdown-review-panel-title { - color: var(--foreground); - font-size: 12px; - font-weight: 600; -} - -.markdown-review-panel-actions { - flex-shrink: 0; - gap: 2px; -} - -.markdown-review-note-list { - min-height: 0; - flex: 1; - overflow-y: auto; - padding: 8px; -} - -.markdown-review-empty { - padding: 8px 4px; - color: var(--muted-foreground); - font-size: 12px; - line-height: 1.45; -} - -.markdown-review-note { - border: 1px solid transparent; - border-radius: 7px; - padding: 6px; -} - -.markdown-review-note + .markdown-review-note { - margin-top: 8px; -} - -.markdown-review-note.is-active { - border-color: color-mix(in srgb, var(--foreground) 28%, transparent); - background: color-mix(in srgb, var(--foreground) 5%, transparent); -} - -.markdown-review-note-anchor { - display: flex; - width: 100%; - min-width: 0; - align-items: flex-start; - gap: 7px; - border: 0; - border-radius: 5px; - background: transparent; - padding: 4px 5px 6px; - color: inherit; - cursor: pointer; - text-align: left; -} - -.markdown-review-note-anchor:hover { - background: var(--accent); -} - -.markdown-review-note-line { - flex-shrink: 0; - color: var(--muted-foreground); - font-size: 11px; - font-variant-numeric: tabular-nums; - font-weight: 700; - line-height: 1.35; -} - -.markdown-review-note-excerpt { - min-width: 0; - display: -webkit-box; - overflow: hidden; - color: var(--muted-foreground); - font-size: 11px; - line-height: 1.35; - white-space: pre-wrap; - word-break: break-word; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; -} - -.markdown-review-note .orca-diff-comment-card { - max-width: none; -} - -@media (max-width: 900px) { - .markdown-review-panel { - width: 260px; - max-width: 48%; - } -} - -@container (max-width: 560px) { - .markdown-review-panel { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 32; - width: min(310px, 82cqw); - min-width: 0; - max-width: calc(100cqw - 36px); - box-shadow: -10px 0 24px rgb(0 0 0 / 0.14); - } -} - -.markdown-annotation-block { +.markdown-annotation-block, +.markdown-annotation-list-block { position: relative; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(260px, min(34cqw, 360px)); + column-gap: 32px; + align-items: start; +} + +.markdown-annotation-block > :not(.markdown-annotation-controls), +.markdown-annotation-list-content { + grid-column: 1; + min-width: 0; +} + +.markdown-annotation-block.has-review-notes > :not(.markdown-annotation-controls), +.markdown-annotation-list-block.has-review-notes > .markdown-annotation-list-content { + cursor: pointer; } .markdown-annotation-block:hover > .markdown-annotation-controls > .markdown-annotation-add, .markdown-annotation-block:focus-within > .markdown-annotation-controls > .markdown-annotation-add, -li:hover > .markdown-annotation-controls > .markdown-annotation-add, -li:focus-within > .markdown-annotation-controls > .markdown-annotation-add { +.markdown-annotation-list-block:hover > .markdown-annotation-controls > .markdown-annotation-add, +.markdown-annotation-list-block:focus-within + > .markdown-annotation-controls + > .markdown-annotation-add { opacity: 1; } .markdown-annotation-controls { - margin-top: 6px; + /* Why: note cards must share the document scroll with their source block; + laying the rail into each block avoids a separate right-side viewport. */ + position: relative; + grid-column: 2; + grid-row: 1; + min-width: 0; + margin-top: 0; } .markdown-annotation-add { + position: absolute; + top: 2px; + left: -26px; display: inline-flex; width: 20px; height: 20px; @@ -462,8 +358,20 @@ li:focus-within > .markdown-annotation-controls > .markdown-annotation-add { .markdown-annotation-composer, .markdown-annotation-card { - max-width: 520px; - margin-top: 6px; + max-width: none; + margin-top: 0; + scroll-margin: 12px; +} + +.markdown-annotation-note-stack { + display: flex; + min-width: 0; + flex-direction: column; + gap: 8px; +} + +.markdown-annotation-composer + .markdown-annotation-note-stack { + margin-top: 8px; } .markdown-annotation-card.is-active > .orca-diff-comment-card { @@ -471,6 +379,31 @@ li:focus-within > .markdown-annotation-controls > .markdown-annotation-add { background: color-mix(in srgb, var(--foreground) 8%, var(--editor-surface)); } +.markdown-annotation-card.is-attention > .orca-diff-comment-card { + animation: markdown-note-attention 900ms ease-out; +} + +@keyframes markdown-note-attention { + 0% { + transform: translateX(0); + box-shadow: + 0 0 0 1px color-mix(in srgb, var(--ring) 55%, transparent), + 0 0 0 8px color-mix(in srgb, var(--ring) 16%, transparent); + } + + 35% { + transform: translateX(-3px); + box-shadow: + 0 0 0 1px color-mix(in srgb, var(--ring) 45%, transparent), + 0 0 0 5px color-mix(in srgb, var(--ring) 12%, transparent); + } + + 100% { + transform: translateX(0); + box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 15%, transparent); + } +} + .markdown-annotation-composer { border: 1px solid color-mix(in srgb, var(--foreground) 18%, transparent); border-radius: 6px; @@ -479,6 +412,26 @@ li:focus-within > .markdown-annotation-controls > .markdown-annotation-add { box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent); } +@container (max-width: 760px) { + .markdown-annotation-block, + .markdown-annotation-list-block { + display: block; + } + + .markdown-annotation-controls { + margin-top: 6px; + } + + .markdown-annotation-add { + position: static; + } + + .markdown-annotation-composer, + .markdown-annotation-note-stack { + margin-top: 6px; + } +} + .markdown-body h1, .markdown-body h2, .markdown-body h3, diff --git a/src/renderer/src/assets/rich-markdown-editor.css b/src/renderer/src/assets/rich-markdown-editor.css index a8605cda74d..23dd55cd508 100644 --- a/src/renderer/src/assets/rich-markdown-editor.css +++ b/src/renderer/src/assets/rich-markdown-editor.css @@ -73,6 +73,27 @@ -webkit-box-decoration-break: clone; } +.rich-markdown-annotation-selection-active { + animation: rich-markdown-source-attention 900ms ease-out; +} + +@keyframes rich-markdown-source-attention { + 0% { + background: color-mix(in srgb, var(--annotation-highlight) 44%, transparent); + box-shadow: 0 0 0 4px color-mix(in srgb, var(--annotation-highlight) 16%, transparent); + } + + 42% { + background: color-mix(in srgb, var(--annotation-highlight) 34%, transparent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--annotation-highlight) 12%, transparent); + } + + 100% { + background: color-mix(in srgb, var(--annotation-highlight) 22%, transparent); + box-shadow: none; + } +} + .rich-markdown-editor-shell.has-rich-markdown-review-notes .rich-markdown-editor { padding-right: min(360px, 34%); } @@ -129,7 +150,6 @@ position: absolute; top: 0; right: 16px; - bottom: 0; width: min(300px, 30%); pointer-events: none; z-index: 30; @@ -142,6 +162,36 @@ pointer-events: auto; } +.rich-markdown-review-note-card.is-active > .orca-diff-comment-card { + border-color: color-mix(in srgb, var(--foreground) 32%, transparent); + background: color-mix(in srgb, var(--foreground) 8%, var(--editor-surface)); +} + +.rich-markdown-review-note-card.is-attention > .orca-diff-comment-card { + animation: rich-markdown-note-attention 900ms ease-out; +} + +@keyframes rich-markdown-note-attention { + 0% { + transform: translateX(0); + box-shadow: + 0 0 0 1px color-mix(in srgb, var(--ring) 55%, transparent), + 0 0 0 8px color-mix(in srgb, var(--ring) 16%, transparent); + } + + 35% { + transform: translateX(-3px); + box-shadow: + 0 0 0 1px color-mix(in srgb, var(--ring) 45%, transparent), + 0 0 0 5px color-mix(in srgb, var(--ring) 12%, transparent); + } + + 100% { + transform: translateX(0); + box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 15%, transparent); + } +} + .rich-markdown-review-note-send { display: inline-flex; align-items: center; diff --git a/src/renderer/src/components/diff-comments/DiffCommentCard.tsx b/src/renderer/src/components/diff-comments/DiffCommentCard.tsx index 310acb3d03b..efc2a232f20 100644 --- a/src/renderer/src/components/diff-comments/DiffCommentCard.tsx +++ b/src/renderer/src/components/diff-comments/DiffCommentCard.tsx @@ -1,8 +1,7 @@ -import { CornerDownLeft, Pencil, Trash } from 'lucide-react' +import { CornerDownLeft, Pencil, Trash, FileText } from 'lucide-react' import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react' import { Button } from '@/components/ui/button' import { getDiffCommentLineLabel } from '@/lib/diff-comment-compat' -import { cn } from '@/lib/utils' // Why: the saved-note card lives inside a Monaco view zone's DOM node. // useDiffCommentDecorator creates a React root per zone and renders this @@ -17,11 +16,11 @@ import { cn } from '@/lib/utils' type Props = { lineNumber: number startLine?: number - label?: string + label?: string | null + quote?: string body: string sentAt?: number author?: string - authorAvatarUrl?: string createdAtLabel?: string url?: string onDelete?: () => void @@ -38,10 +37,10 @@ export function DiffCommentCard({ lineNumber, startLine, label, + quote, body, sentAt, author, - authorAvatarUrl, createdAtLabel, url, onDelete, @@ -107,7 +106,11 @@ export function DiffCommentCard({ const trimmedDraft = draft.trim() const canSubmit = !submitting && trimmedDraft.length > 0 && trimmedDraft !== body - const lineLabel = label ?? getDiffCommentLineLabel({ lineNumber, startLine }).toLowerCase() + const lineLabel = + label === undefined ? getDiffCommentLineLabel({ lineNumber, startLine }).toLowerCase() : label + const metaText = [author || 'Note', lineLabel, createdAtLabel || (sentAt ? 'sent' : null)] + .filter(Boolean) + .join(' ') const handleSubmit = async (): Promise => { if (!canSubmit || !onSubmitEdit) { @@ -132,135 +135,135 @@ export function DiffCommentCard({ return (
-
- - {author ? 'Review comment' : 'Note'} · {lineLabel} - {sentAt ? ' · sent' : ''} - -
- {!editing && headerActions} - {onSubmitEdit && !editing && ( - - )} - {onDelete && !editing && ( - + {headerActions} + {headerActions && (url || onSubmitEdit || onDelete) && ( + + )} + {url && ( + <> + + {(onSubmitEdit || onDelete) && ( + + )} + + )} + {onSubmitEdit && ( + <> + + {onDelete && } + + )} + {onDelete && ( + + )} +
)}
-
- {author ? ( -
- {authorAvatarUrl ? ( - - ) : ( - - {author.slice(0, 1).toUpperCase()} - - )} - {author} - {createdAtLabel ? ( - {createdAtLabel} - ) : null} - {url ? ( -