Skip to content

refactor(views): extract layout state + decompose ManuscriptView#179

Merged
qnbs merged 8 commits into
mainfrom
refactor/view-layout-extraction
Jun 20, 2026
Merged

refactor(views): extract layout state + decompose ManuscriptView#179
qnbs merged 8 commits into
mainfrom
refactor/view-layout-extraction

Conversation

@qnbs

@qnbs qnbs commented Jun 17, 2026

Copy link
Copy Markdown
Owner

User description

Context

Phase 3 of the architecture refactor β€” separate layout state from rendering in the two largest view orchestrators, and split ManuscriptView's responsive markup into focused components.

What changed

ManuscriptView

  • hooks/useManuscriptLayout.ts (new) β€” owns nav tab, mobile drawers, focus mode, the useResizablePanels state, and the Escape-closes-research-split effect.
  • components/manuscript/ManuscriptMobileLayout.tsx (new) β€” mobile header + full-bleed editor + nav/inspector drawers.
  • components/manuscript/ManuscriptDesktopLayout.tsx (new) β€” 3-pane navigator Β· editor+research Β· inspector + focus toggle.
  • ManuscriptView.tsx β€” now a thin orchestrator: guards (spinner/empty), then renders both responsive layouts.

WriterViewUI

  • hooks/useWriterLayout.ts (new) β€” mobile tab, panel collapse, focus mode, flow-mode Escape handler, swipe-to-switch wiring. Component rendering unchanged.

Invariants preserved (no behavior change)

Responsive show/hide (md: classes), ARIA tablists/aria-selected, mobile drawers, container-query containerType, min touch targets, and all keyboard handlers are identical. Per-viewport DOM order is unchanged (only one layout is visible at a time).

Verification

  • pnpm run typecheck βœ“
  • pnpm run lint (--error-on-warnings) βœ“ β€” 0 warnings
  • pnpm exec vitest run βœ“ β€” 38 tests pass (ManuscriptView 6, WriterView, useWriterView 20)

πŸ€– Generated with Claude Code


CodeAnt-AI Description

Make manuscript resizing and view switching stay in sync across screen sizes

What Changed

  • Manuscript now shows only the mobile or desktop layout that matches the current screen size, so resizing no longer leaves the wrong layout or an open mobile drawer visible on desktop
  • The manuscript editor mounts only once at a time, which avoids duplicate side effects when switching between mobile and desktop
  • Desktop panel resizing now keeps the center editor usable by preventing the side panels from growing too wide together
  • Writer swipe navigation and flow-mode Escape handling were kept working while the view state was moved into shared layout hooks
  • Older browsers and runtimes without full media-query support now still switch layouts correctly instead of getting stuck on the mobile view

Impact

βœ… Fewer layout glitches after resizing
βœ… Fewer duplicate editor side effects
βœ… Wider browser support for responsive layouts

πŸ’‘ Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…ering

P3 of the architecture refactor.

ManuscriptView: split ManuscriptViewUI into ManuscriptMobileLayout
(header + full-bleed editor + drawers) and ManuscriptDesktopLayout
(3-pane navigator Β· editor+research Β· inspector). Shared UI state
(nav tab, mobile drawers, focus mode, resizable panels, research-split
Escape handler) moves into hooks/useManuscriptLayout.ts; the view is now
a thin orchestrator that renders both responsive layouts.

WriterViewUI: extract local state (mobile tab, panel collapse, focus
mode, flow-mode Escape handler, swipe-to-switch wiring) into
hooks/useWriterLayout.ts; rendering unchanged.

No behavior change β€” responsive show/hide, ARIA tablists, drawers, and
keyboard handlers are preserved. Verified: typecheck, lint (0 warnings),
38 tests pass (ManuscriptView, WriterView, useWriterView).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldscript-studio Ready Ready Preview, Comment Jun 20, 2026 10:54am

@codeant-ai

codeant-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jun 17, 2026
Comment thread components/manuscript/ManuscriptMobileLayout.tsx
Comment thread components/ManuscriptView.tsx Outdated
ManuscriptViewUI mounted both ManuscriptMobileLayout and ManuscriptDesktopLayout
unconditionally, so two ManuscriptEditor trees ran at once (duplicate voice/window
effects) and a mobile drawer could stay open over the desktop layout on resize.

Add `hooks/useMediaQuery.ts` and render only the layout matching `(min-width: 768px)`
(the `md` breakpoint the layouts already use) β€” one editor mounts; switching the
breakpoint unmounts the other layout (and its drawers). Manuscript content lives in
Redux so the remount is state-safe.

