Skip to content

Conversation

@pallavibakale
Copy link

Refactored the comments layout to be page-aware: comments are aligned to their corresponding document pages, positioned relative to the page container, and rendered with independent per-page scrolling to avoid overlaps and maintain stable layouts.

  • Align floating comments per page using PresentationEditor page bounds and per-page containers.
  • Normalize comment positions on each page to avoid overlap and render with scoped scrolling.
  • Expand tracked-change and regular comment bodies when active; keep floating comments column full height.
Screenshot

- group floating comments per page using PresentationEditor bounds
- normalize per-page positions and avoid overlaps in the sidebar
- expand tracked-change and standard comment bodies when active
- keep floating comments column full height for consistent layout
Copilot AI review requested due to automatic review settings January 22, 2026 21:23
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 88c4d41523

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +174 to +176
// Update the actual top for rendering
comments.forEach((comment) => {
comment.top = comment.relativeTop;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve absolute comment positions across layout updates

This block overwrites comment.top with the page‑relative value. Because processLocations is re‑invoked whenever comment positions refresh (the watchEffect depends on getFloatingComments/renderedSizes, which update on layout events), subsequent runs will treat the already‑relative comment.top as absolute and subtract pageTop again. That compounds the offset and can push comments upward/out of view after any layout update or zoom. Consider keeping an absolute top (e.g., absoluteTop) and deriving a separate render top each run, or recomputing from fresh bounds instead of mutating comment.top.

Useful? React with 👍 / 👎.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the floating comments layout to be page-aware, positioning comments relative to their corresponding document pages with independent per-page scrolling. This architectural change aims to prevent comment overlaps across pages and maintain stable layouts in multi-page documents.

Changes:

  • Refactored comment positioning from global to per-page coordinate system using PresentationEditor page bounds
  • Implemented independent scrollable containers for each page's comments
  • Added visual feedback by expanding comment bodies when active

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.

File Description
packages/superdoc/src/components/CommentsLayer/FloatingComments.vue Core refactoring: added page-aware positioning logic, per-page containers with independent scrolling, and updated comment positioning calculations
packages/superdoc/src/components/CommentsLayer/CommentDialog.vue Enhanced UX by adding collapsible comment bodies that expand when active
packages/superdoc/src/SuperDoc.vue Extended floating comments container to full height to support new per-page layout

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 231 to 242
<div
v-for="comment in renderedSizes"
:key="comment.id"
:style="getCommentPosition(comment)"
class="floating-comment"
v-for="(comments, pageIndex) in commentsByPage"
:key="`page-${pageIndex}`"
class="page-comments-container"
:data-page-index="pageIndex"
:style="getPageContainerStyle(pageIndex)"
:ref="
(el) => {
if (el) pageContainerRefs[pageIndex] = el;
}
"
>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page-comments-container elements are scrollable but may not be keyboard accessible. Consider adding tabindex="0" to make these containers focusable and keyboard-navigable, allowing users to scroll through comments using keyboard arrow keys.

Copilot uses AI. Check for mistakes.
const minTop = prev.top + prev.height + 15;
if (comment.top < minTop) {
comment.top = minTop;
const minTop = prev.relativeTop + prev.height + 15;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic number 15 represents the minimum spacing between comments but lacks explanation. Consider extracting it as a named constant for better code clarity. For example: const MIN_COMMENT_SPACING = 15;

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +87
const gapOffset = pageIndex * 24;
if (!position) {
return {
position: 'absolute',
top: `${pageIndex * 1056 + gapOffset}px`,
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pageIndex parameter in getPageContainerStyle receives a string from the v-for loop (object keys are strings), but it's used in numeric calculations (line 82: pageIndex * 24). While JavaScript's type coercion will convert it to a number, this implicit conversion reduces code clarity. Consider explicitly converting: const gapOffset = parseInt(pageIndex) * 24; or Number(pageIndex) * 24;

Suggested change
const gapOffset = pageIndex * 24;
if (!position) {
return {
position: 'absolute',
top: `${pageIndex * 1056 + gapOffset}px`,
const numericPageIndex = Number(pageIndex);
const gapOffset = numericPageIndex * 24;
if (!position) {
return {
position: 'absolute',
top: `${numericPageIndex * 1056 + gapOffset}px`,

Copilot uses AI. Check for mistakes.
Comment on lines 183 to +251
class="floating-comment"
:parent="parent"
:comment="comment.commentRef"
/>
>
<CommentDialog
:key="comment.id + commentsRenderKey"
class="floating-comment"
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class "floating-comment" is applied to both the wrapper div (line 247) and the CommentDialog component (line 251), creating nested elements with the same class. This could lead to unintended CSS cascading effects since both elements will have position: absolute. Consider using a more specific class name for one of these elements, such as "floating-comment-wrapper" for the outer div.

Copilot uses AI. Check for mistakes.
Comment on lines +283 to 286
.comments-dialog {
position: absolute;
min-width: 300px;
min-width: 290px;
}
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .comments-dialog class is defined here but appears to be unused in this component's template. The class is actually used in CommentDialog.vue, which has its own scoped styles defining the same class. Due to Vue's scoped styling, this definition won't affect the child component and appears to be dead code. Consider removing it to reduce confusion.

Copilot uses AI. Check for mistakes.
}
nextTick(processLocations);
});
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of the watch on activeComment means that floating comments will no longer automatically scroll into view when a comment becomes active. The old implementation called scrollIntoView on the active comment's element. With the new per-page container structure, this functionality should be re-implemented to scroll the appropriate page container to bring the active comment into view, otherwise users may lose the ability to automatically navigate to active comments.

Copilot uses AI. Check for mistakes.
Comment on lines 231 to 242
<div
v-for="comment in renderedSizes"
:key="comment.id"
:style="getCommentPosition(comment)"
class="floating-comment"
v-for="(comments, pageIndex) in commentsByPage"
:key="`page-${pageIndex}`"
class="page-comments-container"
:data-page-index="pageIndex"
:style="getPageContainerStyle(pageIndex)"
:ref="
(el) => {
if (el) pageContainerRefs[pageIndex] = el;
}
"
>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page-comments-container elements have scrollable overflow but lack ARIA attributes for accessibility. Consider adding role="region" and aria-label or aria-labelledby attributes to help screen reader users understand that these are scrollable comment regions for specific pages. For example: role="region" :aria-label="Comments for page ${parseInt(pageIndex) + 1}"

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +87
top: pageBounds.y || index * 1056, // fallback: approximate page height with gap
height: pageBounds.h || pageBounds.height || 1056,
pageIndex: index,
};
});
return positions;
};
// Get style for each page comment container
const getPageContainerStyle = computed(() => (pageIndex) => {
const position = pagePositions.value[pageIndex];
const gapOffset = pageIndex * 24;
if (!position) {
return {
position: 'absolute',
top: `${pageIndex * 1056 + gapOffset}px`,
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic numbers 1056 (page height) and 24 (gap offset) are used in multiple places without explanation. Consider extracting these as named constants at the top of the script section for better maintainability. For example: const DEFAULT_PAGE_HEIGHT = 1056; const PAGE_GAP = 24;

Copilot uses AI. Check for mistakes.
:style="getPageContainerStyle(pageIndex)"
:ref="
(el) => {
if (el) pageContainerRefs[pageIndex] = el;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ref assignment pattern pageContainerRefs[pageIndex] = el is missing .value. In Vue 3 composition API, refs created with ref() require accessing .value to update the underlying object. This should be pageContainerRefs.value[pageIndex] = el to correctly store the element references.

Suggested change
if (el) pageContainerRefs[pageIndex] = el;
if (el) pageContainerRefs.value[pageIndex] = el;

Copilot uses AI. Check for mistakes.
const presentation = PresentationEditor.getInstance(props.currentDocument.id);
if (!presentation) return {};
const pages = presentation.getPages();
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code assumes presentation.getPages() will always return an array, but if it returns null or undefined, calling .forEach() on line 65 will throw a runtime error. Consider adding a check: const pages = presentation.getPages() || []; or const pages = presentation.getPages(); if (!pages) return {};

Suggested change
const pages = presentation.getPages();
const pages = presentation.getPages() || [];

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant