Skip to content

feat: allow docking the floating button to the left or right side#1076

Open
Sufyr wants to merge 5 commits intomengxi-ream:mainfrom
Sufyr:feat/floating-button-dock-side
Open

feat: allow docking the floating button to the left or right side#1076
Sufyr wants to merge 5 commits intomengxi-ream:mainfrom
Sufyr:feat/floating-button-dock-side

Conversation

@Sufyr
Copy link
Copy Markdown
Contributor

@Sufyr Sufyr commented Mar 7, 2026

Type of Changes

  • ✨ New feature (feat)
  • 🐛 Bug fix (fix)
  • 📝 Documentation change (docs)
  • 💄 UI/style change (style)
  • ♻️ Code refactoring (refactor)
  • ⚡ Performance improvement (perf)
  • ✅ Test related (test)
  • 🔧 Build or dependencies update (build)
  • 🔄 CI/CD related (ci)
  • 🌐 Internationalization (i18n)
  • 🧠 AI model related (ai)
  • 🔄 Revert a previous commit (revert)
  • 📦 Other changes that do not modify src or test files (chore)

Description

  • add a floating button dock-side setting in options so users can choose left or right
  • support dragging the floating button to the left or right side
  • fix the floating button dropdown layering issue so the menu remains visible above the page content

Related Issue

Closes #1038

How Has This Been Tested?

  • Added unit tests
  • Verified through manual testing

Screenshots

Before After
image image
https://github.com/user-attachments/assets/e9ada1ea-8305-40bb-b83f-b75eb6e2bf4f https://github.com/user-attachments/assets/e315deda-34d2-4fdb-b0de-adae8ab5afca
image image

Checklist

  • I have tested these changes locally
  • I have updated the documentation accordingly if necessary
  • My code follows the code style of this project
  • My changes do not break existing functionality
  • If my code was generated by AI, I have proofread and improved it as necessary.

Additional Information


Summary by cubic

Adds a Dock Side setting for the floating button so users can pin it to the left or right, switch sides by dragging, and see a drag preview. Polishes drag lifecycle, bounds, and menu layering for a smoother, predictable UI.

  • New Features

    • Added Dock Side option (left/right) in Floating Button settings and command palette; translations included.
    • Drag vertically and across the screen to change position and side; shows a drag preview; persists via floatingButton.anchor. Existing configs auto-migrate to right.
    • Layout adapts for left/right (button, controls, and indicators reposition).
  • Bug Fixes

    • Dropdown menu now renders above page content and the side panel with correct z-index and side-aware alignment.
    • Drag handling simplified with reliable listener cleanup, user-select restoration, a small move threshold, and clamped bounds (respecting side panel width) to prevent mis-clicks, stuck drags, and off-screen placement.

Written for commit 8857d56. Summary will update on new commits.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 7, 2026

🦋 Changeset detected

Latest commit: 8857d56

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@read-frog/extension Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Mar 7, 2026
@github-actions github-actions bot added the feat label Mar 7, 2026
@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 7, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

Read Frog - Open Source Immersive Translate

Configuration Schema and Migration
View Suggested Changes
@@ -33,7 +33,7 @@
 
 **Note:** The schema version and last modified time are tracked in the configuration's metadata (`meta`), not as fields inside the config object itself. This improves separation of user settings and system metadata, and is reflected in all import/export and sync operations.
 
-**Current schema version:** The latest `CONFIG_SCHEMA_VERSION` is **59**.
+**Current schema version:** The latest `CONFIG_SCHEMA_VERSION` is **60**.
 
 #### Configuration Initialization Optimization
 The `initializeConfig()` function optimizes storage operations by tracking changes during initialization:
@@ -538,6 +538,48 @@
 - This change provides users with a convenient way to disable hover translation without removing their hotkey preference
 
 This migration was introduced in PR #1068 to give users more control over the hover translation feature directly from the options page.
+
+#### Example: v59 to v60 Migration (Floating Button Anchor)
+The migration from v59 to v60 adds an `anchor` field to the `floatingButton` configuration, allowing users to choose whether the floating button docks to the left or right side of the screen.
+
+**Key changes:**
+- Adds `anchor` field (type: enum `"left" | "right"`) to `floatingButton` configuration
+- Default value is `"right"` to maintain backward compatibility with existing configurations
+- Users can configure the anchor side in the options page floating button settings
+- Users can also drag the floating button across the screen to switch between left and right sides
+- The floating button's horizontal position is automatically adjusted based on the anchor side
+
+The migration script adds this field with a default value of `"right"` to all existing configurations:
+
+```ts
+// migration-scripts/v059-to-v060.ts
+export function migrate(oldConfig: any): any {
+  const floatingButton = oldConfig.floatingButton
+
+  if (!floatingButton) {
+    return oldConfig
+  }
+
+  if (floatingButton.anchor) {
+    return oldConfig
+  }
+
+  return {
+    ...oldConfig,
+    floatingButton: {
+      ...floatingButton,
+      anchor: "right",
+    },
+  }
+}
+```
+
+**Implementation details:**
+- The UI includes an anchor selector in the Floating Button settings page
+- Dragging the floating button to the opposite side of the screen automatically updates the anchor setting
+- The `anchor` field works alongside the existing `position` field (vertical position) to fully determine the button's placement
+
+This migration provides users with greater control over floating button placement, accommodating both left-handed and right-handed preferences.
 
 #### Example: v57 to v58 Migration (Subtitle Position Persistence)
 The migration from v57 to v58 adds a `position` field to the `videoSubtitles` configuration to persist the subtitle overlay's drag position across page navigations.
@@ -930,6 +972,26 @@
 }
 ```
 
+#### v60 Example (Floating Button Anchor)
+```ts
+export const testSeries = {
+  'complex-config-from-v020': {
+    description: 'Adds anchor field to floatingButton config',
+    config: {
+      // ... other config fields
+      floatingButton: {
+        enabled: true,
+        position: 0.75,
+        anchor: 'right', // New field added in v060
+        disabledFloatingButtonPatterns: ['github.com'],
+        clickAction: 'panel',
+      },
+      // ...
+    },
+  },
+}
+```
+
 #### v58 Example (Subtitle Position Persistence)
 ```ts
 export const testSeries = {

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

5 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".changeset/wise-sides-dock.md">

<violation number="1" location=".changeset/wise-sides-dock.md:2">
P2: Changeset frontmatter is missing required `extensionVersion` minimum (1.15.0), which can mis-target extension update notifications.</violation>
</file>

<file name="src/utils/config/migration-scripts/v058-to-v059.ts">

<violation number="1" location="src/utils/config/migration-scripts/v058-to-v059.ts:9">
P2: Migration script can throw on invalid persisted config by dereferencing `oldConfig` before null/object checks.</violation>
</file>

<file name="src/entrypoints/side.content/components/floating-button/index.tsx">

<violation number="1" location="src/entrypoints/side.content/components/floating-button/index.tsx:78">
P2: Global drag listeners and `userSelect` lock are not lifecycle-cleaned on unmount/interruption; teardown relies only on `mouseup`.</violation>

<violation number="2" location="src/entrypoints/side.content/components/floating-button/index.tsx:97">
P2: Drag Y clamping fails when viewport is shorter than min+offset, allowing invalid (including negative) persisted button positions.</violation>
</file>

<file name="src/entrypoints/options/pages/floating-button/floating-button-anchor.tsx">

<violation number="1" location="src/entrypoints/options/pages/floating-button/floating-button-anchor.tsx:26">
P2: Using a spread of render-time `floatingButton` when writing atom state can overwrite newer sibling fields; update only the changed key to avoid lost updates.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@@ -0,0 +1,5 @@
---
"@read-frog/extension": patch
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

P2: Changeset frontmatter is missing required extensionVersion minimum (1.15.0), which can mis-target extension update notifications.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .changeset/wise-sides-dock.md, line 2:

<comment>Changeset frontmatter is missing required `extensionVersion` minimum (1.15.0), which can mis-target extension update notifications.</comment>

<file context>
@@ -0,0 +1,5 @@
+---
+"@read-frog/extension": patch
+---
+
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/entrypoints/side.content/components/floating-button/index.tsx">

<violation number="1" location="src/entrypoints/side.content/components/floating-button/index.tsx:64">
P2: Drag cleanup does not always reset `isDraggingButton`, which can leave the floating button stuck hidden/non-interactive.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@claude
Copy link
Copy Markdown

claude bot commented Mar 8, 2026

Code Review — PR #1076

Overall

Solid feature addition. The dock-side setting, drag-to-switch, and z-index fix are well-scoped. Migration, localization, and options UI all follow existing patterns. A few items worth addressing:


Issues

1. Left-anchor doesn't offset for side panel

When isLeftAnchor is true the container sets left: "0px". But if the side panel were ever docked on the left, the button would be hidden behind it. Currently the side panel is always on the right so this isn't a live bug — but the asymmetry between the two branches is worth a comment so future contributors don't trip on it.

2. Drag position can exceed viewport on resize

Position is stored as a ratio (0..1) and converted back via window.innerHeight. If the user resizes the window smaller after dragging, the clamped FLOATING_BUTTON_MAX_TOP_OFFSET = 200 might still place the button off-screen. Consider re-clamping on render or adding a CSS max() guard.

3. setIsSideOpen is imported but not in the diff's dependency array context

setIsSideOpen is captured inside handleButtonDragStart via closure, but handleButtonDragStart is recreated every render (not wrapped in useCallback). This is fine for correctness since setIsSideOpen is a stable Jotai setter, but it means the onMouseDown handler is a new reference each render. Low priority, but wrapping handleButtonDragStart in useCallback would avoid unnecessary re-binds.

4. useEffect cleanup race on drag end

useEffect(() => {
  if (isDraggingButton) return
  if (dragPosition !== null && dragAnchor !== null) {
    void setFloatingButton({ position: dragPosition, anchor: dragAnchor })
    setDragPosition(null)
    setDragAnchor(null)
  }
  if (dragPreview !== null) {
    setDragPreview(null)
  }
}, [dragAnchor, dragPosition, dragPreview, isDraggingButton, setFloatingButton])

This effect fires when dragPreview changes during a drag (since isDraggingButton flips to false first, then the state settles). Specifically: dragPreview is in the dependency array and is set on every mouse-move. While the if (isDraggingButton) return guard prevents writes during dragging, the effect still runs on every dragPreview update — that's extra work for React. Consider splitting the preview cleanup into the cleanupDragListeners callback instead of reacting to it in an effect.

5. clamp utility already exists in many libraries

Math.min(max, Math.max(min, value)) is correct. Just noting that if this pattern appears elsewhere in the codebase, a shared utility might be warranted. Fine as-is for now per YAGNI.


Positives

  • Migration script follows the frozen-snapshot convention correctly — no external imports.
  • positionerClassName on DropdownMenuContent is a clean extension of the shared component.
  • Drag preview with the ghost logo is a nice UX touch.
  • cleanupDragListeners + refs is a meaningful improvement over the old useEffect-based drag lifecycle — proper listener removal and userSelect restoration.
  • All 8 locale files updated consistently.
  • Test data (v059.ts) covers two representative config shapes.

Suggestions (non-blocking)

Area Suggestion
Accessibility The drag interaction is mouse-only. Consider keyboard support (e.g., arrow keys while focused) in a follow-up.
Test coverage No unit tests for the drag logic or side-switching behavior. The new FloatingButtonAnchor options component could use a basic render test.
Magic number DRAG_PREVIEW_SIZE = 44 doesn't match size-11 (which is 44px at default spacing, so it's correct — but a comment linking them would help).
Minor setIsSideOpen is destructured from useAtom but only the setter is used — useSetAtom would be more precise. (Pre-existing, not introduced by this PR.)