Tests: useMediaQuery (initial + change); ManuscriptView suite still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 19, 2026
Comment thread components/manuscript/ManuscriptDesktopLayout.tsx Outdated
Comment thread hooks/useMediaQuery.ts Outdated
Comment thread hooks/useWriterLayout.ts Outdated
@qnbs

qnbs commented Jun 19, 2026

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 19, 2026
@codeant-ai

codeant-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR refactors ManuscriptView so shared layout state is managed by a hook and only the layout matching the current breakpoint is rendered, ensuring a single ManuscriptEditor tree mounts at any time.

sequenceDiagram
    participant User
    participant ManuscriptView
    participant LayoutState
    participant MediaQuery
    participant DesktopLayout
    participant MobileLayout
    participant Editor

    User->>ManuscriptView: Navigate to manuscript view
    ManuscriptView->>LayoutState: Initialize shared layout state

    ManuscriptView->>MediaQuery: Evaluate desktop breakpoint

    alt Desktop viewport
        ManuscriptView->>DesktopLayout: Render desktop layout with project and layout
        DesktopLayout->>Editor: Mount editor and research split
    else Mobile viewport
        ManuscriptView->>MobileLayout: Render mobile layout with project and layout
        MobileLayout->>Editor: Mount editor and drawers
    end

    MediaQuery-->>ManuscriptView: Notify breakpoint change
    ManuscriptView->>ManuscriptView: Switch layout and unmount previous editor tree
Loading

Generated by CodeAnt AI

@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 20, 2026
@qnbs

qnbs commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

…st fallback (CodeAnt #179)

- ManuscriptDesktopLayout focus toggle and useWriterLayout swipe handlers now use
  functional setState updaters, so rapid clicks/swipes processed before a commit
  derive from the latest value instead of a stale captured one (no lost/collapsed
  toggles).
- useMediaQuery now falls back to the deprecated addListener/removeListener when
  MediaQueryList.addEventListener is unavailable (older Safari/WebView <14), where
  the previous code would throw a TypeError and break layout rendering entirely.
  New test covers the legacy path (subscribe + react + cleanup).

Typecheck + lint clean; useMediaQuery suite 3/3 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@qnbs

qnbs commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 20, 2026
@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jun 20, 2026
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR refactors Manuscript and Writer views so layout state lives in dedicated hooks and ManuscriptView mounts only the layout that matches the current viewport, ensuring a single editor tree and consistent drawers and focus behavior across mobile and desktop.

sequenceDiagram
    participant User
    participant ManuscriptView
    participant MediaQueryHook
    participant ManuscriptLayoutHook
    participant DesktopLayout
    participant MobileLayout
    participant WriterViewUI
    participant WriterLayoutHook

    User->>ManuscriptView: Open manuscript view
    ManuscriptView->>ManuscriptLayoutHook: Initialize shared layout state
    ManuscriptView->>MediaQueryHook: Evaluate min width 768 query
    MediaQueryHook-->>ManuscriptView: Return isDesktop flag

    alt Desktop viewport
        ManuscriptView->>DesktopLayout: Render with shared layout state
        DesktopLayout->>DesktopLayout: Show navigator editor inspector and research split
    else Mobile viewport
        ManuscriptView->>MobileLayout: Render with shared layout state
        MobileLayout->>MobileLayout: Show mobile header editor and drawers
    end

    User->>WriterViewUI: Open writer view
    WriterViewUI->>WriterLayoutHook: Initialize mobile tabs focus and swipe handlers
    alt Flow mode active and Escape pressed
        WriterLayoutHook-->>WriterViewUI: Toggle flow mode off
    end
Loading

Generated by CodeAnt AI

Comment thread components/manuscript/ManuscriptDesktopLayout.tsx Outdated
Comment thread hooks/useWriterLayout.ts Outdated
Comment thread hooks/useMediaQuery.ts Outdated
…Media fallback (CodeAnt #179 wave-2)

- ManuscriptDesktopLayout: the left/right resizers clamped each side to 50% independently, so both
  could reach 50% and collapse the center editor. Added a combined constraint (each side ≀ 80 minus
  the other's current width) so the editor keeps β‰₯ 20%.
- useWriterLayout: the swipe handlers were inline closures recreated every render, so useSwipeGesture
  (effect deps include the callbacks) tore down + reattached DOM touch listeners on each rerender
  (e.g. while writer state streams). Wrapped both in useCallback (functional setState β†’ no deps).
- useMediaQuery: when matchMedia is unavailable it returned a permanent false, trapping callers in the
  mobile layout forever. Now evaluates (min/max-width) against window.innerWidth and updates on resize.
  New test covers the fallback (initial eval + resize update).

Typecheck clean; useMediaQuery 4/4 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@qnbs

qnbs commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 20, 2026
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR refactors ManuscriptView to centralize layout state in a hook and use a media query hook so only the mobile or desktop layout mounts at a time, ensuring a single ManuscriptEditor tree and correct behavior on resize.

sequenceDiagram
    participant User
    participant Browser
    participant ManuscriptView
    participant MediaQueryHook
    participant ManuscriptLayoutHook
    participant DesktopLayout
    participant MobileLayout
    participant ManuscriptEditor

    User->>Browser: Open Manuscript view
    Browser->>ManuscriptView: Mount ManuscriptView component
    ManuscriptView->>MediaQueryHook: Evaluate desktop breakpoint query
    MediaQueryHook-->>ManuscriptView: Return isDesktop flag
    ManuscriptView->>ManuscriptLayoutHook: Initialize shared layout state
    ManuscriptLayoutHook-->>ManuscriptView: Layout state and handlers

    alt Desktop viewport
        ManuscriptView->>DesktopLayout: Render with project and layout state
        DesktopLayout->>ManuscriptEditor: Render editor with panels and research split
    else Mobile viewport
        ManuscriptView->>MobileLayout: Render with project and layout state
        MobileLayout->>ManuscriptEditor: Render editor with mobile header and drawers
    end
Loading

Generated by CodeAnt AI

Comment thread components/manuscript/ManuscriptDesktopLayout.tsx
Comment thread components/manuscript/ManuscriptDesktopLayout.tsx
Comment thread components/manuscript/ManuscriptMobileLayout.tsx
Comment thread hooks/useManuscriptLayout.ts Outdated
…p reset (CodeAnt #179 wave-4)

- useResizablePanels (HIGH): the pointer-DRAG handlers clamped each side independently to 15–50%, so a
  drag could still collapse the center editor (my earlier combined constraint only covered keyboard
  resize). Track both widths in a ref and clamp left to min(50, 80 - right) / right to min(50, 80 -
  left), keeping the editor β‰₯ 20%.
- useManuscriptLayout: the two mobile drawers are now mutually exclusive (opening one closes the other)
  β€” the shared Drawer applies global body-overflow + focus side effects, so two open at once let a
  close re-enable scrolling / restore focus while the other was still open.
- Reset both mobile drawers when the viewport crosses to desktop, so a drawer opened on mobile doesn't
  stay logically open and reappear on return.

Typecheck clean; useMediaQuery suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@qnbs

qnbs commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! πŸŽ‰

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X Β·
Reddit Β·
LinkedIn

@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Jun 20, 2026
@codeant-ai

codeant-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR refactors ManuscriptView to centralize layout state and use a media query hook so only the matching mobile or desktop layout renders at a time, ensuring a single ManuscriptEditor tree and preventing mobile drawers from lingering after resize.

sequenceDiagram
    participant User
    participant Browser
    participant ManuscriptView
    participant LayoutState
    participant MediaQuery
    participant Editor

    User->>ManuscriptView: Open manuscript screen
    ManuscriptView->>LayoutState: Initialize manuscript layout state
    ManuscriptView->>MediaQuery: Evaluate min width 768 query
    MediaQuery-->>ManuscriptView: Return isDesktop flag
    ManuscriptView->>Editor: Render matching desktop or mobile layout with one editor tree

    User->>Browser: Resize viewport across breakpoint
    Browser->>MediaQuery: Notify media query change
    MediaQuery-->>ManuscriptView: Return updated isDesktop flag
    ManuscriptView->>Editor: Unmount old layout and mount new matching layout
Loading

Generated by CodeAnt AI

Comment on lines +95 to +101
Object.defineProperty(window, 'innerWidth', { value: 1024, configurable: true });
const { result } = renderHook(() => useMediaQuery('(min-width: 768px)'));
expect(result.current).toBe(true); // 1024 >= 768
act(() => {
Object.defineProperty(window, 'innerWidth', { value: 500, configurable: true });
window.dispatchEvent(new Event('resize'));
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This test mutates the global window.innerWidth value but never restores it, so later tests in the same worker can inherit a stale viewport width and become order-dependent/flaky. Save the original width and restore it at the end of the test (or in afterEach). [code quality]

Severity Level: Major ⚠️
- ⚠️ Vitest workers retain mutated window.innerWidth across suites.
- ⚠️ Components using innerWidth see unexpected viewport in tests.
- ⚠️ Future layout-sensitive tests can become order-dependent and flaky.
Steps of Reproduction βœ…
1. In `tests/unit/useMediaQuery.test.ts:92-103`, the `"evaluates innerWidth + tracks
resize when matchMedia is unavailable"` test sets `window.innerWidth` to `1024` and later
to `500` via `Object.defineProperty(window, 'innerWidth', { value: ..., configurable: true
})` at lines 95 and 99, and never restores the original value.

2. The `afterEach` hook for this suite at `tests/unit/useMediaQuery.test.ts:65-66` only
calls `vi.unstubAllGlobals()`, which clears the `matchMedia` stub but leaves the mutated
`window.innerWidth` value (`500`) in place for the rest of the Vitest worker.

3. In the same worker, subsequent tests that render components depending on
`window.innerWidth`β€”for example `ManuscriptEditor` at
`components/manuscript/ManuscriptEditor.tsx:12-23` (which computes `isMobile` from
`window.innerWidth < 768`) or logic under test in `hooks/useResizablePanels.ts:31-40`
(which computes panel widths from `window.innerWidth`)β€”will run against this stale 500px
viewport unless they explicitly override it themselves.

4. When Vitest schedules `tests/unit/useMediaQuery.test.ts` before suites like
`tests/unit/manuscript/ManuscriptEditor.test.tsx` or other future tests that implicitly
rely on jsdom's default viewport size, those tests will observe different layout decisions
and DOM structures depending on test ordering (isolated vs full run), making the suite
order-dependent and potentially flaky.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** tests/unit/useMediaQuery.test.ts
**Line:** 95:101
**Comment:**
	*Code Quality: This test mutates the global `window.innerWidth` value but never restores it, so later tests in the same worker can inherit a stale viewport width and become order-dependent/flaky. Save the original width and restore it at the end of the test (or in `afterEach`).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

Comment on lines +32 to 35
const maxLeft = Math.min(50, 80 - widthsRef.current.right);
if (newWidth > 15 && newWidth < maxLeft) {
setLeftPanelWidth(newWidth);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The left resize handler drops out-of-range pointer positions instead of clamping them, so a fast drag that overshoots the allowed range leaves the panel width stuck at its old value and makes edge resizing feel broken. Clamp newWidth into the valid interval and always apply it while resizing. [logic error]

Severity Level: Major ⚠️
- ⚠️ Manuscript desktop navigator resizer ignores fast drags past limit.
- ⚠️ Editor layout feels stuck when expanding left panel quickly.
Steps of Reproduction βœ…
1. Render the Manuscript view by mounting `ManuscriptView` from
`components/ManuscriptView.tsx:63`, which internally renders `ManuscriptViewUI` and, on
desktop (`useMediaQuery` at `components/ManuscriptView.tsx:22–23`), shows
`ManuscriptDesktopLayout` at `components/ManuscriptView.tsx:55`.

2. In `ManuscriptDesktopLayout`
(`components/manuscript/ManuscriptDesktopLayout.tsx:25–45`), the layout prop comes from
`useManuscriptLayout` (`hooks/useManuscriptLayout.ts:9–65`), which in turn calls
`useResizablePanels(20, 20)` at `hooks/useManuscriptLayout.ts:42`, initializing
`leftPanelWidth` and `rightPanelWidth` to 20% (`hooks/useResizablePanels.ts:15–17`).

3. On the desktop manuscript screen, start dragging the left resizer handle between the
navigator and editor, which is the `Resizer` wired with `onPointerDown={startLeftResize}`
at `components/manuscript/ManuscriptDesktopLayout.tsx:129–140`. This calls
`startLeftResize` (`hooks/useResizablePanels.ts:62–70`), which sets
`isResizingLeft.current = true` and installs a throttled `pointermove` listener that
delegates to `handleLeftResize` in the `useEffect` at
`hooks/useResizablePanels.ts:81–103`.

4. While holding the drag, move the pointer quickly to the far right so that the first
processed `pointermove` has `clientX` corresponding to `newWidth` greater than the allowed
maximum (e.g., >50% when `rightPanelWidth` is 20%, per `maxLeft = Math.min(50, 80 -
widthsRef.current.right)` at `hooks/useResizablePanels.ts:31–32`). In `handleLeftResize`
(`hooks/useResizablePanels.ts:29–35`), the condition `if (newWidth > 15 && newWidth <
maxLeft)` fails for this overshoot value, so `setLeftPanelWidth(newWidth)` is not called
and `leftPanelWidth` remains at the old value. Visually, the left panel does not resize
despite the drag, and only updates again if a later `pointermove` happens to fall back
into the 15–maxLeft interval, creating the "stuck/jumpy near the edge" behavior the
suggestion describes.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** hooks/useResizablePanels.ts
**Line:** 32:35
**Comment:**
	*Logic Error: The left resize handler drops out-of-range pointer positions instead of clamping them, so a fast drag that overshoots the allowed range leaves the panel width stuck at its old value and makes edge resizing feel broken. Clamp `newWidth` into the valid interval and always apply it while resizing.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

Comment on lines +41 to 44
const maxRight = Math.min(50, 80 - widthsRef.current.left);
if (newWidth > 15 && newWidth < maxRight) {
setRightPanelWidth(newWidth);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The right resize handler has the same boundary bug: it ignores pointer positions outside the valid range rather than clamping, so dragging past the limit can fail to update the width and makes it hard to reach the constraint edge reliably. Clamp and apply the bounded value instead of skipping the update. [logic error]

Severity Level: Major ⚠️
- ⚠️ Manuscript inspector resizer unresponsive when dragging handle past limit.
- ⚠️ Users struggle sizing inspector while preserving central editor space.
Steps of Reproduction βœ…
1. Render the Manuscript view by mounting `ManuscriptView` from
`components/ManuscriptView.tsx:63`; on desktop (`components/ManuscriptView.tsx:22–23`),
this renders `ManuscriptDesktopLayout` at `components/ManuscriptView.tsx:55` with a
`layout` prop from `useManuscriptLayout` (`hooks/useManuscriptLayout.ts:9–65`), which
internally uses `useResizablePanels(20, 20)` at `hooks/useManuscriptLayout.ts:42`.

2. In `ManuscriptDesktopLayout`, the right inspector pane width is bound to
`rightPanelWidth` in the style prop at
`components/manuscript/ManuscriptDesktopLayout.tsx:179–183`, and the right resize handle
is the `Resizer` with `onPointerDown={startRightResize}` at
`components/manuscript/ManuscriptDesktopLayout.tsx:165–175`, using the same `layout`
object that exposes `startRightResize` and `rightPanelWidth` from `useResizablePanels`
(`hooks/useResizablePanels.ts:111–118`).

3. Begin a drag on the right resizer handle; `onPointerDown` calls `startRightResize`
(`hooks/useResizablePanels.ts:72–79`), which sets `isResizingRight.current = true`,
`activeResize = 'right'`, and registers a throttled `pointermove` listener that routes to
`handleRightResize` via the `useEffect` at `hooks/useResizablePanels.ts:81–103`.

4. While holding the drag, move the pointer quickly towards the left so that the first
processed `pointermove` places the handle beyond the allowed maximum width for the right
panel (computed as `maxRight = Math.min(50, 80 - widthsRef.current.left)` at
`hooks/useResizablePanels.ts:40–41`). In `handleRightResize`
(`hooks/useResizablePanels.ts:38–44`), this makes `newWidth` either below 15 or above
`maxRight`, so the condition `if (newWidth > 15 && newWidth < maxRight)` fails and
`setRightPanelWidth(newWidth)` is skipped, leaving `rightPanelWidth` unchanged and the
inspector pane visually stuck despite the drag. Only when a subsequent `pointermove`
happens to fall back inside the valid interval does the width update, causing inconsistent
and hard-to-hit behavior near the constraint edge as described in the suggestion.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** hooks/useResizablePanels.ts
**Line:** 41:44
**Comment:**
	*Logic Error: The right resize handler has the same boundary bug: it ignores pointer positions outside the valid range rather than clamping, so dragging past the limit can fail to update the width and makes it hard to reach the constraint edge reliably. Clamp and apply the bounded value instead of skipping the update.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

…extraction

# Conflicts:
#	graphify-out/GRAPH_REPORT.md
@qnbs
qnbs merged commit 437ae25 into main Jun 20, 2026
5 of 6 checks passed
@qnbs
qnbs deleted the refactor/view-layout-extraction branch June 20, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant