Skip to content

fix: Allow indenting image blocks in lists with Tab#2781

Closed
mkcash wants to merge 1 commit into
TypeCellOS:mainfrom
mkcash:fix/image-tab-indent
Closed

fix: Allow indenting image blocks in lists with Tab#2781
mkcash wants to merge 1 commit into
TypeCellOS:mainfrom
mkcash:fix/image-tab-indent

Conversation

@mkcash
Copy link
Copy Markdown

@mkcash mkcash commented May 26, 2026

Fixes #2545

Expands BlockNote nesting range check to include listItem nodes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved list nesting and unnesting operations to work in additional contexts, expanding where these features can be applied within your documents.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

Someone is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

The PR expands node-type predicates in nestBlock.ts to accept listItem nodes alongside existing blockGroup and column nodes. The sinkItem predicate (line 28) and liftItem predicate (line 172) are updated to enable nesting and unnesting operations for list item containers.

Changes

List item nesting and unnesting support

Layer / File(s) Summary
Expand node-type predicates for list item support
packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts
sinkItem and liftItem range predicates now include "listItem" as a valid node type in addition to "blockGroup" and "column", enabling list nesting and unnesting operations to apply to list item containers.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • TypeCellOS/BlockNote#2601: Both PRs modify the ProseMirror-style list manipulation logic inside nestBlock.ts—specifically the node-type predicates and lift/unnest behavior for identifying valid list/container candidates.

Suggested reviewers

  • nperez0111

Poem

🐇 A rabbit hops through nested lists,
Adding items left and right,
With listItem now in the mix,
Indentation feels just right! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: allowing indentation of image blocks in lists using Tab, which matches the code modification to include listItem nodes in nesting predicates.
Description check ✅ Passed The description is minimal but directly addresses the issue. It lacks detail on testing, impact analysis, and other template sections, but clearly states the purpose and references the linked issue.
Linked Issues check ✅ Passed The PR directly addresses issue #2545 by modifying node-type predicates to include listItem nodes, enabling Tab indentation of image blocks in lists as requested in the issue.
Out of Scope Changes check ✅ Passed All changes are focused on the stated objective: modifying nesting predicates to support listItem nodes for image indentation in lists. No out-of-scope modifications are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

Copy link
Copy Markdown

@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

🤖 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 `@packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts`:
- Line 28: The sink/lift predicates in nestBlock (used in the nestBlock command)
currently check for node.type.name === "listItem", which doesn't exist in our
BlockNote schema; update the predicate expressions in nestBlock (both
occurrences used to compute sink/lift ranges) to check for the actual list-item
node names like "bulletListItem" | "numberedListItem" | "checkListItem" |
"toggleListItem" or simply allow their wrapper nodes such as "blockContainer" /
"blockGroup" depending on intended behavior; modify the condition (e.g. replace
node.type.name === "listItem") to compare against the correct node names or
include an OR for the wrapper node names so list indentation (Tab/Shift+Tab)
works with BlockNote list types.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7307f433-3692-4e35-8090-eb06b882574e

📥 Commits

Reviewing files that changed from the base of the PR and between 118d8dc and 19a9c23.

📒 Files selected for processing (1)
  • packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts

(node) =>
node.childCount > 0 &&
(node.type.name === "blockGroup" || node.type.name === "column"), // change 1
(node.type.name === "blockGroup" || node.type.name === "column" || node.type.name === "listItem"), // change 1
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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify listItem schema definition supports blockGroup/blockContainer children

# Search for listItem schema definition
echo "=== Searching for listItem schema definition ==="
rg -n -A 10 -B 2 'listItem.*spec|content.*listItem' --type=ts

# Search for blockGroup and blockContainer schema definitions  
echo -e "\n=== Searching for blockGroup/blockContainer schema definitions ==="
rg -n -A 5 'blockGroup.*spec|blockContainer.*spec' --type=ts

# Look for schema files that might define node relationships
echo -e "\n=== Searching for schema definition files ==="
fd -e ts -e js schema --exec echo {} \; --exec head -50 {}

Repository: TypeCellOS/BlockNote

Length of output: 15686


🏁 Script executed:

#!/bin/bash
# Find the actual ProseMirror node spec for "listItem" and inspect its "content" / children model.

set -euo pipefail

echo "=== Locate listItem node spec (node name / spec) ==="
rg -n --hidden --no-ignore-vcs '"listItem"|\\blistItem\\b' packages/core/src -S

echo -e "\n=== Look for ProseMirror NodeSpec definitions mentioning listItem ==="
rg -n --hidden --no-ignore-vcs 'NodeSpec|content:|group:|draggable:|isolating:|definitelyTyped|addAttributes|parseDOM|toDOM' packages/core/src/blocks/ListItem -S || true

echo -e "\n=== Inspect ListItem block implementation files ==="
fd -t f -e ts packages/core/src/blocks/ListItem -0 | xargs -0 -n1 echo

echo -e "\n=== Show getListItemContent.ts (where it assumes blockGroup children) ==="
sed -n '1,220p' packages/core/src/blocks/ListItem/getListItemContent.ts

echo -e "\n=== Find where the listItem node is created in schema/spec ==="
rg -n --hidden --no-ignore-vcs 'getListItemContent|listItemNode|schema\.nodes\["listItem"\]|schema\.nodes\[name\]' packages/core/src/blocks/ListItem -S

echo -e "\n=== Search for the listItem ProseMirror spec file under schema/ ==="
fd -t f -e ts packages/core/src/schema packages/core/src/blocks -0 | \
  xargs -0 rg -n --hidden --no-ignore-vcs 'listItem' -S | head -n 200

Repository: TypeCellOS/BlockNote

Length of output: 8594


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Open getListItemContent.ts (lines near earlier matches) ==="
sed -n '1,220p' packages/core/src/blocks/ListItem/getListItemContent.ts

echo -e "\n=== Search for actual ProseMirror node name/spec for 'listItem' in core schema code ==="
rg -n --hidden --no-ignore-vcs 'name:\s*["'\'']listItem["'\'']|nodeType.*listItem|schema\.nodes\[\s*["'\'']listItem["'\'']\s*\]|listItem\s*:\s*\{' packages/core/src -S

echo -e "\n=== Search for node spec content model referencing listItem ==="
rg -n --hidden --no-ignore-vcs 'listItem.*content:|content:.*listItem|prosemirror.*listItem|NodeSpec.*listItem' packages/core/src -S

echo -e "\n=== Inspect list item block spec files that might wrap/produce the ProseMirror listItem node ==="
for f in $(fd -t f packages/core/src/blocks/ListItem -0 --full-path | tr '\0' '\n' | head -n 50); do
  echo "---- $f ----"
  head -n 80 "$f"
done

Repository: TypeCellOS/BlockNote

Length of output: 5917


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect nestBlock.ts around the predicate and structure creation ==="
sed -n '1,260p' packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts

echo -e "\n=== Inspect list item block specs (content model) ==="
for f in \
  packages/core/src/blocks/ListItem/BulletListItem/block.ts \
  packages/core/src/blocks/ListItem/NumberedListItem/block.ts \
  packages/core/src/blocks/ListItem/CheckListItem/block.ts \
  packages/core/src/blocks/ListItem/ToggleListItem/block.ts
do
  echo -e "\n---- $f ----"
  sed -n '1,120p' "$f"
done

echo -e "\n=== Search for ProseMirror node 'blockGroup'/'blockContainer' content models ==="
rg -n --hidden --no-ignore-vcs 'name:\s*["'\'']blockGroup["'\'']|name:\s*["'\'']blockContainer["'\'']' packages/core/src/blocks packages/core/src/schema -S

Repository: TypeCellOS/BlockNote

Length of output: 20942


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for where ProseMirror node 'listItem' is defined in schema ==="
rg -n --hidden --no-ignore-vcs '"listItem"\s*:' packages/core/src/schema packages/core/src -S || true
rg -n --hidden --no-ignore-vcs 'pmSchema\.nodes\["listItem"\]|nodes\["listItem"\]|name:\s*["'"'"']listItem["'"'"']' packages/core/src -S || true
rg -n --hidden --no-ignore-vcs 'prosemirror-schema-list|schema-list' packages/core/src -S || true

echo -e "\n=== Search for building blocks / node specs that mention listItem as a node name ==="
rg -n --hidden --no-ignore-vcs 'listItem.*(content|group|blockGroup|blockContainer|child)' packages/core/src -S || true

echo -e "\n=== List files likely responsible for ProseMirror schema construction ==="
fd -t f --full-path packages/core/src/schema | head -n 200

Repository: TypeCellOS/BlockNote

Length of output: 2984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find how createBlockConfig/content: 'inline' maps to ProseMirror node content model ==="
rg -n --hidden --no-ignore-vcs 'content:\s*["'\'']inline["'\'']|content:\s*{|\binline['\'"]' packages/core/src/schema -S
rg -n --hidden --no-ignore-vcs 'createBlockConfig|createBlockSpec|contentType|BlockSpec|content:' packages/core/src/schema/blocks packages/core/src/schema -S

echo -e "\n=== Inspect schema spec builder for blocks ==="
rg -n --hidden --no-ignore-vcs 'content.*inline|inline.*content|addToEnd|blockGroup|blockContainer' packages/core/src/schema/blocks -S || true

echo -e "\n=== Inspect createSpec.ts / internal.ts where node content is set ==="
for f in packages/core/src/schema/blocks/createSpec.ts packages/core/src/schema/blocks/internal.ts packages/core/src/schema/blocks/types.ts packages/core/src/schema/blocks/internal.ts; do
  if [ -f "$f" ]; then
    echo -e "\n---- $f ----"
    sed -n '1,240p' "$f"
  fi
done

Repository: TypeCellOS/BlockNote

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find how createBlockConfig/content: 'inline' maps to ProseMirror node content model ==="
rg -n --hidden --no-ignore-vcs "content:\s*['\"]inline['\"]" packages/core/src/schema -S || true
rg -n --hidden --no-ignore-vcs "content:\s*inline" packages/core/src/schema -S || true

echo -e "\n=== Inspect schema spec builder for blocks ==="
rg -n --hidden --no-ignore-vcs "createBlockConfig|createBlockSpec|content:\s*|contentType|contentSpec|BlockSpec|blockGroup|blockContainer" packages/core/src/schema/blocks -S || true

echo -e "\n=== Inspect createSpec.ts / internal.ts where node content is set ==="
for f in \
  packages/core/src/schema/blocks/createSpec.ts \
  packages/core/src/schema/blocks/internal.ts \
  packages/core/src/schema/blocks/types.ts
do
  echo -e "\n---- $f ----"
  sed -n '1,260p' "$f"
done

Repository: TypeCellOS/BlockNote

Length of output: 28252


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search node specs for blockGroup and blockContainer (content + group) ==="
rg -n --hidden --no-ignore-vcs 'name:\s*["'\'']blockGroup["'\'']|name:\s*["'\'']blockContainer["'\'']|blockGroup:\s*\{|blockContainer:\s*\{' packages/core/src/schema packages/core/src -S || true

echo -e "\n=== Search for group definitions involving 'inline' ==="
rg -n --hidden --no-ignore-vcs 'group:\s*["'\'']inline["'\'']' packages/core/src/schema packages/core/src -S || true

echo -e "\n=== Search for node specs where listItem node might be customized ==="
rg -n --hidden --no-ignore-vcs 'listItem.*(content|group|parseHTML|renderHTML|addAttributes|isolating|defining)|name:\s*["'\'']listItem["'\'']' packages/core/src/schema packages/core/src -S || true

echo -e "\n=== Inspect schema/blocks/internal or schema/blocks/types for blockContainer/blockGroup content wiring ==="
rg -n --hidden --no-ignore-vcs 'blockGroup|blockContainer' packages/core/src/schema/schema.ts packages/core/src/schema/blocks -S | head -n 200 || true

Repository: TypeCellOS/BlockNote

Length of output: 1868


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect pm-nodes/BlockGroup.ts ==="
sed -n '1,220p' packages/core/src/pm-nodes/BlockGroup.ts

echo -e "\n=== Inspect pm-nodes/BlockContainer.ts ==="
sed -n '1,260p' packages/core/src/pm-nodes/BlockContainer.ts

echo -e "\n=== Search for ProseMirror pm-nodes named listItem ==="
rg -n --hidden --no-ignore-vcs 'name:\s*["'\'']listItem["'\'']|listItem' packages/core/src/pm-nodes packages/core/src/schema packages/core/src/blocks -S || true

echo -e "\n=== List files in pm-nodes to locate potential list nodes ==="
ls -1 packages/core/src/pm-nodes

Repository: TypeCellOS/BlockNote

Length of output: 6620


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Global search for ProseMirror node name \"listItem\" ==="
rg -n --hidden --no-ignore-vcs 'name:\s*["'\'']listItem["'\'']|nodes\[\s*["'\'']listItem["'\'']\]|"listItem"' packages/core/src -S || true

echo -e "\n=== Search for any Node.create(... name: \"listItem\" ) patterns ==="
rg -n --hidden --no-ignore-vcs 'Node\.create<[^>]*>|Node\.create\(|name:\s*["'\'']listItem["'\'']' packages/core/src -S | head -n 200

echo -e "\n=== Search for list container/pair nodes (bulletList/numberedList) in core blocks ==="
rg -n --hidden --no-ignore-vcs 'type:\s*["'\''](bulletList|numberedList|orderedList|unorderedList|list)[^"'\'']*["'\'']|create.*List' packages/core/src/blocks packages/core/src -S || true

echo -e "\n=== Search for 'li' parsing utilities that might create PM 'listItem' ==="
rg -n --hidden --no-ignore-vcs 'prosemirror.*listItem|schema-list|listItem.*insert|listItem.*create' packages/core/src -S || true

Repository: TypeCellOS/BlockNote

Length of output: 23234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for Tiptap list extension usage in packages/core/src ==="
rg -n --hidden --no-ignore-vcs '`@tiptap/`(extension-)?list|ExtensionList|listItem|orderedList|bulletList' packages/core/src -S || true

echo -e "\n=== Inspect where pmSchema is constructed/extended ==="
rg -n --hidden --no-ignore-vcs 'pmSchema|prosemirror schema|new Schema|Schema\.create|schema\.nodes' packages/core/src/editor packages/core/src/schema packages/core/src -S | head -n 200

echo -e "\n=== List pm-nodes index.ts exports ==="
sed -n '1,200p' packages/core/src/pm-nodes/index.ts

Repository: TypeCellOS/BlockNote

Length of output: 34675


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect BlockNoteEditor.ts around pmSchema creation/initialization ==="
sed -n '420,720p' packages/core/src/editor/BlockNoteEditor.ts

echo -e "\n=== Search for list-related tiptap extensions imports/usages in BlockNoteEditor.ts ==="
rg -n --hidden --no-ignore-vcs 'listItem|ExtensionList|orderedList|bulletList|tiptap-extensions/.*list|`@tiptap/`(extension-)?list' packages/core/src/editor/BlockNoteEditor.ts packages/core/src/editor -S || true

Repository: TypeCellOS/BlockNote

Length of output: 14719


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect ExtensionManager.getTiptapExtensions implementation ==="
rg -n --hidden --no-ignore-vcs 'class ExtensionManager|getTiptapExtensions' packages/core/src/editor packages/core/src -S
rg -n --hidden --no-ignore-vcs 'getTiptapExtensions\(' packages/core/src/editor packages/core/src -S

echo -e "\n=== Open ExtensionManager file(s) around getTiptapExtensions ==="
f=$(rg -l --hidden --no-ignore-vcs 'getTiptapExtensions' packages/core/src/editor | head -n 20)
echo "Files: $f"
for x in $f; do
  echo -e "\n---- $x ----"
  sed -n '1,260p' "$x"
done

echo -e "\n=== Search for tiptap list extension in ExtensionManager and related files ==="
rg -n --hidden --no-ignore-vcs '`@tiptap/extension-`(bullet|ordered|list|list-item)|ExtensionList|listItem' packages/core/src/editor packages/core/src -S || true

Repository: TypeCellOS/BlockNote

Length of output: 23430


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== List ExtensionManager extensions files ==="
ls -1 packages/core/src/editor/managers/ExtensionManager

echo -e "\n=== Inspect extensions.js (default extension lists) ==="
sed -n '1,260p' packages/core/src/editor/managers/ExtensionManager/extensions.js

echo -e "\n=== Search default tiptap extensions for listItem/list extension ==="
rg -n --hidden --no-ignore-vcs 'listItem|extension-list|bulletList|orderedList|list-item|tiptap.*list' packages/core/src/editor/managers/ExtensionManager/extensions.js packages/core/src/editor/managers/ExtensionManager -S || true

echo -e "\n=== Inspect getDefaultTiptapExtensions definition usages ==="
rg -n --hidden --no-ignore-vcs 'getDefaultTiptapExtensions|getDefaultExtensions' packages/core/src/editor/managers/ExtensionManager -S

Repository: TypeCellOS/BlockNote

Length of output: 327


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Inspect ExtensionManager/extensions.ts ==="
sed -n '1,260p' packages/core/src/editor/managers/ExtensionManager/extensions.ts

echo -e "\n=== Search within extensions.ts for list-related tiptap extensions ==="
rg -n --hidden --no-ignore-vcs '(listItem|extension-list|orderedList|bulletList|list-item|tiptap.*list)' packages/core/src/editor/managers/ExtensionManager/extensions.ts packages/core/src/editor/managers/ExtensionManager -S || true

echo -e "\n=== Inspect ExtensionManager/index.ts for getDefaultTiptapExtensions usage ==="
rg -n --hidden --no-ignore-vcs 'getDefaultTiptapExtensions|getDefaultExtensions' packages/core/src/editor/managers/ExtensionManager/index.ts
sed -n '260,420p' packages/core/src/editor/managers/ExtensionManager/index.ts

Repository: TypeCellOS/BlockNote

Length of output: 12325


Fix nestBlock predicates to use BlockNote list-item node names (not ProseMirror listItem)

nestBlock.ts adds node.type.name === "listItem" to the sink/lift range predicates (lines 28 and 172), but this BlockNote schema does not define any ProseMirror node named listItem (default tiptap nodes are doc, blockGroup, and blockContainer; list-item blocks are bulletListItem / numberedListItem / checkListItem / toggleListItem, each with content: "inline"). As written, the listItem predicate won’t match anything, so list indentation for nested blocks won’t rely on schema hierarchy here.

Update the predicate(s) to target the actual list-item node types (or the correct wrapper nodes, e.g. the surrounding blockContainer / blockGroup) that should be allowed as nesting parents for Tab/Shift+Tab.

🤖 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 `@packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts` at
line 28, The sink/lift predicates in nestBlock (used in the nestBlock command)
currently check for node.type.name === "listItem", which doesn't exist in our
BlockNote schema; update the predicate expressions in nestBlock (both
occurrences used to compute sink/lift ranges) to check for the actual list-item
node names like "bulletListItem" | "numberedListItem" | "checkListItem" |
"toggleListItem" or simply allow their wrapper nodes such as "blockContainer" /
"blockGroup" depending on intended behavior; modify the condition (e.g. replace
node.type.name === "listItem") to compare against the correct node names or
include an OR for the wrapper node names so list indentation (Tab/Shift+Tab)
works with BlockNote list types.

@nperez0111 nperez0111 closed this May 26, 2026
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.

Can't indent an image with tab or toolbar

2 participants