Verdict: Approve with minor suggestions. The core implementation is sound. The drag refactor is cleaner than the original, the migration is correct, and the UI adapts well for both sides. The effect dependency on dragPreview (item 4) is the most actionable item.

@claude
Copy link
Copy Markdown

claude bot commented Mar 8, 2026

PR Review: feat: allow docking the floating button to the left or right side

Overall Assessment

Taste Score: Good taste

Well-structured PR that adds left/right docking with drag-to-switch. Clean migration, good i18n coverage (8 locales), proper schema update. The drag rewrite fixes real lifecycle bugs from the original code.


Positive Highlights

  • Migration script follows the frozen snapshot pattern correctly -- no external imports, hardcoded defaults, proper guard for idempotency
  • Drag handling rewrite is a genuine improvement: proper cleanup via refs, user-select restoration, threshold-based drag detection before setting isDraggingButton, and viewport clamping
  • DropdownMenu modal={false} -- good catch to prevent the dropdown from blocking interaction when docked left
  • positionerClassName on DropdownMenuContent -- clean extension of the shared component, correctly composed via cn()

Issues and Suggestions

1. Bug risk: Left-anchor + side panel open

When the button is on the left side and the side panel is open, the button stays at left: 0px. The side panel opens on the right, so left-docked should be fine, but worth verifying there is no layout shift edge case where the button overlaps content.

2. clamp helper

Math.clamp is not standard JS yet, so the custom clamp function is necessary. Consider checking if a shared utility already exists in the codebase to avoid duplication.

3. Drag preview does not account for side panel width

The anchor detection correctly considers the side panel via availableWidth. However, the drag preview x clamp uses full window.innerWidth, so the preview could visually appear behind the side panel. Minor visual issue, not a blocker.

4. Schema: anchor is .optional().default("right")

This means existing configs without anchor get the default via Zod parsing. Combined with the migration script, there is double-coverage. This is fine -- belt and suspenders approach for data integrity.

5. Small viewport edge case

On viewports shorter than ~230px, window.innerHeight - FLOATING_BUTTON_MAX_TOP_OFFSET could be less than FLOATING_BUTTON_MIN_TOP, locking the button to a single vertical position. The Math.max guard handles this correctly. Acceptable edge case.


Security

No concerns. Changes are purely UI/config with no external data handling.

Performance

No concerns. The drag handler properly cleans up listeners. The pointer-events-none opacity-0 on the real button during drag (while showing a lightweight preview) avoids layout thrashing.

Test Coverage

  • Migration test data (v059.ts) exists with two test series -- good
  • The automated migration test runner will pick this up automatically
  • No unit tests for the drag behavior or anchor switching -- acceptable for UI interaction logic, but integration/e2e tests would add confidence

Summary

This is a well-executed feature PR. The drag lifecycle rewrite is a clear improvement over the original. Migration is correct. i18n is comprehensive. The code is readable and follows existing patterns. A few minor edge cases noted above but nothing blocking.

Recommendation: Approve

@Sufyr Sufyr force-pushed the feat/floating-button-dock-side branch 2 times, most recently from 130ff3e to 7af1f55 Compare March 8, 2026 11:07
@claude
Copy link
Copy Markdown

claude bot commented Mar 8, 2026

PR Review: feat: allow docking the floating button to the left or right side

Overall

Solid feature addition. Clean migration, good i18n coverage across all 8 locales, and the drag rework is a meaningful improvement. A few items worth addressing:


Code Quality

Good:

  • Migration script follows the frozen-snapshot pattern correctly — no external imports, all values hardcoded
  • clamp helper and named constants (DRAG_THRESHOLD, FLOATING_BUTTON_MIN_TOP, etc.) replace inline magic numbers — much better readability
  • cleanupDragListeners as a useCallback with proper ref-based listener tracking fixes the stale-closure issues the old useEffect had
  • positionerClassName on DropdownMenuContent is a clean extension of the shared component
  • Options page component (FloatingButtonAnchor) is minimal and focused

Suggestions:

  1. hiddenopacity-0 pointer-events-none on close button: The old code used hidden/block for the close (X) button visibility toggle. The new code uses opacity-0 pointer-events-none / opacity-100 pointer-events-auto. This is fine for the hover animation, but consider whether transition-opacity is needed for a smooth visual transition — currently the opacity snaps.

  2. type FloatingButtonAnchor: This type alias is declared but only used once (in dragAnchor state). Consider inlining "left" | "right" or reusing the Zod-inferred type from the config schema to stay DRY.


Potential Bugs

  1. attachSideClassName applies translate-x-0 regardless of anchor side: When docked left, the hidden buttons use -translate-x-12 and the main button uses -translate-x-5. The attachSideClassName forces translate-x-0 on hover/drag/open, which is correct. However, double-check that Tailwind's group-hover:translate-x-0 and the forced translate-x-0 don't conflict when both are present — they target the same CSS property so whichever has higher specificity wins. This seems fine in practice but warrants manual testing on both sides.

  2. Left-anchored button + side panel open: When the button is docked left and the side panel is open (right side), left: "0px" is used. This seems correct since the panel doesn't overlap the left side. But when docked right, the code accounts for sideContent.width. Is there a scenario where the side panel could open on the left? If so, the left-anchor case would need similar offset logic.

  3. setFloatingButton({ position: dragPosition, anchor: dragAnchor }) is a partial update: In the drag-end useEffect, only position and anchor are passed. Verify that the atom setter merges this with existing fields (like enabled, disabledFloatingButtonPatterns, clickAction). If the setter replaces the whole object, this would lose all other fields. The options page setter correctly spreads: { ...floatingButton, anchor: value } — ensure consistency.


Performance

  1. Multiple useState setters in handleMouseMove: On every mouse move, setDragPosition, setDragAnchor, and setDragPreview are called. React batches these in event handlers, but since these are called from a native addEventListener callback (not a React event), they may trigger 3 separate re-renders per mouse move in React 17. In React 18+, setState calls from any context are auto-batched, so this should be fine. Just confirming the project uses React 19 — no issue here.

  2. Drag preview re-renders the entire component: Since dragPreview state changes on every mouse move, the whole FloatingButton component re-renders. The actual drag preview element is cheap to render, but all the sibling DOM (dropdown, buttons, etc.) also re-renders. For a smooth 60fps drag this is likely fine, but if jank is observed, extracting the drag preview into a separate component with its own state would help.


Security

No concerns. No user input is used unsanitized, no new message passing or external data flows introduced.


Test Coverage

  1. Migration test data (v059.ts) is provided — good. Both test series include the expected output with anchor: "right".

  2. No unit tests for the drag behavior or anchor switching logic. The PR acknowledges "manual testing only." Given the complexity of the drag lifecycle (threshold detection, anchor inference from cursor position, cleanup on unmount, clamping), at minimum a test for the clamp function and the anchor-determination logic (clientX < availableWidth / 2 ? "left" : "right") would add value. These are pure calculations easily unit-tested.


Minor Nits

  1. The changeset labels this as patch. Since it adds a new user-facing feature (dock side setting + drag to switch), minor would be more semantically correct under semver.

  2. Some Chinese comments remain (// 拖拽结束时写入 storage, // 标记是否发生了移动). Not a blocker, but consider translating for consistency if the project prefers English comments.


Summary

The feature works well with a clean implementation. The main actionable items are #5 (verify partial update doesn't lose fields) and #9 (add basic unit tests for pure logic). Everything else is polish.

@claude
Copy link
Copy Markdown

claude bot commented Mar 11, 2026

PR Review: feat: allow docking the floating button to the left or right side

Overall this is a well-structured PR. The refactoring from useEffect-driven drag to imperative event listeners with ref-based cleanup is a clear improvement. Good i18n coverage across all locales and proper migration script.


Potential Bugs

1. Migration script uses truthiness check instead of value validation
src/utils/config/migration-scripts/v059-to-v060.ts:15

if (floatingButton.anchor) {
  return oldConfig
}

If anchor is somehow set to an invalid value (e.g. "center"), the migration skips it and the invalid value persists. Consider checking for valid values explicitly:

if (floatingButton.anchor === "left" || floatingButton.anchor === "right") {
  return oldConfig
}

2. modal={false} on DropdownMenu is an undocumented behavioral change
src/entrypoints/side.content/components/floating-button/index.tsx

The dropdown was changed to non-modal. This affects accessibility behavior (focus trapping, backdrop overlay, Escape key handling). If intentional, worth documenting in the PR description. If not intentional, this could degrade keyboard/screen-reader UX.


Code Quality

3. DRAG_PREVIEW_SIZE (44) may drift from CSS size-11 (also 44px)
src/entrypoints/side.content/components/floating-button/index.tsx:24

The drag preview uses size-11 in its className but DRAG_PREVIEW_SIZE = 44 for position clamping. These are the same value from different sources. If Tailwind config changes or the class is updated, they could diverge. Consider adding a comment linking the two or deriving from a single source.

4. attachSideClassName semantics are slightly confusing for left anchor

const attachSideClassName = isDraggingButton || isSideOpen || isDropdownOpen ? "translate-x-0" : ""

On the left side, the hidden state is -translate-x-5, and translate-x-0 correctly snaps it visible. But isSideOpen is semantically about the right-side panel offset -- applying it symmetrically to left anchor works by coincidence. A brief comment would help future readers.

5. Left-anchor positioning is hardcoded to left: "0px"
If the side panel ever opens on the left, the button would be hidden behind it. Not a bug today, but worth a // TODO comment for awareness.


Test Coverage

6. No tests for drag-to-switch-sides logic
The drag logic is the most complex part of this PR (threshold detection, anchor switching, preview positioning, cleanup). The migration test fixture (v060.ts) is good, but the core drag behavior is entirely untested. Consider adding at minimum:

  • Unit tests for the clamp utility
  • Component tests verifying anchor switching on drag across midpoint

Minor / Nits

7. Changeset uses patch but this is a feat
.changeset/wise-sides-dock.md — Per conventional commits, feat: typically maps to a minor bump. Confirm this is intentional for your versioning scheme.

8. Chinese comments
Pre-existing, but new code intermixes with them (e.g. // 标记是否发生了移动, // 拖拽结束时写入 storage). Consider translating in a follow-up for consistency.

9. Defensive onValueChange guard is redundant
src/entrypoints/options/pages/floating-button/floating-button-anchor.tsx:23-25

if (value !== "left" && value !== "right") { return }

RadioGroup will only emit values of its children. Not harmful, but unnecessary.


What looks good

  • Clean separation of drag state (dragPosition, dragAnchor, dragPreview) with proper cleanup via refs
  • Drag threshold prevents false drags on click -- nice UX improvement
  • useCallback + useEffect cleanup pattern is solid and avoids the previous eslint-disable for exhaustive deps
  • The HiddenButton and TranslateButton components properly accept a side prop and mirror their positioning
  • Good i18n: all 8 locale files updated consistently
  • Proper Zod schema with .optional().default("right") for backward compat
  • Type derivation via Config["floatingButton"]["anchor"] keeps types DRY

Verdict: Solid PR. The main actionable items are the migration truthiness check (#1), confirming the modal={false} change is intentional (#2), and the test coverage gap (#6). The rest are minor improvements.

)
const newPosition = newY / window.innerHeight
setDragPosition(newPosition)
const cleanupDragListeners = useCallback(() => {
Copy link
Copy Markdown
Collaborator

@iykon iykon Mar 15, 2026

Choose a reason for hiding this comment

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

cleanupDragListeners is wrapped in useCallback with [setIsDraggingButton] dep, setIsDraggingButton is from Jotai useAtom, it is not stable and can cause unnecessary cleanupDragListener call.
Recommend to do cleanup directly in handleMouseUp and unmount

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated in a new commit. Could you check if this resolves the issue?

Copy link
Copy Markdown
Collaborator

@iykon iykon left a comment

Choose a reason for hiding this comment

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

the rest of the changes LGTM.
can you check the conflicts?

@mengxi-ream
Copy link
Copy Markdown
Owner

@codex review this PR

Copy link
Copy Markdown

@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: 950f9fec37

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +155 to +158
y: clamp(
moveEvent.clientY,
DRAG_PREVIEW_SIZE / 2,
window.innerHeight - DRAG_PREVIEW_SIZE / 2,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Keep drag preview within final floating-button bounds

The drag preview Y position is clamped to the full viewport (DRAG_PREVIEW_SIZE / 2 to window.innerHeight - DRAG_PREVIEW_SIZE / 2), but the persisted button position is clamped much higher (FLOATING_BUTTON_MIN_TOP to window.innerHeight - FLOATING_BUTTON_MAX_TOP_OFFSET). When users drag near the bottom, the preview shows the button can be dropped there, then it snaps upward on mouseup, which makes placement feel inconsistent. Clamp preview Y with the same min/max used for newPosition so the preview reflects the actual drop result.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Sufyr can you check this? I can reproduce locally with the change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Sufyr can you check this? I can reproduce locally with the change

i think the current behavior is fine, need to restrict the draggable height to the final placement range?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

oh ok, it is consistent to current behaviour

@Sufyr Sufyr force-pushed the feat/floating-button-dock-side branch from 950f9fe to 8857d56 Compare March 17, 2026 17:19
@Sufyr
Copy link
Copy Markdown
Contributor Author

Sufyr commented Mar 17, 2026

the rest of the changes LGTM. can you check the conflicts?

conflicts are resolved now

@mengxi-ream
Copy link
Copy Markdown
Owner

感谢一直的改进,我过几天会看看这个 PR

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Mar 19, 2026
@github-actions github-actions bot added the contrib-trust:trusted PR author trust score is 60-79. label Apr 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Contributor trust score

23/100 — New contributor

This score estimates contributor familiarity with mengxi-ream/read-frog using public GitHub signals. It is advisory only and does not block merges automatically.

Outcome

Score breakdown

Dimension Score Signals
Repo familiarity 11/35 merged PRs, resolved PR history, reviews
Community standing 5/25 account age, followers, repo role
OSS influence 0/20 stars on owned non-fork repositories
PR track record 7/20 merge rate across resolved PRs in this repo

Signals used

  • Repo PR history: merged 2, open 1, closed-unmerged 0
  • Repo reviews: 0
  • Repo permission: read
  • Followers: 1
  • Account age: 39 months
  • Owned non-fork repos considered: max 0, total 0 (none)

Policy

  • Low-score review threshold: < 30
  • Auto-close: disabled in v1
  • Policy version: v1

Updated automatically when the PR changes or when a maintainer reruns the workflow.

@github-actions github-actions bot removed the contrib-trust:trusted PR author trust score is 60-79. label Apr 2, 2026
@github-actions github-actions bot added contrib-trust:moderate PR author trust score is 30-59. contrib-trust:new PR author trust score is 0-29. needs-maintainer-review Contributor trust automation recommends maintainer review. and removed contrib-trust:moderate PR author trust score is 30-59. labels Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contrib-trust:new PR author trust score is 0-29. feat lgtm This PR has been approved by a maintainer needs-maintainer-review Contributor trust automation recommends maintainer review. size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] 悬浮按钮可以自由设置左侧或者右侧吗

3 participants