Skip to content

feat: add configurable paste mode#162

Open
TumGovic wants to merge 1 commit into
erictli:mainfrom
TumGovic:feat/paste-mode
Open

feat: add configurable paste mode#162
TumGovic wants to merge 1 commit into
erictli:mainfrom
TumGovic:feat/paste-mode

Conversation

@TumGovic

@TumGovic TumGovic commented Jun 4, 2026

Copy link
Copy Markdown

Summary

  • Adds a Paste mode setting under Settings → Appearance → Editing
  • Three options, persisted to .scratch/settings.json:
    • Markdown (default) — existing behavior: detects markdown patterns and renders them
    • Plain text — inserts pasted content as raw text, no formatting applied
    • Code block — wraps the pasted content in a fenced ``` code block

Changes

  • src/types/note.ts — adds PasteMode type and pasteMode field to Settings
  • src/context/ThemeContext.tsx — loads, stores, and exposes pasteMode / setPasteMode
  • src/components/editor/Editor.tsxhandlePaste branches on pasteMode
  • src/components/settings/EditorSettingsSection.tsx — adds "Editing" section with a Select for paste mode

Test plan

  • Default behavior (Markdown mode) works as before — markdown in clipboard is rendered
  • Plain text mode: paste markdown source, it appears as raw text
  • Code block mode: paste any text, it is wrapped in a fenced code block
  • Setting persists across app restarts
  • Image paste still works in all modes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added configurable paste mode setting in editor preferences. Users can now choose between Markdown (default), Plain Text, and Code Block modes to control how pasted content is formatted and processed. Preferences are automatically saved.

Adds a "Paste mode" setting in Appearance → Editing that controls how
pasted text is handled:
- Markdown (default): detects and renders markdown syntax as before
- Plain text: inserts without any markdown processing
- Code block: wraps the pasted content in a fenced ``` code block

The setting persists to .scratch/settings.json alongside other editor
settings.
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a user-configurable "paste mode" setting that lets users choose how pasted content is handled in the editor. The feature includes type definitions, theme context state management with backend persistence, editor paste handler branching, and UI controls for mode selection.

Changes

Paste Mode Feature

Layer / File(s) Summary
Paste mode type contract
src/types/note.ts
Introduces PasteMode union type ("markdown" | "plain" | "code-block") and extends Settings interface with optional pasteMode field.
Theme context state and persistence
src/context/ThemeContext.tsx
Extends ThemeContext type with pasteMode and setPasteMode, initializes state to "markdown", loads from backend on provider mount, and provides async setter that persists changes.
Editor paste handling by mode
src/components/editor/Editor.tsx
Editor reads pasteMode from context and branches handlePaste into three paths: plain text insertion, code-block parsing, or markdown detection (default).
Settings UI for paste mode selection
src/components/settings/EditorSettingsSection.tsx
Imports PasteMode type, defines UI options with labels/descriptions, and renders new "Editing" settings section with dropdown bound to context's pasteMode and setPasteMode.

Sequence Diagram

sequenceDiagram
  participant User
  participant SettingsUI as Settings UI
  participant ThemeContext
  participant Backend
  participant Editor
  
  User->>SettingsUI: select paste mode
  SettingsUI->>ThemeContext: setPasteMode(mode)
  ThemeContext->>Backend: persist pasteMode
  Backend-->>ThemeContext: confirmation
  
  Note over Editor,ThemeContext: Later when user pastes...
  User->>Editor: paste content
  Editor->>ThemeContext: useTheme() reads pasteMode
  alt pasteMode == "plain"
    Editor->>Editor: insert as plain text
  else pasteMode == "code-block"
    Editor->>Editor: parse as fenced code block
  else pasteMode == "markdown"
    Editor->>Editor: detect & parse markdown
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A rabbit hops through pastes with glee,
Plain text or code blocks, user's spree!
Markdown by default, but now you choose,
Three modes to pick—you cannot lose! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add configurable paste mode' accurately and concisely describes the main change—introducing a new configurable paste mode feature with three options (markdown, plain text, code block).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/editor/Editor.tsx`:
- Around line 1232-1247: When pasteMode === "code-block", ensure a deterministic
fallback inserts a codeBlock and returns true even if manager is missing or
manager.parse throws; if parsing succeeds keep existing behavior
(currentEditor.commands.insertContent(parsed)); otherwise normalize the pasted
text (use a variable e.g. normalized = text.replace(/\r\n?/g, '\n') or similar),
then call currentEditor.chain().focus().insertContent({ type: "codeBlock",
attrs: { language: null }, content: normalized ? [{ type: "text", text:
normalized }] : [] }).run(); finally return true so paste handling stays in
code-block mode instead of falling back to default.

In `@src/context/ThemeContext.tsx`:
- Around line 558-563: The setPasteMode setter currently does a
read-modify-write by calling getSettings() then updateSettings({...settings,
pasteMode: mode}), which can clobber concurrent changes; change setPasteMode
(and related callers) to persist only the changed field atomically by calling an
API that accepts a partial update (e.g., updateSettings({ pasteMode: mode }) or
a new updateSettingField('pasteMode', mode)) instead of spreading the full
settings object, or implement a versioned/conditional write in updateSettings to
prevent lost updates; keep setPasteModeState(mode) locally, await the atomic
patch/field-update call (or handle version conflicts) and preserve the existing
error handling in the catch block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c32f4ed-517a-42ca-9956-f655eae95510

📥 Commits

Reviewing files that changed from the base of the PR and between a638f02 and 60a18ef.

📒 Files selected for processing (4)
  • src/components/editor/Editor.tsx
  • src/components/settings/EditorSettingsSection.tsx
  • src/context/ThemeContext.tsx
  • src/types/note.ts

Comment on lines +1232 to +1247
if (pasteMode === "code-block") {
const manager = currentEditor.storage.markdown?.manager;
if (manager && typeof manager.parse === "function") {
try {
const fenced = "```\n" + text + "\n```";
const parsed = manager.parse(fenced);
if (parsed) {
currentEditor.commands.insertContent(parsed);
return true;
}
} catch {
// fall through to default
}
}
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guarantee code-block mode even when markdown parse fails.

If parse fails (or manager is unavailable), this returns false, so default paste behavior runs instead of code-block mode. Add a deterministic fallback that inserts a codeBlock node and still returns true.

Suggested fix
         if (pasteMode === "code-block") {
+          const normalized = text.replace(/\r\n/g, "\n");
           const manager = currentEditor.storage.markdown?.manager;
           if (manager && typeof manager.parse === "function") {
             try {
-              const fenced = "```\n" + text + "\n```";
+              const fenced = "```\n" + normalized + "\n```";
               const parsed = manager.parse(fenced);
               if (parsed) {
                 currentEditor.commands.insertContent(parsed);
                 return true;
               }
             } catch {
               // fall through to default
             }
           }
-          return false;
+          currentEditor
+            .chain()
+            .focus()
+            .insertContent({
+              type: "codeBlock",
+              attrs: { language: null },
+              content: normalized ? [{ type: "text", text: normalized }] : [],
+            })
+            .run();
+          return true;
         }
📝 Committable suggestion

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

Suggested change
if (pasteMode === "code-block") {
const manager = currentEditor.storage.markdown?.manager;
if (manager && typeof manager.parse === "function") {
try {
const fenced = "```\n" + text + "\n```";
const parsed = manager.parse(fenced);
if (parsed) {
currentEditor.commands.insertContent(parsed);
return true;
}
} catch {
// fall through to default
}
}
return false;
}
if (pasteMode === "code-block") {
const normalized = text.replace(/\r\n/g, "\n");
const manager = currentEditor.storage.markdown?.manager;
if (manager && typeof manager.parse === "function") {
try {
const fenced = "
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/editor/Editor.tsx` around lines 1232 - 1247, When pasteMode
=== "code-block", ensure a deterministic fallback inserts a codeBlock and
returns true even if manager is missing or manager.parse throws; if parsing
succeeds keep existing behavior (currentEditor.commands.insertContent(parsed));
otherwise normalize the pasted text (use a variable e.g. normalized =
text.replace(/\r\n?/g, '\n') or similar), then call
currentEditor.chain().focus().insertContent({ type: "codeBlock", attrs: {
language: null }, content: normalized ? [{ type: "text", text: normalized }] :
[] }).run(); finally return true so paste handling stays in code-block mode
instead of falling back to default.

Comment on lines +558 to +563
const setPasteMode = useCallback(async (mode: PasteMode) => {
setPasteModeState(mode);
try {
const settings = await getSettings();
await updateSettings({ ...settings, pasteMode: mode });
} catch (error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid read-modify-write overwrites when persisting pasteMode.

This setter reads the full settings object and writes it back with one field changed. Concurrent setting updates can clobber each other and drop unrelated fields. Prefer an atomic backend patch command (e.g., update only pasteMode) or a versioned/serialized write strategy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/context/ThemeContext.tsx` around lines 558 - 563, The setPasteMode setter
currently does a read-modify-write by calling getSettings() then
updateSettings({...settings, pasteMode: mode}), which can clobber concurrent
changes; change setPasteMode (and related callers) to persist only the changed
field atomically by calling an API that accepts a partial update (e.g.,
updateSettings({ pasteMode: mode }) or a new updateSettingField('pasteMode',
mode)) instead of spreading the full settings object, or implement a
versioned/conditional write in updateSettings to prevent lost updates; keep
setPasteModeState(mode) locally, await the atomic patch/field-update call (or
handle version conflicts) and preserve the existing error handling in the catch
block.

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