Skip to content

Add keyboard shortcuts and orientation control to split view#39

Open
remcostoeten wants to merge 1 commit intodaddyfrom
codex/add-split-editor-layout-button
Open

Add keyboard shortcuts and orientation control to split view#39
remcostoeten wants to merge 1 commit intodaddyfrom
codex/add-split-editor-layout-button

Conversation

@remcostoeten
Copy link
Owner

@remcostoeten remcostoeten commented Dec 9, 2025

Summary

  • add shortcut definitions and registrations so split view, swapping, and orientation are configurable via the shortcuts module
  • extend split editor layout with vertical and horizontal orientations plus an orientation toggle
  • wire split state management to track orientation while keeping pane interactions independent

Testing

  • not run (not requested)

Codex Task

Summary by CodeRabbit

  • New Features
    • Split-view editing mode: View and edit multiple notes simultaneously in vertical or horizontal layouts.
    • Drag-to-resize split panes to adjust workspace proportions.
    • Drag-and-drop notes directly into split panes for quick switching.
    • New keyboard shortcuts for split view operations: toggle, swap panes, and cycle layout orientation.
    • Re-enabled delete item shortcut.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Dec 9, 2025

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

Project Deployment Preview Comments Updated (UTC)
skriuw Error Error Dec 9, 2025 0:46am

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

Walkthrough

This change introduces split-view editing mode for notes, enabling users to simultaneously edit multiple notes side-by-side. The implementation adds UI state management, layout rendering with SplitEditorLayout, keyboard shortcuts for split-view control, and drag-and-drop support for assigning notes to panes.

Changes

Cohort / File(s) Summary
Layout & Toolbar Components
apps/web/components/layout/app-layout-manager.tsx, apps/web/components/layout/top-toolbar.tsx
Added split-view feature with state management (isSplitView, splitPanes, activePaneId, splitRatio, splitOrientation), effect-driven pane initialization and synchronization, handlers for toggling, closing, swapping, and assigning notes to panes, and toolbar UI control (Panels icon button) to toggle split view. Extended props to support sidebarContentType and sidebarCustomContent.
Editor Components
apps/web/features/editor/components/editor-tabs-bar.tsx, apps/web/features/editor/components/split-editor-layout.tsx
Enhanced drag-start to include noteId in dataTransfer. New SplitEditorLayout component with responsive two-pane layout, draggable resize divider, drag-and-drop note assignment, pane management controls (swap, close), and support for vertical/horizontal orientation toggling.
Shortcuts
apps/web/features/shortcuts/api/queries/get-shortcuts.ts, apps/web/features/shortcuts/shortcut-definitions.ts
Added three new split-view shortcuts (toggle-split-view, swap-split-panes, cycle-split-orientation) with key bindings and descriptions. Re-enabled delete-item shortcut (changed from disabled to enabled).

Sequence Diagram

sequenceDiagram
    actor User
    participant AppLayoutManager
    participant SplitEditorLayout
    participant NoteEditor1
    participant NoteEditor2

    User->>AppLayoutManager: Click toggle-split-view button
    activate AppLayoutManager
    AppLayoutManager->>AppLayoutManager: Update isSplitView state<br/>Initialize splitPanes array
    AppLayoutManager->>SplitEditorLayout: Render with initial panes
    deactivate AppLayoutManager

    activate SplitEditorLayout
    SplitEditorLayout->>NoteEditor1: renderPane(pane1)
    activate NoteEditor1
    NoteEditor1-->>SplitEditorLayout: Render editor content
    deactivate NoteEditor1

    SplitEditorLayout->>NoteEditor2: renderPane(pane2)
    activate NoteEditor2
    NoteEditor2-->>SplitEditorLayout: Render editor content
    deactivate NoteEditor2
    deactivate SplitEditorLayout

    User->>SplitEditorLayout: Drag note to secondary pane
    activate SplitEditorLayout
    SplitEditorLayout->>SplitEditorLayout: Detect drop (NOTE_DATA_TYPE)
    SplitEditorLayout->>AppLayoutManager: onAssignNote(paneId, noteId)
    deactivate SplitEditorLayout

    activate AppLayoutManager
    AppLayoutManager->>AppLayoutManager: Update splitPanes<br/>Set activePaneId
    AppLayoutManager->>SplitEditorLayout: Re-render with updated panes
    deactivate AppLayoutManager

    User->>SplitEditorLayout: Drag divider to resize
    activate SplitEditorLayout
    SplitEditorLayout->>AppLayoutManager: onResize(newRatio)
    AppLayoutManager->>AppLayoutManager: Update splitRatio
    SplitEditorLayout->>SplitEditorLayout: Adjust layout dimensions
    deactivate SplitEditorLayout
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • AppLayoutManager: Dense state management with multiple effect dependencies, effect-driven initialization/synchronization, and complex handler logic for pane lifecycle management
  • SplitEditorLayout: New component with drag-to-resize pointer event handling, drag-and-drop logic with custom data types, and responsive layout calculations
  • Cross-component interactions: Changes span multiple layers (layout manager → editor layout → note editor) with interdependent state and prop wiring
  • Shortcuts integration: Verify keyboard shortcut handlers are properly wired to corresponding AppLayoutManager methods

Possibly related PRs

Poem

🐰 A split-view dream, now side by side,
Two notes dance where editors collide!
Drag, resize, and swap with glee—
Notes in panes, in harmony! ✨

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 accurately summarizes the main changes: adding keyboard shortcuts and orientation control to the split view editor functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/add-split-editor-layout-button

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.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 9, 2025

Greptile Overview

Greptile Summary

Added keyboard-driven split view functionality to the editor with vertical/horizontal orientation control and drag-and-drop tab assignment between panes.

Key Changes:

  • Implemented SplitEditorLayout component with resizable panes, drag-and-drop support, and orientation toggle
  • Added three new keyboard shortcuts: Ctrl+\ (toggle split), Ctrl+Shift+\ (swap panes), Ctrl+Alt+\ (cycle orientation)
  • Integrated split state management into AppLayoutManager with proper lifecycle handling for pane creation, swapping, and cleanup
  • Enhanced EditorTabsBar drag-and-drop to support cross-pane tab dragging using custom MIME type
  • Added split view toggle button to TopToolbar with conditional visibility

Minor Issue:

  • get-shortcuts.ts uses Date.now() instead of createTimestamp() from shared type utilities (style guide violation)

Confidence Score: 4/5

  • Safe to merge with minor style guide violation that should be addressed
  • The implementation is solid with well-structured state management, proper event handling, and comprehensive integration. The split view logic correctly handles edge cases (route changes, pane cleanup, fallback noteIds). Only issue is a style guide violation using Date.now() instead of createTimestamp(), which doesn't affect functionality but should follow the codebase's mandatory timestamp pattern.
  • apps/web/features/shortcuts/api/queries/get-shortcuts.ts needs timestamp utility update

Important Files Changed

File Analysis

Filename Score Overview
apps/web/features/shortcuts/api/queries/get-shortcuts.ts 4/5 Added new shortcut IDs to cache result object, uses Date.now() for caching but violates shared type utilities rule
apps/web/features/editor/components/split-editor-layout.tsx 5/5 New component implementing split editor with drag-and-drop, resizable panes, and orientation toggle functionality
apps/web/components/layout/app-layout-manager.tsx 4/5 Integrated split view state management with shortcuts, manages pane lifecycle and note assignments

Sequence Diagram

sequenceDiagram
    participant User
    participant TopToolbar
    participant AppLayoutManager
    participant SplitEditorLayout
    participant ShortcutProvider
    participant NoteEditor

    User->>TopToolbar: Click split view button
    TopToolbar->>AppLayoutManager: onToggleSplitView()
    AppLayoutManager->>AppLayoutManager: setIsSplitView(true)
    AppLayoutManager->>AppLayoutManager: setSplitPanes([primary, secondary])
    AppLayoutManager->>SplitEditorLayout: Render with isSplit=true
    SplitEditorLayout->>NoteEditor: renderPane(primaryPane)
    SplitEditorLayout->>NoteEditor: renderPane(secondaryPane)

    User->>ShortcutProvider: Press Ctrl+\
    ShortcutProvider->>AppLayoutManager: handleToggleSplitView()
    AppLayoutManager->>SplitEditorLayout: Update isSplit state

    User->>ShortcutProvider: Press Ctrl+Shift+\
    ShortcutProvider->>AppLayoutManager: handleSwapPanes()
    AppLayoutManager->>AppLayoutManager: Swap panes[0] and panes[1]
    AppLayoutManager->>SplitEditorLayout: Re-render with swapped panes

    User->>ShortcutProvider: Press Ctrl+Alt+\
    ShortcutProvider->>AppLayoutManager: handleCycleSplitOrientation()
    AppLayoutManager->>AppLayoutManager: Toggle vertical/horizontal
    AppLayoutManager->>SplitEditorLayout: Update orientation prop

    User->>SplitEditorLayout: Drag tab to pane
    SplitEditorLayout->>SplitEditorLayout: handleDrop(paneId, noteId)
    SplitEditorLayout->>AppLayoutManager: onAssignNote(paneId, noteId)
    AppLayoutManager->>AppLayoutManager: Update pane noteId
    AppLayoutManager->>SplitEditorLayout: Re-render with updated pane
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (1)

  1. apps/web/features/shortcuts/api/queries/get-shortcuts.ts, line 17-18 (link)

    style: Replace Date.now() with createTimestamp() from the shared type utilities to follow the codebase's mandatory timestamp pattern.

    Context Used: Context from dashboard - .cursor/rules/shared-type-usage.mdc (source)

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

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: 2

🧹 Nitpick comments (7)
apps/web/features/editor/components/split-editor-layout.tsx (2)

57-85: Consider adding pointer capture for smoother resize experience.

The resize handler works but could lose tracking if the pointer moves rapidly outside the divider element. Using setPointerCapture ensures the element continues receiving pointer events even when the pointer leaves its bounds.

 const handlePointerDown = useCallback(
         (event: React.PointerEvent) => {
                 if (!containerRef.current) return
                 setIsDragging(true)
+                (event.target as HTMLElement).setPointerCapture(event.pointerId)
                 const startPosition = orientation === 'vertical' ? event.clientX : event.clientY

52-55: Add defensive check for empty panes array.

If panes is empty, primaryPane will be undefined, causing renderPaneContainer to fail when accessing pane.id. While the parent component should always provide at least one pane, a defensive check improves robustness.

 const primaryPane = panes[0]
 const secondaryPane = panes[1]

+if (!primaryPane) {
+        return <div className="flex h-full items-center justify-center text-muted-foreground">No pane available</div>
+}
+
 const clampedRatio = useMemo(() => Math.min(0.8, Math.max(0.2, splitRatio)), [splitRatio])

Also applies to: 154-168

apps/web/components/layout/top-toolbar.tsx (1)

107-119: Inconsistent indentation detected.

The changed lines use different indentation (appears to be spaces) compared to the unchanged portions of the file (which use tabs). While not a functional issue, this creates inconsistent formatting.

Consider running a formatter to normalize indentation across the file.

apps/web/components/layout/app-layout-manager.tsx (4)

67-77: Split-view and support state are well-structured; consider persistence as a follow-up

The split-related state (isSplitView, splitPanes, activePaneId, splitRatio, splitOrientation) plus isMobile/isSearchOpen/hasEverLoadedRef are cleanly initialized and scoped. If you later want cross-route/session persistence of split orientation or ratio, this would be a good place to lift into a store or preference hook, but it’s not required for this PR.


130-137: Resetting split state when leaving note routes is appropriate

The useEffect that clears isSplitView, splitPanes, and activePaneId when !isNoteRoute ensures non-note routes don’t accidentally render editor split UI. You intentionally preserve splitRatio and splitOrientation, which is a reasonable trade-off; if that’s not desired UX, they could be reset here as well.


210-227: Split toggle logic is sound; consider extracting the pane-initialization block

The split toggle correctly:

  • No-ops when not on a note route.
  • Turns split off by just flipping isSplitView (the collapse effect handles panes).
  • When enabling split, seeds two panes based on the active pane’s note or currentNoteId.

Embedding setSplitPanes inside the setIsSplitView updater is valid but a bit dense. You might consider extracting the pane-initialization logic into a small helper for readability, but behavior looks correct.


233-254: Pane-closing behavior is correct; tiny unreachable branch you can simplify

Closing a pane:

  • No-ops when only one pane exists.
  • For 2+ panes, updates isSplitView and activePaneId appropriately based on whether the closed pane was active and whether only one pane remains.

The if (!remaining.length) branch is currently unreachable because you early-return when prev.length <= 1. Not an issue, but you can remove that branch later to simplify the code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9846c0a and 437673f.

📒 Files selected for processing (6)
  • apps/web/components/layout/app-layout-manager.tsx (8 hunks)
  • apps/web/components/layout/top-toolbar.tsx (4 hunks)
  • apps/web/features/editor/components/editor-tabs-bar.tsx (1 hunks)
  • apps/web/features/editor/components/split-editor-layout.tsx (1 hunks)
  • apps/web/features/shortcuts/api/queries/get-shortcuts.ts (1 hunks)
  • apps/web/features/shortcuts/shortcut-definitions.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/spec.mdc)

Use TypeScript-first schema with Skriuw SDK for the instantdb MVP

Files:

  • apps/web/features/editor/components/split-editor-layout.tsx
  • apps/web/features/editor/components/editor-tabs-bar.tsx
  • apps/web/features/shortcuts/shortcut-definitions.ts
  • apps/web/components/layout/top-toolbar.tsx
  • apps/web/components/layout/app-layout-manager.tsx
  • apps/web/features/shortcuts/api/queries/get-shortcuts.ts
🧠 Learnings (1)
📚 Learning: 2025-12-04T14:57:24.760Z
Learnt from: CR
Repo: remcostoeten/skriuw PR: 0
File: .cursor/rules/shared-type-usage.mdc:0-0
Timestamp: 2025-12-04T14:57:24.760Z
Learning: Applies to apps/web/src/types/**/*.{ts,tsx} : Keep new utility types in `apps/web/src/types/` and update documentation when adding or changing shared primitives

Applied to files:

  • apps/web/components/layout/top-toolbar.tsx
  • apps/web/components/layout/app-layout-manager.tsx
🧬 Code graph analysis (3)
apps/web/features/editor/components/split-editor-layout.tsx (1)
packages/ui/icons.tsx (1)
  • IconButton (96-202)
apps/web/components/layout/top-toolbar.tsx (1)
packages/ui/icons.tsx (1)
  • IconButton (96-202)
apps/web/components/layout/app-layout-manager.tsx (5)
packages/core-logic/use-media-query.ts (2)
  • useMediaQuery (7-26)
  • MOBILE_BREAKPOINT (5-5)
apps/web/features/editor/components/split-editor-layout.tsx (1)
  • SplitEditorLayout (33-237)
apps/web/features/editor/components/note-editor.tsx (1)
  • NoteEditor (21-122)
apps/web/components/layout/app-layout-shell.tsx (1)
  • AppLayoutShell (27-166)
apps/web/stores/app-store.ts (1)
  • showSidebar (58-61)
🔇 Additional comments (25)
apps/web/features/shortcuts/api/queries/get-shortcuts.ts (1)

33-43: LGTM!

The expanded shortcuts map correctly includes the new split-view shortcuts (toggle-split-view, swap-split-panes, cycle-split-orientation) that align with the definitions in shortcut-definitions.ts. The existing shortcuts are preserved.

apps/web/features/editor/components/split-editor-layout.tsx (3)

10-29: LGTM!

The type definitions are well-structured. The SplitPane type and SplitEditorLayoutProps provide a clear contract for the component's interface.


112-152: LGTM!

The pane controls and container rendering logic is well-implemented with appropriate conditional rendering for swap/close buttons based on split state and pane count. The visual feedback for active and dragging states is clear.


170-236: LGTM!

The split layout structure is well-implemented with:

  • Proper flex-direction toggling based on orientation
  • Clean divider styling with appropriate cursor feedback
  • Visual indication of dragging state

Good use of flexBasis for proportional sizing.

apps/web/features/shortcuts/shortcut-definitions.ts (2)

81-104: LGTM!

The new split-view shortcuts are well-designed:

  • toggle-split-view: Ctrl/Meta + \ - intuitive for splitting
  • swap-split-panes: Ctrl/Meta + Shift + \ - extends the base shortcut
  • cycle-split-orientation: Ctrl/Meta + Alt + \ - consistent modifier pattern

The backslash key is a good mnemonic for "split" operations.


105-109: Verify delete shortcut doesn't interfere with text editing.

The delete-item shortcut is now enabled with Delete and Backspace keys. The description says "when context menu is open", but ensure the shortcut handler properly checks for context menu state to avoid interfering with normal text editing operations in the note editor.

apps/web/components/layout/top-toolbar.tsx (2)

26-33: LGTM!

The new props for split-view control (showSplitToggle, isSplitView, onToggleSplitView) follow the existing optional prop pattern and maintain backward compatibility with sensible defaults.


120-127: LGTM!

The split toggle button follows the established IconButton pattern with appropriate dynamic tooltip based on isSplitView state. Good consistency with other toolbar controls.

apps/web/components/layout/app-layout-manager.tsx (17)

7-7: Imports for SplitEditorLayout and NoteEditor are consistent and localized

Bringing SplitEditorLayout and NoteEditor in at this level keeps layout concerns in AppLayoutManager and leaves editor behavior encapsulated in editor components. No issues here.

Also applies to: 36-36


125-128: currentNoteIndex memoization and sentinel behavior look correct

Using -1 as the sentinel when sidebarActiveNoteId is falsy keeps the navigation helpers simple and avoids out-of-bounds errors. Dependencies on sidebarActiveNoteId and notesInOrder are also correct.


138-157: Auto-syncing the current note into panes matches active-pane semantics

The effect that, on currentNoteId change, injects the note into the active pane (or first pane if the active one is missing) and fills any null noteId slots gives sensible behavior:

  • New route → active pane shows the routed note.
  • Blank panes automatically adopt the new active note.

This seems aligned with the “active pane follows navigation” model and doesn’t override panes that already have content.


158-167: Collapsing back to a single primary pane when exiting split mode is consistent

When isSplitView becomes false, you normalize to a single primary pane with a sensible fallbackNoteId derived from the active pane or currentNoteId. This keeps subsequent logic simple and avoids lingering secondary panes in non-split mode.


169-173: Guarding computedTitle for missing currentNote avoids null access

Short-circuiting to 'Untitled' when currentNote is falsy avoids potential null-access issues in the switch and gives a predictable title when on a note route but the note hasn’t resolved. Behavior looks safe and backwards-compatible with the existing fallbacks.


206-208: Editor mode toggle is a clean, side-effect‑free wrapper

handleToggleEditorMode simply delegates to togglePreference('rawMDXMode') and is memoized correctly. This keeps the toolbar integration straightforward.


229-231: Orientation cycling is minimal and correct

Toggling splitOrientation between 'vertical' and 'horizontal' via a functional state update is straightforward and avoids stale closures.


256-267: Swapping the first two panes and updating activePaneId is handled correctly

The swap handler:

  • Bails out when fewer than two panes.
  • Swaps the first two entries and preserves the rest.
  • Re-points activePaneId when it referred to one of the swapped panes.

This matches expected UX and keeps pane IDs stable.


269-272: Assigning notes to panes and focusing them is straightforward

handleAssignNoteToPane cleanly updates the matching pane’s noteId and focuses that pane via setActivePaneId. Dependencies can safely be empty here since only stable setters are used.


274-276: Pane focus handler is appropriately minimal

handlePaneClick just updates activePaneId, which is what SplitEditorLayout needs to drive focus without extra side effects.


406-409: open-settings shortcut wiring mirrors existing toggle behavior

The new open-settings shortcut simply prevents default and calls toggleSettings(), consistent with the existing toggle-shortcuts behavior. No concerns.


411-414: search-notes shortcut correctly targets the shared search dialog state

Toggling isSearchOpen via the search-notes shortcut is wired correctly and reuses the same state as the toolbar’s search button, so behavior stays consistent across entry points.


416-432: Split-view keyboard shortcuts are correctly guarded by route and split state

The new shortcuts:

  • toggle-split-view: only acts on note routes, with preventDefault() when handling.
  • swap-split-panes and cycle-split-orientation: additionally require isSplitView before acting.

These guards prevent shortcuts from interfering on non-note routes or when split view is off, and they delegate to the existing handlers, so behavior stays centralized.


434-442: resolvedSplitPanes provides sensible fallbacks without overriding explicit assignments

The memoized resolvedSplitPanes:

  • Returns splitPanes unchanged for non-note routes.
  • On note routes, fills only the first pane’s noteId using currentNoteId when missing, leaving other panes untouched.

This plays nicely with the earlier effect that writes currentNoteId into panes and ensures at least one pane has content without fighting explicit assignments.


444-470: noteContent composition cleanly isolates note routes from generic children

Routing all note-route content through SplitEditorLayout (with renderPane delegating to NoteEditor or a placeholder) and falling back to children for non-note routes keeps layout logic centralized here while leaving page components simple. No issues with the prop wiring to SplitEditorLayout are apparent.


472-516: App layout wiring for toolbar and split controls looks consistent

The updated AppLayoutShell usage:

  • Keeps LeftToolbar, sidebar, footer, right panel, and widgets behavior unchanged.
  • Extends TopToolbar with isRawMDXMode, onToggleEditorMode, showSidebar, showEditorModeToggle, and the split-view flags/handler, which matches the new state in this component.
  • Continues to use canNavigatePrevious/canNavigateNext derived from currentNoteIndex.

This provides the right data for the toolbar to render editor- and split-related controls without leaking internal state details.


516-546: Main content composition with tabs, note content, and scroll container is well-factored

Wrapping EditorTabsBar (when multiNoteTabs is enabled) and the noteContent area in a flex column:

  • Ensures tabs stay fixed at the top with the editor region scrolling.
  • Adds a subtle top padding when tabs are present.
  • Routes both split and non-split note routes through the same scrollable container, while non-note routes still render their children as-is.

onFavoriteNote and getNoteData plumbing into EditorTabsBar also look correct and consistent with the notes data model.

Comment on lines +71 to +82
function handleDragStart(e: React.DragEvent, index: number) {
// Onlywe allow dragging if not clicking on a button
const target = e.target as HTMLElement
if (target.tagName === 'BUTTON' || target.closest('button')) {
e.preventDefault()
return
}
setDraggedIndex(index)
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('text/plain', tabs[index]?.noteId || '') // Required for Firefox
e.dataTransfer.setData('application/x-skriuw-note-id', tabs[index]?.noteId || '')
}
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 | 🟡 Minor

Typo in comment and minor improvement opportunity.

Line 72 has a typo: "Onlywe" should be "Only".

The custom data type addition aligns well with the NOTE_DATA_TYPE constant in split-editor-layout.tsx for split-view drag-and-drop support.

 function handleDragStart(e: React.DragEvent, index: number) {
-        // Onlywe allow dragging if not clicking on a button
+        // Only allow dragging if not clicking on a button
         const target = e.target as HTMLElement
📝 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
function handleDragStart(e: React.DragEvent, index: number) {
// Onlywe allow dragging if not clicking on a button
const target = e.target as HTMLElement
if (target.tagName === 'BUTTON' || target.closest('button')) {
e.preventDefault()
return
}
setDraggedIndex(index)
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('text/plain', tabs[index]?.noteId || '') // Required for Firefox
e.dataTransfer.setData('application/x-skriuw-note-id', tabs[index]?.noteId || '')
}
function handleDragStart(e: React.DragEvent, index: number) {
// Only allow dragging if not clicking on a button
const target = e.target as HTMLElement
if (target.tagName === 'BUTTON' || target.closest('button')) {
e.preventDefault()
return
}
setDraggedIndex(index)
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('text/plain', tabs[index]?.noteId || '') // Required for Firefox
e.dataTransfer.setData('application/x-skriuw-note-id', tabs[index]?.noteId || '')
}
🤖 Prompt for AI Agents
In apps/web/features/editor/components/editor-tabs-bar.tsx around lines 71 to
82, fix the typo in the comment ("Onlywe" -> "Only") and replace the hard-coded
custom data type string with the shared NOTE_DATA_TYPE constant from
split-editor-layout.tsx (import NOTE_DATA_TYPE at top if not already imported);
keep the text/plain setData for Firefox and set the custom type using
NOTE_DATA_TYPE so split-view drag-and-drop uses the same data type across
components.

Comment on lines +101 to +106
const handleDragOver = useCallback((event: React.DragEvent, paneId: string) => {
if (event.dataTransfer.types.includes(NOTE_DATA_TYPE)) {
event.preventDefault()
setDraggingPane(paneId)
}
}, [])
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 | 🟡 Minor

Drag-over check is inconsistent with drop handler.

handleDragOver only activates the drop indicator when NOTE_DATA_TYPE is present, but handleDrop falls back to text/plain. This means dragging from sources that only provide text/plain (e.g., Firefox fallback) won't show the visual indicator but the drop will still work.

 const handleDragOver = useCallback((event: React.DragEvent, paneId: string) => {
-        if (event.dataTransfer.types.includes(NOTE_DATA_TYPE)) {
+        if (event.dataTransfer.types.includes(NOTE_DATA_TYPE) || event.dataTransfer.types.includes('text/plain')) {
                 event.preventDefault()
                 setDraggingPane(paneId)
         }
 }, [])
📝 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
const handleDragOver = useCallback((event: React.DragEvent, paneId: string) => {
if (event.dataTransfer.types.includes(NOTE_DATA_TYPE)) {
event.preventDefault()
setDraggingPane(paneId)
}
}, [])
const handleDragOver = useCallback((event: React.DragEvent, paneId: string) => {
if (event.dataTransfer.types.includes(NOTE_DATA_TYPE) || event.dataTransfer.types.includes('text/plain')) {
event.preventDefault()
setDraggingPane(paneId)
}
}, [])
🤖 Prompt for AI Agents
In apps/web/features/editor/components/split-editor-layout.tsx around lines
101-106, the drag-over visual indicator only checks for NOTE_DATA_TYPE but the
drop handler accepts a 'text/plain' fallback; update handleDragOver to mirror
handleDrop by checking for NOTE_DATA_TYPE OR 'text/plain' (i.e., if
event.dataTransfer.types includes NOTE_DATA_TYPE || includes 'text/plain'), then
call event.preventDefault() and setDraggingPane(paneId) so sources that only
provide text/plain (Firefox fallback) show the drop indicator.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant