Skip to content

setup file handler extension & tiptap styling#1748

Merged
yujonglee merged 1 commit intomainfrom
tiptap-file-handler-styling
Nov 20, 2025
Merged

setup file handler extension & tiptap styling#1748
yujonglee merged 1 commit intomainfrom
tiptap-file-handler-styling

Conversation

@yujonglee
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

📝 Walkthrough

Walkthrough

The changes introduce file handling capabilities to the TipTap editor by adding a fileHandlerConfig prop to the Editor component, implementing a FileHandler extension with drop and paste callbacks, creating an AttachmentImage extension to manage image attributes, and adding CSS layout and image styling rules.

Changes

Cohort / File(s) Summary
Editor Component Enhancement
packages/tiptap/src/editor/index.tsx
Adds optional fileHandlerConfig prop to EditorProps interface, propagates it to shared.getExtensions(), adds it to dependency array, and wraps EditorContent with className="tiptap-root" while preserving textbox role.
File Handling & Extensions
packages/tiptap/src/shared/extensions/index.ts
Introduces FileHandlerConfig type with optional onDrop and onPaste callbacks; creates AttachmentImage extension (extends Image) with attachmentId attribute support; updates getExtensions() signature to accept fileHandlerConfig and conditionally inject FileHandler extension with image MIME type filtering and drop/paste file reading logic.
Editor Styling
packages/tiptap/src/styles/base.css
Adds .tiptap-root and nested flex layout rules for full-height column layout; applies flex: 1 1 auto; min-height: 100% to inner editor; adds min-height: 100% to .tiptap-normal; introduces .tiptap-image rule constraining width to max-width: 240px with height: auto and display: block.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Editor as Editor Component
    participant FileHandler as FileHandler Extension
    participant Callbacks as fileHandlerConfig Callbacks
    participant ImageOps as Image Operations
    
    User->>Editor: Drag/Drop or Paste Files
    Editor->>FileHandler: Trigger file event
    FileHandler->>Callbacks: Call onDrop/onPaste (if provided)
    alt Custom Handler Returns False
        Callbacks-->>FileHandler: false
        FileHandler->>FileHandler: Skip default handling
    else Custom Handler Proceeds
        Callbacks-->>FileHandler: true or void
        FileHandler->>ImageOps: Read dropped/pasted files as Data URLs
        ImageOps->>ImageOps: Filter for image MIME types
        ImageOps->>Editor: Insert AttachmentImage nodes
        Editor->>User: Display images with attachmentId
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

Areas requiring extra attention:

  • File reading logic (packages/tiptap/src/shared/extensions/index.ts): Verify correct Data URL conversion for dropped/pasted files and edge case handling (e.g., non-image files, missing position parameters).
  • AttachmentImage extension: Confirm attachmentId attribute serialization/deserialization and HTML output formatting are correct.
  • Callback delegation flow: Ensure callback return values (true/false/void) are properly handled to allow custom handlers to override or augment default file insertion behavior.
  • CSS layout implications: Verify that flex: 1 1 auto; min-height: 100% doesn't cause unintended layout shifts or overflow behavior in parent containers.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate relevance to the changeset. Add a pull request description explaining the purpose and benefits of the file handler extension and styling changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: setting up a file handler extension and adding tiptap styling, which matches the changeset across three files.
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 tiptap-file-handler-styling

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

@netlify
Copy link

netlify bot commented Nov 20, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 31128ef
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/691ed8bc21f1770008904760
😎 Deploy Preview https://deploy-preview-1748--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/tiptap/src/editor/index.tsx (1)

19-27: Add documentation about stable fileHandlerConfig to prevent unnecessary editor re-creation

The concern in the original comment is validated: if the extensions array changes identity, useEditor will create a new editor instance, with side effects like lost selection/focus/undo stack. Since fileHandlerConfig is included in the useMemo deps for extensions (lines 58–64), callers must pass a stable config object or memoize their handlers.

Add a JSDoc note to EditorProps (lines 19–27) or a comment above the component's extension setup explicitly recommending that callers memoize or stabilize fileHandlerConfig to avoid unintended editor re-instantiation. Reference that Tiptap React docs explain useEditor behavior and recommend keeping values stable to avoid recreation.

🧹 Nitpick comments (3)
packages/tiptap/src/styles/base.css (1)

35-39: Clarify the intent of the 240px image width constraint

Styling .tiptap-image with max-width: 240px; height: auto; display: block; is fine, but it hard‑caps images to a relatively small width. If these are meant to behave more like inline attachments/thumbnails this is good; if they should be responsive content images, consider max-width: 100% (optionally with a separate thumbnail style) so they can expand to the editor width on larger screens.

packages/tiptap/src/shared/extensions/index.ts (2)

129-185: Consider using batch insertion or position advancement for multiple files

The FileHandler integration delegates correctly to user handlers, but the multi-file insertion pattern could align better with Tiptap's documented approach:

  • pos is documented as always a number (the position the file was dropped at), so a defensive guard is unnecessary.
  • For inserting multiple files, Tiptap recommends either: (1) batch insert with insertContentAt(pos, [nodeA, nodeB, ...]), or (2) iterate while advancing pos by each inserted node's size. The current code inserts all files at the same pos asynchronously, which works but may not provide the insertion ordering you intend if user edits interleave.
  • FileReader.result is safe after readAsDataURL, but a defensive typeof check is reasonable if you want to be extra cautious against API misuse.

20-23: Strengthen typing for FileHandlerConfig and make MIME types configurable

Verification confirms both recommendations are valid and actionable:

  1. Type editor parameter: Import Editor from @tiptap/core (already used in placeholder.tsx in the same package) and update FileHandlerConfig to use Editor instead of any:

    import { Editor } from "@tiptap/core";
    
    export type FileHandlerConfig = {
      onDrop?: (files: File[], editor: Editor, position?: number) => boolean | void;
      onPaste?: (files: File[], editor: Editor) => boolean | void;
    };
  2. Make allowedMimeTypes configurable: Add an optional allowedMimeTypes field to FileHandlerConfig and use it in FileHandler.configure (lines 123–126). This allows callers to override the hard-coded image types without modifying this central file:

    export type FileHandlerConfig = {
      onDrop?: (files: File[], editor: Editor, position?: number) => boolean | void;
      onPaste?: (files: File[], editor: Editor) => boolean | void;
      allowedMimeTypes?: string[];
    };

    Then in the FileHandler.configure call, use: allowedMimeTypes: fileHandlerConfig?.allowedMimeTypes ?? ["image/png", "image/jpeg", "image/gif", "image/webp"]

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c302764 and 31128ef.

📒 Files selected for processing (3)
  • packages/tiptap/src/editor/index.tsx (5 hunks)
  • packages/tiptap/src/shared/extensions/index.ts (3 hunks)
  • packages/tiptap/src/styles/base.css (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/tiptap/src/editor/index.tsx (1)
packages/tiptap/src/shared/extensions/index.ts (1)
  • FileHandlerConfig (20-23)
packages/tiptap/src/shared/extensions/index.ts (1)
packages/tiptap/src/shared/extensions/placeholder.tsx (1)
  • PlaceholderFunction (8-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (2)
packages/tiptap/src/shared/extensions/index.ts (1)

25-41: AttachmentImage extension is correct and verified against Tiptap v3.10.x documentation

The extension properly extends Image and merges parent attributes via this.parent?.() to add attachmentId mapped to data-attachment-id, following Tiptap's recommended pattern. Configuration with allowBase64: true and HTMLAttributes: { class: "tiptap-image" } correctly enables data: URLs produced by FileReader.readAsDataURL() and applies custom styling to rendered <img> tags, as documented in Tiptap v3.10.x.

packages/tiptap/src/editor/index.tsx (1)

160-162: EditorContent class merging confirmed—CSS layout assumptions are sound

Verification confirms that EditorContent merges its className="tiptap-root" prop with editorProps.attributes.class = "tiptap-normal" onto the same DOM element, producing a single element with both classes (e.g., class="tiptap-root tiptap-normal"). This flat structure means your flex layout and min-height styles in base.css will apply as expected.

Comment on lines +1 to +10
.tiptap-root {
height: 100%;
display: flex;
flex-direction: column;
}

.tiptap-root .tiptap {
flex: 1 1 auto;
min-height: 100%;
}
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 | 🔴 Critical

🧩 Analysis chain

Confirm .tiptap-root / inner editor structure so flex+min-height actually apply

The layout assumes a .tiptap-root container with a nested .tiptap element, and .tiptap-normal inside with min-height: 100%. Please double‑check the actual DOM that EditorContent + editorProps.attributes.class = "tiptap-normal" produce; if there isn’t a real .tiptap-root .tiptap descendant chain, the flex growth and min-height rules here won’t take effect and you may want to target .tiptap-root and/or .tiptap-normal directly instead.

Also applies to: 19-19


🌐 Web query:

Check how `@tiptap/react`’s `EditorContent` applies `className` and `editorProps.attributes.class` in version 3.10.7. Specifically: does passing `className="tiptap-root"` plus `editorProps.attributes.class = "tiptap-normal"` result in a `.tiptap-root .tiptap-normal` structure, or just a single root element with both classes?

💡 Result:

Short answer: a single root element with both classes (not nested). EditorContent renders one element and applies your component className together with the editor's editorProps.attributes.class to that same root, so you'll see one element with both classes (e.g. class="tiptap-root tiptap-normal"). Source: Tiptap React docs and the Editor API note on editorProps/attributes. [1][2]

Sources

  • React integration / EditorContent usage (EditorContent is the React wrapper for the editor DOM). [1]
  • Editor API — editorProps.attributes.class (how the editor's attributes/classes are applied). [2]

Fix CSS selectors: .tiptap-root .tiptap descendant rule won't apply (dead code)

Based on @tiptap/react's EditorContent implementation, both className="tiptap-root" and editorProps.attributes.class = "tiptap-normal" are applied to a single root element (e.g., class="tiptap-root tiptap-normal"), not nested elements. The CSS rule .tiptap-root .tiptap on lines 7–10 is unreachable dead code because there is no .tiptap child element inside .tiptap-root.

To fix: either target .tiptap-root directly with the flex and min-height rules, or adjust selectors to match your actual DOM structure. If .tiptap-normal is on the root, use .tiptap-root.tiptap-normal or simplify to .tiptap-normal.

🤖 Prompt for AI Agents
packages/tiptap/src/styles/base.css lines 1-10: the descendant selector
`.tiptap-root .tiptap` is dead because EditorContent applies both classes on the
same root element (e.g., `class="tiptap-root tiptap-normal"`); replace the
unreachable selector with one that matches the actual DOM (e.g., target
`.tiptap-root` directly for flex/min-height, or use the combined selector
`.tiptap-root.tiptap-normal` or `.tiptap-normal`) so the flex layout and
min-height rules apply to the root element.

@yujonglee yujonglee merged commit e1d9dce into main Nov 20, 2025
13 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 21, 2025
@ComputelessComputer ComputelessComputer deleted the tiptap-file-handler-styling branch December 14, 2025 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant