Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6d7d2e7
[lexical-playground] Feature: Add PagesExtension and enhance print st…
ibastawisi Apr 9, 2026
ade5af8
[lexical-playground] Add print styles to hide page-break elements
ibastawisi Apr 9, 2026
80740b8
[lexical-playground] Refactor PageNode and PageContentNode to use $co…
ibastawisi Apr 10, 2026
e09860b
[lexical-playground] Refactor PageSetupNode to utilize NodeState
ibastawisi Apr 10, 2026
63857b2
[lexical-playground] Remove unused DOM conversion and export methods …
ibastawisi Apr 10, 2026
50ed58c
Refactor PageSetupNode to RootNode state
etrepum Apr 10, 2026
6e576a4
Lift measurement from PageNode statics to extension state
etrepum Apr 10, 2026
e2c0b16
clean up measurement update
etrepum Apr 10, 2026
a214ad5
Make the extension mostly self-contained
etrepum Apr 10, 2026
d6a2899
provide signal for observing pageSetup
etrepum Apr 10, 2026
7671a82
Split extension into PagesExtension and PagesReactExtension, refactor…
etrepum Apr 10, 2026
d92bd21
remove unneeded import
etrepum Apr 10, 2026
609254d
set onnxruntime-node-install=skip to avoid fetching binaries we do no…
etrepum Apr 10, 2026
7ce8871
use $create in PageContentNode
etrepum Apr 10, 2026
5028abc
Merge branch 'main' into pages
etrepum Apr 10, 2026
6a7f25d
Export marginsIsEqual function for margin comparison and update PageS…
ibastawisi Apr 11, 2026
e6cab8d
Refactor PageSetupDropdown to prevent event propagation on item click…
ibastawisi Apr 11, 2026
fd290eb
Remove unused page setup node selection handling from PagesExtension.
ibastawisi Apr 11, 2026
928c1c0
Refactor PagesExtension to streamline page measurement and structure …
ibastawisi Apr 11, 2026
27a481e
Refactor PageNode and PagesExtension to remove the measurement logic …
ibastawisi Apr 11, 2026
3cc6e96
use editor context in dev tools generateContent
etrepum Apr 11, 2026
4dab534
Merge branch 'main' into pages
etrepum Apr 11, 2026
eb3c183
Merge branch 'main' into pages
etrepum Apr 11, 2026
e95b85f
Add CSS rule to hide page-break elements in paged mode
ibastawisi Apr 11, 2026
20e33f2
Refactor PagesExtension registration to ensure page dimensions are up…
ibastawisi Apr 11, 2026
370029a
Enhance PagesExtension functionality by adding node selection and imp…
ibastawisi Apr 11, 2026
2fc5f67
Merge remote-tracking branch 'origin/main' into pages
etrepum Apr 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ npm-debug.log*
.idea/**/shelf

.idea

.claude
1 change: 1 addition & 0 deletions .pnpmrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# This is needed because test files use ESM (.mjs) which has issues
# with pnpm's default symlinked structure
node-linker=hoisted
onnxruntime-node-install=skip
80 changes: 43 additions & 37 deletions packages/lexical-devtools-core/src/generateContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,49 +107,55 @@ export function generateContent(

if (exportDOM) {
let htmlString = '';
editorState.read(() => {
htmlString = printPrettyHTML($generateHtmlFromNodes(editor));
});
editorState.read(
() => {
htmlString = printPrettyHTML($generateHtmlFromNodes(editor));
},
{editor},
);
return htmlString;
}

let res = ' root\n';

const selectionString = editorState.read(() => {
const selection = $getSelection();

visitTree($getRoot(), (node: LexicalNode, indent: Array<string>) => {
const nodeKey = node.getKey();
const nodeKeyDisplay = `(${nodeKey})`;
const typeDisplay = node.getType() || '';
const isSelected = node.isSelected();

res += `${isSelected ? SYMBOLS.selectedLine : ' '} ${indent.join(
' ',
)} ${nodeKeyDisplay} ${typeDisplay} ${printNode(
node,
customPrintNode,
obfuscateText,
)}\n`;

res += $printSelectedCharsLine({
indent,
isSelected,
node,
nodeKeyDisplay,
selection,
typeDisplay,
const selectionString = editorState.read(
() => {
const selection = $getSelection();

visitTree($getRoot(), (node: LexicalNode, indent: Array<string>) => {
const nodeKey = node.getKey();
const nodeKeyDisplay = `(${nodeKey})`;
const typeDisplay = node.getType() || '';
const isSelected = node.isSelected();

res += `${isSelected ? SYMBOLS.selectedLine : ' '} ${indent.join(
' ',
)} ${nodeKeyDisplay} ${typeDisplay} ${printNode(
node,
customPrintNode,
obfuscateText,
)}\n`;

res += $printSelectedCharsLine({
indent,
isSelected,
node,
nodeKeyDisplay,
selection,
typeDisplay,
});
});
});

return selection === null
? ': null'
: $isRangeSelection(selection)
? printRangeSelection(selection)
: $isTableSelection(selection)
? printTableSelection(selection)
: printNodeSelection(selection);
});

return selection === null
? ': null'
: $isRangeSelection(selection)
? printRangeSelection(selection)
: $isTableSelection(selection)
? printTableSelection(selection)
: printNodeSelection(selection);
},
{editor},
);

res += '\n selection' + selectionString;

Expand Down
4 changes: 4 additions & 0 deletions packages/lexical-playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import {EmojisExtension} from './plugins/EmojisExtension';
import {ImagesExtension} from './plugins/ImagesExtension';
import {PlaygroundMarkdownShortcutsExtension} from './plugins/MarkdownShortcutsExtension';
import {MaxLengthExtension} from './plugins/MaxLengthPlugin';
import {PageBreakExtension} from './plugins/PageBreakExtension';
import {PagesReactExtension} from './plugins/PagesReactExtension';
import PasteLogPlugin from './plugins/PasteLogPlugin';
import TestRecorderPlugin from './plugins/TestRecorderPlugin';
import TypingPerfPlugin from './plugins/TypingPerfPlugin';
Expand Down Expand Up @@ -160,6 +162,8 @@ const PlaygroundRichTextExtension = defineExtension({
configExtension(ListExtension, {shouldPreserveNumbering: false}),
CheckListExtension,
PlaygroundMarkdownShortcutsExtension,
PageBreakExtension,
PagesReactExtension,
],
name: '@lexical/playground/RichText',
});
Expand Down
2 changes: 0 additions & 2 deletions packages/lexical-playground/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ import FloatingTextFormatToolbarPlugin from './plugins/FloatingTextFormatToolbar
import {LayoutPlugin} from './plugins/LayoutPlugin/LayoutPlugin';
import {MaxLengthExtension} from './plugins/MaxLengthPlugin';
import MentionsPlugin from './plugins/MentionsPlugin';
import PageBreakPlugin from './plugins/PageBreakPlugin';
import PollPlugin from './plugins/PollPlugin';
import ShortcutsPlugin from './plugins/ShortcutsPlugin';
import SpecialTextPlugin from './plugins/SpecialTextPlugin';
Expand Down Expand Up @@ -263,7 +262,6 @@ export default function Editor(): JSX.Element {
<TabFocusPlugin />
<TabIndentationPlugin maxIndent={7} />
<CollapsiblePlugin />
<PageBreakPlugin />
<LayoutPlugin />
{floatingAnchorElem && (
<>
Expand Down
68 changes: 67 additions & 1 deletion packages/lexical-playground/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ header h1 {
.editor-shell {
margin: 20px auto;
border-radius: 2px;
max-width: 1100px;
max-width: 1128px;
color: #000;
position: relative;
line-height: 1.7;
Expand Down Expand Up @@ -95,6 +95,52 @@ header h1 {
z-index: -1;
}

@media print {
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}

*,
*:before,
*:after {
filter: unset !important;
color-scheme: light !important;
}

body {
padding: 0 !important;
margin: 0 !important;
background: none !important;
}

body
*:not(
[data-lexical-editor],
[data-lexical-editor] *,
:has([data-lexical-editor])
) {
display: none !important;
}

.editor-shell {
margin: 0 !important;
border-radius: 0 !important;
max-width: none !important;
}

[data-lexical-editor] {
padding: 0 !important;
margin: 0 !important;
border: none !important;
box-shadow: none !important;
max-width: none !important;
width: 100% !important;
height: auto !important;
zoom: 1 !important;
}
}

.test-recorder-output {
margin: 20px auto 20px auto;
width: 100%;
Expand Down Expand Up @@ -609,6 +655,11 @@ i.page-break,
background-image: url(images/icons/scissors.svg);
}

i.page-setup,
.icon.page-setup {
background-image: url(images/icons/file-earmark-text.svg);
}

.link-editor .button.active,
.toolbar .button.active {
background-color: rgb(223, 232, 250);
Expand Down Expand Up @@ -829,6 +880,21 @@ i.page-break,
min-width: 150px;
}

.dropdown .item.dropdown-submenu-trigger .dropdown-submenu-chevron {
flex-shrink: 0;
margin-left: 8px;
transform: rotate(0deg);
transition: transform 0.15s ease;
}

.dropdown .item.dropdown-submenu-trigger.expanded .dropdown-submenu-chevron {
transform: rotate(180deg);
}

.dropdown .item.dropdown-submenu-item {
padding-left: 24px;
}

.dropdown .item .icon {
display: flex;
width: 20px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@
.selected[type='page-break']::before {
opacity: 1;
}

@media print {
[type='page-break'] {
visibility: hidden;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {InsertEquationDialog} from '../EquationsPlugin';
import {INSERT_EXCALIDRAW_COMMAND} from '../ExcalidrawPlugin';
import {INSERT_IMAGE_COMMAND, InsertImageDialog} from '../ImagesExtension';
import InsertLayoutDialog from '../LayoutPlugin/InsertLayoutDialog';
import {INSERT_PAGE_BREAK} from '../PageBreakPlugin';
import {INSERT_PAGE_BREAK} from '../PageBreakExtension';
import {InsertPollDialog} from '../PollPlugin';
import {InsertTableDialog} from '../TablePlugin';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {ReactExtension} from '@lexical/react/ReactExtension';
import {$insertNodeToNearestRoot} from '@lexical/utils';
import {
$getSelection,
$isRangeSelection,
COMMAND_PRIORITY_EDITOR,
createCommand,
defineExtension,
LexicalCommand,
} from 'lexical';

import {$createPageBreakNode, PageBreakNode} from '../../nodes/PageBreakNode';

export const INSERT_PAGE_BREAK: LexicalCommand<undefined> = createCommand();

export const PageBreakExtension = defineExtension({
dependencies: [ReactExtension],
name: '@lexical/playground/PageBreak',
nodes: () => [PageBreakNode],
register: (editor) =>
editor.registerCommand(
INSERT_PAGE_BREAK,
() => {
const selection = $getSelection();

if (!$isRangeSelection(selection)) {
return false;
}

const pgBreak = $createPageBreakNode();
$insertNodeToNearestRoot(pgBreak);

return true;
},
COMMAND_PRIORITY_EDITOR,
),
});
60 changes: 0 additions & 60 deletions packages/lexical-playground/src/plugins/PageBreakPlugin/index.tsx

This file was deleted.

Loading
Loading