Skip to content

Conversation

@VellerRider
Copy link

@VellerRider VellerRider commented Sep 19, 2025

Hi y'all, I noticed that when dragging and dropping a text block, two drop indicators appear between blocks: the “after” indicator of the upper block and the “before” indicator of the lower block. Although they look different visually, there is actually no functional difference when dropping a block.
I merged these two indicators into a single one that sits between blocks. I think this approach is more straightforward and less likely to confuse users. I'd love to know if you guys think this makes sense.
Thanks!

Summary by CodeRabbit

  • New Features
    • Improved drag-and-drop indicator when placing blocks before/after: the indicator now spans adjacent blocks to create a clearer, collapsible visual gap.
    • More accurate indicator positioning and sizing (left, width, and top) for better targeting between blocks.
    • Preserves existing behavior for “in” and edge-based placements while applying the enhanced visuals to before/after drops.

@CLAassistant
Copy link

CLAassistant commented Sep 19, 2025

CLA assistant check
All committers have signed the CLA.

@darkskygit darkskygit changed the title feat: merged before/after drop indicator feat(core): merged before/after drop indicator Oct 10, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Walkthrough

Implements a new geometry calculation for the drop indicator in _getDropResult for non-insert placements. When dropping before/after, it computes a collapsed visual indicator spanning adjacent blocks using derived line height and rect bounds; existing behavior for “in” and edge placements remains, with the final rect using the new indicator when applicable.

Changes

Cohort / File(s) Summary
Drag indicator geometry update
blocksuite/affine/widgets/drag-handle/src/watchers/drag-event-watcher.ts
Adds a new branch in _getDropResult to compute a collapsed before/after drop indicator across adjacent blocks, introducing indicatorLeft/Width/Top/lineHeight and constructing the final Rect from these values; preserves existing “in” and edge placement logic otherwise.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant D as DragEventWatcher
  participant G as Geometry Calculator
  participant UI as Drop Indicator

  U->>D: Drag block over target
  D->>G: _getDropResult(event, placement)
  alt placement == "in" or edge-based
    G-->>D: Compute existing rect (unchanged)
  else placement == "before"/"after"
    G->>G: Find parent and adjacent block
    G->>G: Measure rects, derive lineHeight
    G-->>D: Compute collapsed indicator (left, width, top)
  end
  D->>UI: Render indicator with final Rect
  note right of UI: Uses new indicator geometry when available
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • doodlewind
  • Saul-Mirone

Poem

A hop, a drop, a gentle glide,
I measure edges, side by side.
Between two blocks I draw a line—
A slender path, precisely fine.
Thump-thump! the cursor finds its mark,
A rabbit’s rule in UI’s arc. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and concisely summarizes the main change by indicating that the before and after drop indicators are merged into a single indicator in the core component, matching the pull request’s purpose.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 10, 2025

Codecov Report

❌ Patch coverage is 40.00000% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.76%. Comparing base (c63e3e7) to head (1769f23).
⚠️ Report is 1 commits behind head on canary.

Files with missing lines Patch % Lines
...ets/drag-handle/src/watchers/drag-event-watcher.ts 40.00% 12 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           canary   #13612   +/-   ##
=======================================
  Coverage   56.75%   56.76%           
=======================================
  Files        2755     2755           
  Lines      136963   136983   +20     
  Branches    20956    20965    +9     
=======================================
+ Hits        77730    77755   +25     
- Misses      57015    57020    +5     
+ Partials     2218     2208   -10     
Flag Coverage Δ
server-test 77.81% <ø> (+0.01%) ⬆️
unittest 31.97% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
blocksuite/affine/widgets/drag-handle/src/watchers/drag-event-watcher.ts (1)

287-294: Guard against index === -1 to avoid incorrect adjacent pick

If findIndex returns -1, placement === 'after' will read siblings[0], causing a wrong span. Add an index check before accessing siblings.

-          const siblings = parent.children;
-          const index = siblings.findIndex(child => child.id === dropModel.id);
-          const adjacentModel =
-            placement === 'before' ? siblings[index - 1] : siblings[index + 1];
+          const siblings = parent.children;
+          const index = siblings.findIndex(child => child.id === dropModel.id);
+          const adjacentModel =
+            index >= 0
+              ? (placement === 'before' ? siblings[index - 1] : siblings[index + 1])
+              : undefined;

Please verify if dropModel can ever be temporarily out of parent.children during drag; if so, we might also early-return to the simple (non-merged) indicator when index < 0.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c63e3e7 and 1769f23.

📒 Files selected for processing (1)
  • blocksuite/affine/widgets/drag-handle/src/watchers/drag-event-watcher.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
blocksuite/affine/widgets/drag-handle/src/watchers/drag-event-watcher.ts (2)
blocksuite/affine/shared/src/utils/dom/point-to-block.ts (1)
  • getRectByBlockComponent (266-269)
blocksuite/affine/widgets/page-dragging-area/src/utils.ts (1)
  • Rect (9-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (48)
  • GitHub Check: E2E Test (10)
  • GitHub Check: E2E Test (3)
  • GitHub Check: E2E Test (5)
  • GitHub Check: E2E Test (4)
  • GitHub Check: E2E Test (9)
  • GitHub Check: E2E Test (8)
  • GitHub Check: E2E Test (7)
  • GitHub Check: E2E Test (6)
  • GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
  • GitHub Check: E2E Test (2)
  • GitHub Check: E2E Test (1)
  • GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
  • GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
  • GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
  • GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
  • GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
  • GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
  • GitHub Check: Build @affine/electron renderer
  • GitHub Check: y-octo binding test on x86_64-apple-darwin
  • GitHub Check: Build AFFiNE native (x86_64-apple-darwin)
  • GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
  • GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
  • GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
  • GitHub Check: Build Server native
  • GitHub Check: fuzzing
  • GitHub Check: Run native tests
  • GitHub Check: loom thread test
  • GitHub Check: E2E Mobile Test (3)
  • GitHub Check: E2E Mobile Test (5)
  • GitHub Check: E2E BlockSuite Test (5)
  • GitHub Check: E2E Mobile Test (2)
  • GitHub Check: E2E Mobile Test (4)
  • GitHub Check: E2E BlockSuite Test (6)
  • GitHub Check: E2E Mobile Test (1)
  • GitHub Check: E2E BlockSuite Test (8)
  • GitHub Check: E2E BlockSuite Test (7)
  • GitHub Check: E2E BlockSuite Test (10)
  • GitHub Check: E2E BlockSuite Test (9)
  • GitHub Check: E2E BlockSuite Test (4)
  • GitHub Check: E2E BlockSuite Test (1)
  • GitHub Check: E2E BlockSuite Test (3)
  • GitHub Check: E2E BlockSuite Test (2)
  • GitHub Check: Lint
  • GitHub Check: Analyze (typescript, blocksuite)
  • GitHub Check: Analyze (javascript, blocksuite)
  • GitHub Check: Analyze (typescript, affine)
  • GitHub Check: Analyze (javascript, affine)
  • GitHub Check: Typecheck

Comment on lines +298 to +314
if (adjacentRect) {
// Collapse before/after visuals onto the same gap by spanning both blocks.
const upperRect = placement === 'before' ? adjacentRect : domRect;
const lowerRect = placement === 'before' ? domRect : adjacentRect;

const left = Math.min(upperRect.left, lowerRect.left);
const right = Math.max(
upperRect.left + upperRect.width,
lowerRect.left + lowerRect.width
);

indicatorLeft = left;
indicatorWidth = right - left;
indicatorTop = lowerRect.top - lineHeight * 2;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Merged indicator vertical offset likely off by one thickness

Using indicatorTop = lowerRect.top - lineHeight * 2 places the line one extra thickness above the gap. Aligning it to the boundary (just above the lower block) should be - lineHeight. If you want the line centered between blocks, compute the mid-gap.

Apply one of these:

  • Boundary-aligned (matches prior before/after behavior):
-              indicatorTop = lowerRect.top - lineHeight * 2;
+              indicatorTop = lowerRect.top - lineHeight;
  • Centered in the gap (if desired by design):
-              indicatorTop = lowerRect.top - lineHeight * 2;
+              const gapTop = upperRect.top + upperRect.height;
+              const gapBottom = lowerRect.top;
+              const mid = (gapTop + gapBottom) / 2;
+              indicatorTop = mid - lineHeight / 2;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (adjacentRect) {
// Collapse before/after visuals onto the same gap by spanning both blocks.
const upperRect = placement === 'before' ? adjacentRect : domRect;
const lowerRect = placement === 'before' ? domRect : adjacentRect;
const left = Math.min(upperRect.left, lowerRect.left);
const right = Math.max(
upperRect.left + upperRect.width,
lowerRect.left + lowerRect.width
);
indicatorLeft = left;
indicatorWidth = right - left;
indicatorTop = lowerRect.top - lineHeight * 2;
}
}
}
if (adjacentRect) {
// Collapse before/after visuals onto the same gap by spanning both blocks.
const upperRect = placement === 'before' ? adjacentRect : domRect;
const lowerRect = placement === 'before' ? domRect : adjacentRect;
const left = Math.min(upperRect.left, lowerRect.left);
const right = Math.max(
upperRect.left + upperRect.width,
lowerRect.left + lowerRect.width
);
indicatorLeft = left;
indicatorWidth = right - left;
indicatorTop = lowerRect.top - lineHeight;
}
Suggested change
if (adjacentRect) {
// Collapse before/after visuals onto the same gap by spanning both blocks.
const upperRect = placement === 'before' ? adjacentRect : domRect;
const lowerRect = placement === 'before' ? domRect : adjacentRect;
const left = Math.min(upperRect.left, lowerRect.left);
const right = Math.max(
upperRect.left + upperRect.width,
lowerRect.left + lowerRect.width
);
indicatorLeft = left;
indicatorWidth = right - left;
indicatorTop = lowerRect.top - lineHeight * 2;
}
}
}
if (adjacentRect) {
// Collapse before/after visuals onto the same gap by spanning both blocks.
const upperRect = placement === 'before' ? adjacentRect : domRect;
const lowerRect = placement === 'before' ? domRect : adjacentRect;
const left = Math.min(upperRect.left, lowerRect.left);
const right = Math.max(
upperRect.left + upperRect.width,
lowerRect.left + lowerRect.width
);
indicatorLeft = left;
indicatorWidth = right - left;
const gapTop = upperRect.top + upperRect.height;
const gapBottom = lowerRect.top;
const mid = (gapTop + gapBottom) / 2;
indicatorTop = mid - lineHeight / 2;
}
🤖 Prompt for AI Agents
In blocksuite/affine/widgets/drag-handle/src/watchers/drag-event-watcher.ts
around lines 298-314 the computed vertical position places the indicator one
extra thickness above the gap; replace the current indicatorTop assignment so it
either aligns to the boundary (indicatorTop = lowerRect.top - lineHeight) or is
centered in the gap by computing the mid-gap and offsetting half the line
thickness (indicatorTop = ((upperRect.top + upperRect.height) + lowerRect.top) /
2 - lineHeight / 2), choose the behavior required by design and remove the old -
lineHeight * 2 value.

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants