This commit is contained in:
2026-05-14 19:55:53 +08:00
parent c764603dbd
commit 8a16b01e9e
55 changed files with 5754 additions and 5277 deletions
+10
View File
@@ -97,6 +97,12 @@ test.db1:
```bash
cd cyynote-backend
mvn clean package
mvn clean package -DskipTests
mvn solon:run
java -jar demo.jar
java -jar target/cyynote.jar
```
@@ -177,7 +183,11 @@ VITE_WS_URL=ws://localhost:1234
```bash
cd cyynote-frontend
若网络不好
pnpm config set registry https://registry.npmmirror.com/
pnpm install
pnpm dev
```
Binary file not shown.
+1 -1
View File
@@ -1,3 +1,3 @@
test.db1:
jdbcUrl: "jdbc:sqlite:D:/cyynote.db"
jdbcUrl: "jdbc:sqlite:./cyynote.db"
driverClassName: "org.sqlite.JDBC"
+53 -52
View File
@@ -1,53 +1,54 @@
CREATE TABLE `user` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT,
password TEXT,
username TEXT,
role TEXT,
token TEXT,
logintime TEXT
);
CREATE TABLE `note` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pid INTEGER,
title TEXT,
context TEXT,
conjson TEXT,
createtime TEXT,
updatetime TEXT,
viewtime TEXT,
userid INTEGER,
username TEXT,
flag INTEGER DEFAULT 1
);
CREATE TABLE `history` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nid INTEGER,
title TEXT,
context TEXT,
conjson TEXT,
createtime TEXT,
userid INTEGER,
username TEXT
);
CREATE TABLE appx (
app_id INTEGER PRIMARY KEY AUTOINCREMENT,
app_key TEXT,
akey TEXT,
ugroup_id INTEGER DEFAULT 0,
agroup_id INTEGER,
name TEXT,
note TEXT,
ar_is_setting INTEGER NOT NULL DEFAULT 0,
ar_is_examine INTEGER NOT NULL DEFAULT 0,
ar_examine_ver INTEGER NOT NULL DEFAULT 0,
log_fulltime TEXT
);
INSERT INTO `user` VALUES ('1', 'aaaaaaaaa', 'cyy123', 'admin', 'admin','','');
INSERT INTO `user` VALUES ('2', 'aaaaaaaaa', 'test', 'test', 'test','','');
CREATE TABLE `user` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT,
password TEXT,
username TEXT,
role TEXT,
token TEXT,
logintime TEXT
);
CREATE TABLE `note` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pid INTEGER,
title TEXT,
context TEXT,
conjson TEXT,
createtime TEXT,
updatetime TEXT,
viewtime TEXT,
userid INTEGER,
username TEXT,
flag INTEGER DEFAULT 1
);
CREATE TABLE `history` (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nid INTEGER,
title TEXT,
context TEXT,
conjson TEXT,
createtime TEXT,
userid INTEGER,
username TEXT,
flag INTEGER DEFAULT 1
);
CREATE TABLE appx (
app_id INTEGER PRIMARY KEY AUTOINCREMENT,
app_key TEXT,
akey TEXT,
ugroup_id INTEGER DEFAULT 0,
agroup_id INTEGER,
name TEXT,
note TEXT,
ar_is_setting INTEGER NOT NULL DEFAULT 0,
ar_is_examine INTEGER NOT NULL DEFAULT 0,
ar_examine_ver INTEGER NOT NULL DEFAULT 0,
log_fulltime TEXT
);
INSERT INTO `user` VALUES ('1', 'aaaaaaaaa', 'cyy123', 'admin', 'admin','','');
INSERT INTO `user` VALUES ('2', 'aaaaaaaaa', 'test', 'test', 'test','','');
INSERT INTO `note` VALUES ('1', '0', 'test', 'test', 'test','','','','1','1','1');
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
#Generated by Maven
#Wed Apr 29 15:30:28 CST 2026
#Thu May 14 15:10:41 CST 2026
groupId=com.cyynote
artifactId=cyynote
version=1.0.0
+2 -1
View File
@@ -1,4 +1,3 @@
{
"name": "cyynote",
"private": true,
@@ -42,6 +41,7 @@
"html-react-parser": "^5.2.0",
"katex": "^0.16.21",
"lexical": "0.44.0",
"lodash-es": "^4.18.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
@@ -52,6 +52,7 @@
"zustand": "^5.0.3"
},
"devDependencies": {
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.3.20",
"@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "^8.30.0",
+5537
View File
File diff suppressed because it is too large Load Diff
@@ -10,9 +10,9 @@ import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {CharacterLimitPlugin} from '@lexical/react/LexicalCharacterLimitPlugin';
import {CheckListPlugin} from '@lexical/react/LexicalCheckListPlugin';
import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';
import LexicalClickableLinkPlugin from '@lexical/react/LexicalClickableLinkPlugin';
import {ClickableLinkPlugin} from '@lexical/react/LexicalClickableLinkPlugin';
import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
import {HashtagPlugin} from '@lexical/react/LexicalHashtagPlugin';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {HorizontalRulePlugin} from '@lexical/react/LexicalHorizontalRulePlugin';
@@ -21,7 +21,7 @@ import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
import {TabIndentationPlugin} from '@lexical/react/LexicalTabIndentationPlugin';
import {TablePlugin} from '@lexical/react/LexicalTablePlugin';
import useLexicalEditable from '@lexical/react/useLexicalEditable';
import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
import {useEffect, useState,useLayoutEffect} from 'react';
import {CAN_USE_DOM} from './shared/src/canUseDOM';
@@ -221,7 +221,7 @@ export default function Editor(props: { noteId: bigint; noteTitle: string; editD
<TwitterPlugin />
<YouTubePlugin />
<FigmaPlugin />
{!isEditable && <LexicalClickableLinkPlugin />}
{!isEditable && <ClickableLinkPlugin />}
<HorizontalRulePlugin />
<EquationsPlugin />
<ExcalidrawPlugin />
@@ -9,6 +9,7 @@ import "./index.css";
import {$createLinkNode} from '@lexical/link';
import {$createListItemNode, $createListNode} from '@lexical/list';
import {LexicalComposer} from '@lexical/react/LexicalComposer';
import {LexicalCollaboration} from '@lexical/react/LexicalCollaborationContext';
import {$createHeadingNode, $createQuoteNode} from '@lexical/rich-text';
import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
@@ -121,13 +122,27 @@ function LexicalApp(props: { noteId: bigint; editData: object | null; noteTitle:
console.log(props.editData!==null)
console.log(prepopulatedRichText)
const resolveEditorState = (): string | null | undefined => {
if (props.editData === null) return undefined; // use prepopulatedRichText via function below
const raw = props.editData as unknown as string;
if (typeof raw === 'string') {
try {
JSON.parse(raw);
return raw;
} catch {
return null; // not valid Lexical JSON, start with empty editor
}
}
return undefined;
};
const editorState = resolveEditorState();
const resolvedEditorState = editorState === undefined && props.editData === null
? prepopulatedRichText
: editorState;
const initialConfig = {
// editorState: isCollab
// ? null
// : emptyEditor
// ? undefined
// : prepopulatedRichText,
editorState: props.editData!==null?props.editData:prepopulatedRichText,
editorState: resolvedEditorState,
namespace: 'Playground',
nodes: [...PlaygroundNodes],
onError: (error: Error) => {
@@ -137,6 +152,7 @@ function LexicalApp(props: { noteId: bigint; editData: object | null; noteTitle:
};
return (
<LexicalCollaboration>
<LexicalComposer initialConfig={initialConfig}>
<SharedHistoryContext>
<TableContext>
@@ -159,6 +175,7 @@ function LexicalApp(props: { noteId: bigint; editData: object | null; noteTitle:
</TableContext>
</SharedHistoryContext>
</LexicalComposer>
</LexicalCollaboration>
);
}
@@ -46,9 +46,8 @@ header h1 {
}
.editor-shell {
margin: 20px auto;
margin: 0;
border-radius: 2px;
max-width: 1100px;
color: #000;
position: relative;
line-height: 1.7;
@@ -61,6 +60,7 @@ header h1 {
display: block;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
padding: 8px 15px 15px;
}
.editor-shell .editor-container.tree-view {
@@ -1428,7 +1428,6 @@ button.action-button:disabled {
.toolbar {
display: flex;
margin-bottom: 1px;
background: #fff;
padding: 4px;
border-top-left-radius: 10px;
@@ -1438,7 +1437,9 @@ button.action-button:disabled {
height: 36px;
position: sticky;
top: 0;
z-index: 2;
z-index: 4;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-bottom: 1px solid #eee;
}
button.toolbar-item {
@@ -19,7 +19,7 @@ import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';
import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
import {HashtagPlugin} from '@lexical/react/LexicalHashtagPlugin';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer';
@@ -12,7 +12,7 @@ import './InlineImageNode.css';
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
import {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer';
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
import {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection';
@@ -13,7 +13,7 @@ import './StickyNode.css';
import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';
import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer';
import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';
@@ -32,7 +32,7 @@ import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContex
import {LexicalComposer} from '@lexical/react/LexicalComposer';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {EditorRefPlugin} from '@lexical/react/LexicalEditorRefPlugin';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin';
import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';
@@ -8,120 +8,40 @@
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {
LexicalContextMenuPlugin,
MenuOption,
} from '@lexical/react/LexicalContextMenuPlugin';
NodeContextMenuPlugin,
NodeContextMenuOption,
} from '@lexical/react/LexicalNodeContextMenuPlugin';
import {
type LexicalNode,
$getSelection,
$isRangeSelection,
COPY_COMMAND,
CUT_COMMAND,
PASTE_COMMAND,
} from 'lexical';
import {useCallback, useMemo} from 'react';
import {useMemo} from 'react';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
function ContextMenuItem({
index,
isSelected,
onClick,
onMouseEnter,
option,
}: {
index: number;
isSelected: boolean;
onClick: () => void;
onMouseEnter: () => void;
option: ContextMenuOption;
}) {
let className = 'item';
if (isSelected) {
className += ' selected';
}
return (
<li
key={option.key}
tabIndex={-1}
className={className}
ref={option.setRefElement}
role="option"
aria-selected={isSelected}
id={'typeahead-item-' + index}
onMouseEnter={onMouseEnter}
onClick={onClick}>
<span className="text">{option.title}</span>
</li>
);
}
function ContextMenu({
options,
selectedItemIndex,
onOptionClick,
onOptionMouseEnter,
}: {
selectedItemIndex: number | null;
onOptionClick: (option: ContextMenuOption, index: number) => void;
onOptionMouseEnter: (index: number) => void;
options: Array<ContextMenuOption>;
}) {
return (
<div className="typeahead-popover">
<ul>
{options.map((option: ContextMenuOption, i: number) => (
<ContextMenuItem
index={i}
isSelected={selectedItemIndex === i}
onClick={() => onOptionClick(option, i)}
onMouseEnter={() => onOptionMouseEnter(i)}
key={option.key}
option={option}
/>
))}
</ul>
</div>
);
}
export class ContextMenuOption extends MenuOption {
title: string;
onSelect: (targetNode: LexicalNode | null) => void;
constructor(
title: string,
options: {
onSelect: (targetNode: LexicalNode | null) => void;
},
) {
super(title);
this.title = title;
this.onSelect = options.onSelect.bind(this);
}
}
export default function ContextMenuPlugin(): JSX.Element {
const [editor] = useLexicalComposerContext();
const options = useMemo(() => {
const items = useMemo(() => {
return [
new ContextMenuOption(`Copy`, {
onSelect: (_node) => {
new NodeContextMenuOption('Copy', {
$onSelect: () => {
editor.dispatchCommand(COPY_COMMAND, null);
},
}),
new ContextMenuOption(`Cut`, {
onSelect: (_node) => {
new NodeContextMenuOption('Cut', {
$onSelect: () => {
editor.dispatchCommand(CUT_COMMAND, null);
},
}),
new ContextMenuOption(`Paste`, {
onSelect: (_node) => {
navigator.clipboard.read().then(async (...args) => {
new NodeContextMenuOption('Paste', {
$onSelect: () => {
navigator.clipboard.read().then(async () => {
const data = new DataTransfer();
const items = await navigator.clipboard.read();
const item = items[0];
const clipItems = await navigator.clipboard.read();
const item = clipItems[0];
const permission = await navigator.permissions.query({
// @ts-ignore These types are incorrect.
@@ -137,108 +57,48 @@ export default function ContextMenuPlugin(): JSX.Element {
data.setData(type, dataString);
}
const event = new ClipboardEvent('paste', {
clipboardData: data,
});
const event = new ClipboardEvent('paste', {clipboardData: data});
editor.dispatchCommand(PASTE_COMMAND, event);
});
},
}),
new ContextMenuOption(`Paste as Plain Text`, {
onSelect: (_node) => {
navigator.clipboard.read().then(async (...args) => {
new NodeContextMenuOption('Paste as Plain Text', {
$onSelect: () => {
navigator.clipboard.read().then(async () => {
const permission = await navigator.permissions.query({
// @ts-ignore These types are incorrect.
name: 'clipboard-read',
});
if (permission.state === 'denied') {
alert('Not allowed to paste from clipboard.');
return;
}
const data = new DataTransfer();
const items = await navigator.clipboard.readText();
data.setData('text/plain', items);
const text = await navigator.clipboard.readText();
data.setData('text/plain', text);
const event = new ClipboardEvent('paste', {
clipboardData: data,
});
const event = new ClipboardEvent('paste', {clipboardData: data});
editor.dispatchCommand(PASTE_COMMAND, event);
});
},
}),
new ContextMenuOption(`Delete Node`, {
onSelect: (_node) => {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
const currentNode = selection.anchor.getNode();
const ancestorNodeWithRootAsParent = currentNode
.getParents()
.at(-2);
ancestorNodeWithRootAsParent?.remove();
}
new NodeContextMenuOption('Delete Node', {
$onSelect: () => {
editor.update(() => {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
const currentNode = selection.anchor.getNode();
const ancestorNodeWithRootAsParent = currentNode
.getParents()
.at(-2);
ancestorNodeWithRootAsParent?.remove();
}
});
},
}),
];
}, [editor]);
const onSelectOption = useCallback(
(
selectedOption: ContextMenuOption,
targetNode: LexicalNode | null,
closeMenu: () => void,
) => {
editor.update(() => {
selectedOption.onSelect(targetNode);
closeMenu();
});
},
[editor],
);
return (
<LexicalContextMenuPlugin
options={options}
onSelectOption={onSelectOption}
menuRenderFn={(
anchorElementRef,
{
selectedIndex,
options: _options,
selectOptionAndCleanUp,
setHighlightedIndex,
},
{setMenuRef},
) =>
anchorElementRef.current
? ReactDOM.createPortal(
<div
className="typeahead-popover auto-embed-menu"
style={{
marginLeft: anchorElementRef.current.style.width,
userSelect: 'none',
width: 200,
}}
ref={setMenuRef}>
<ContextMenu
options={options}
selectedItemIndex={selectedIndex}
onOptionClick={(option: ContextMenuOption, index: number) => {
setHighlightedIndex(index);
selectOptionAndCleanUp(option);
}}
onOptionMouseEnter={(index: number) => {
setHighlightedIndex(index);
}}
/>
</div>,
anchorElementRef.current,
)
: null
}
/>
);
return <NodeContextMenuPlugin items={items} />;
}
@@ -7,16 +7,16 @@
*/
import type {
DEPRECATED_GridCellNode,
ElementNode,
LexicalEditor,
} from 'lexical';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import useLexicalEditable from '@lexical/react/useLexicalEditable';
import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
import {
$deleteTableColumn__EXPERIMENTAL,
$deleteTableRow__EXPERIMENTAL,
$getNodeTriplet,
$getTableCellNodeFromLexicalNode,
$getTableColumnIndexFromTableCellNode,
$getTableNodeFromLexicalNodeOrThrow,
@@ -25,11 +25,13 @@ import {
$insertTableRow__EXPERIMENTAL,
$isTableCellNode,
$isTableRowNode,
$isTableSelection,
$unmergeCell,
getTableSelectionFromTableElement,
getTableObserverFromTableElement,
HTMLTableElementWithWithTableSelectionState,
TableCellHeaderStates,
TableCellNode,
TableSelection,
} from '@lexical/table';
import {
$createParagraphNode,
@@ -39,10 +41,6 @@ import {
$isParagraphNode,
$isRangeSelection,
$isTextNode,
DEPRECATED_$getNodeTriplet,
DEPRECATED_$isGridCellNode,
DEPRECATED_$isGridSelection,
GridSelection,
} from 'lexical';
import * as React from 'react';
import {ReactPortal, useCallback, useEffect, useRef, useState} from 'react';
@@ -52,7 +50,7 @@ import invariant from '../../shared/src/invariant';
import useModal from '../../hooks/useModal';
import ColorPicker from '../../ui/ColorPicker';
function computeSelectionCount(selection: GridSelection): {
function computeSelectionCount(selection: TableSelection): {
columns: number;
rows: number;
} {
@@ -65,7 +63,7 @@ function computeSelectionCount(selection: GridSelection): {
// This is important when merging cells as there is no good way to re-merge weird shapes (a result
// of selecting merged cells and non-merged)
function isGridSelectionRectangular(selection: GridSelection): boolean {
function isTableSelectionRectangular(selection: TableSelection): boolean {
const nodes = selection.getNodes();
const currentRows: Array<number> = [];
let currentRow = null;
@@ -109,17 +107,17 @@ function $canUnmerge(): boolean {
const selection = $getSelection();
if (
($isRangeSelection(selection) && !selection.isCollapsed()) ||
(DEPRECATED_$isGridSelection(selection) &&
($isTableSelection(selection) &&
!selection.anchor.is(selection.focus)) ||
(!$isRangeSelection(selection) && !DEPRECATED_$isGridSelection(selection))
(!$isRangeSelection(selection) && !$isTableSelection(selection))
) {
return false;
}
const [cell] = DEPRECATED_$getNodeTriplet(selection.anchor);
const [cell] = $getNodeTriplet(selection.anchor);
return cell.__colSpan > 1 || cell.__rowSpan > 1;
}
function $cellContainsEmptyParagraph(cell: DEPRECATED_GridCellNode): boolean {
function $cellContainsEmptyParagraph(cell: TableCellNode): boolean {
if (cell.getChildrenSize() !== 1) {
return false;
}
@@ -146,9 +144,9 @@ function currentCellBackgroundColor(editor: LexicalEditor): null | string {
const selection = $getSelection();
if (
$isRangeSelection(selection) ||
DEPRECATED_$isGridSelection(selection)
$isTableSelection(selection)
) {
const [cell] = DEPRECATED_$getNodeTriplet(selection.anchor);
const [cell] = $getNodeTriplet(selection.anchor);
if ($isTableCellNode(cell)) {
return cell.getBackgroundColor();
}
@@ -208,11 +206,11 @@ function TableActionMenu({
editor.getEditorState().read(() => {
const selection = $getSelection();
// Merge cells
if (DEPRECATED_$isGridSelection(selection)) {
if ($isTableSelection(selection)) {
const currentSelectionCounts = computeSelectionCount(selection);
updateSelectionCounts(computeSelectionCount(selection));
setCanMergeCells(
isGridSelectionRectangular(selection) &&
isTableSelectionRectangular(selection) &&
(currentSelectionCounts.columns > 1 ||
currentSelectionCounts.rows > 1),
);
@@ -286,9 +284,9 @@ function TableActionMenu({
throw new Error('Expected to find tableElement in DOM');
}
const tableSelection = getTableSelectionFromTableElement(tableElement);
const tableSelection = getTableObserverFromTableElement(tableElement);
if (tableSelection !== null) {
tableSelection.clearHighlight();
tableSelection.$clearHighlight();
}
tableNode.markDirty();
@@ -303,13 +301,13 @@ function TableActionMenu({
const mergeTableCellsAtSelection = () => {
editor.update(() => {
const selection = $getSelection();
if (DEPRECATED_$isGridSelection(selection)) {
if ($isTableSelection(selection)) {
const {columns, rows} = computeSelectionCount(selection);
const nodes = selection.getNodes();
let firstCell: null | DEPRECATED_GridCellNode = null;
let firstCell: null | TableCellNode = null;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (DEPRECATED_$isGridCellNode(node)) {
if ($isTableCellNode(node)) {
if (firstCell === null) {
node.setColSpan(columns).setRowSpan(rows);
firstCell = node;
@@ -321,7 +319,7 @@ function TableActionMenu({
) {
firstChild.remove();
}
} else if (DEPRECATED_$isGridCellNode(firstCell)) {
} else if ($isTableCellNode(firstCell)) {
const isEmpty = $cellContainsEmptyParagraph(node);
if (!isEmpty) {
firstCell.append(...node.getChildren());
@@ -473,19 +471,19 @@ function TableActionMenu({
const selection = $getSelection();
if (
$isRangeSelection(selection) ||
DEPRECATED_$isGridSelection(selection)
$isTableSelection(selection)
) {
const [cell] = DEPRECATED_$getNodeTriplet(selection.anchor);
const [cell] = $getNodeTriplet(selection.anchor);
if ($isTableCellNode(cell)) {
cell.setBackgroundColor(value);
}
if (DEPRECATED_$isGridSelection(selection)) {
if ($isTableSelection(selection)) {
const nodes = selection.getNodes();
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (DEPRECATED_$isGridCellNode(node)) {
if ($isTableCellNode(node)) {
node.setBackgroundColor(value);
}
}
@@ -5,26 +5,26 @@
* LICENSE file in the root directory of this source tree.
*
*/
import type {Cell} from '@lexical/table';
import type {TableDOMCell} from '@lexical/table';
import type {LexicalEditor} from 'lexical';
import './index.css';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import useLexicalEditable from '@lexical/react/useLexicalEditable';
import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
import {
$getTableColumnIndexFromTableCellNode,
$getTableNodeFromLexicalNodeOrThrow,
$getTableRowIndexFromTableCellNode,
$isTableCellNode,
$isTableRowNode,
getCellFromTarget,
$isTableSelection,
getDOMCellFromTarget,
} from '@lexical/table';
import {
$getNearestNodeFromDOMNode,
$getSelection,
COMMAND_PRIORITY_HIGH,
DEPRECATED_$isGridSelection,
SELECTION_CHANGE_COMMAND,
} from 'lexical';
import * as React from 'react';
@@ -58,7 +58,7 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
const [mouseCurrentPos, updateMouseCurrentPos] =
useState<MousePosition | null>(null);
const [activeCell, updateActiveCell] = useState<Cell | null>(null);
const [activeCell, updateActiveCell] = useState<TableDOMCell | null>(null);
const [isSelectingGrid, updateIsSelectingGrid] = useState<boolean>(false);
const [draggingDirection, updateDraggingDirection] =
useState<MouseDraggingDirection | null>(null);
@@ -68,7 +68,7 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
SELECTION_CHANGE_COMMAND,
(payload) => {
const selection = $getSelection();
const isGridSelection = DEPRECATED_$isGridSelection(selection);
const isGridSelection = $isTableSelection(selection);
if (isSelectingGrid !== isGridSelection) {
updateIsSelectingGrid(isGridSelection);
@@ -107,7 +107,7 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
if (targetRef.current !== target) {
targetRef.current = target as HTMLElement;
const cell = getCellFromTarget(target as HTMLElement);
const cell = getDOMCellFromTarget(target as HTMLElement);
if (cell && activeCell !== cell) {
editor.update(() => {
@@ -5,14 +5,14 @@
* LICENSE file in the root directory of this source tree.
*
*/
import type {TableOfContentsEntry} from '@lexical/react/LexicalTableOfContents';
import type {TableOfContentsEntry} from '@lexical/react/LexicalTableOfContentsPlugin';
import type {HeadingTagType} from '@lexical/rich-text';
import type {NodeKey} from 'lexical';
import './index.css';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents';
import {TableOfContentsPlugin} from '@lexical/react/LexicalTableOfContentsPlugin';
import {useEffect, useRef, useState} from 'react';
import { Collapsible, Button } from '@douyinfe/semi-ui';
@@ -243,12 +243,12 @@ function TableOfContentsList({
);
}
export default function TableOfContentsPlugin() {
export default function TableOfContentsPluginWrapper() {
return (
<LexicalTableOfContents>
<TableOfContentsPlugin>
{(tableOfContents) => {
return <TableOfContentsList tableOfContents={tableOfContents} />;
}}
</LexicalTableOfContents>
</TableOfContentsPlugin>
);
}
@@ -51,7 +51,6 @@ import {
$getNodeByKey,
$getRoot,
$getSelection,
$INTERNAL_isPointSelection,
$isElementNode,
$isRangeSelection,
$isRootOrShadowRoot,
@@ -213,7 +212,7 @@ function BlockFormatDropDown({
const formatParagraph = () => {
editor.update(() => {
const selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
$setBlocksType(selection, () => $createParagraphNode());
}
});
@@ -223,7 +222,7 @@ function BlockFormatDropDown({
if (blockType !== headingSize) {
editor.update(() => {
const selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
$setBlocksType(selection, () => $createHeadingNode(headingSize));
}
});
@@ -258,7 +257,7 @@ function BlockFormatDropDown({
if (blockType !== 'quote') {
editor.update(() => {
const selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
$setBlocksType(selection, () => $createQuoteNode());
}
});
@@ -270,7 +269,7 @@ function BlockFormatDropDown({
editor.update(() => {
let selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
if (selection.isCollapsed()) {
$setBlocksType(selection, () => $createCodeNode());
} else {
@@ -370,7 +369,7 @@ function FontDropDown({
(option: string) => {
editor.update(() => {
const selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
$patchStyleText(selection, {
[style]: option,
});
@@ -677,7 +676,7 @@ export default function ToolbarPlugin({
activeEditor.registerUpdateListener(({editorState}) => {
editorState.read(() => {
$updateToolbar();
});
}, {editor: activeEditor});
}),
activeEditor.registerCommand<boolean>(
CAN_UNDO_COMMAND,
@@ -727,7 +726,7 @@ export default function ToolbarPlugin({
(styles: Record<string, string>) => {
activeEditor.update(() => {
const selection = $getSelection();
if ($INTERNAL_isPointSelection(selection)) {
if ($isRangeSelection(selection)) {
$patchStyleText(selection, styles);
}
});
@@ -25,28 +25,27 @@ export default function NoteEditor() {
return (
<>
<Breadcrumb
style={{ marginBottom: '24px' }}
style={{ marginBottom: '16px', paddingTop: '10px' }}
routes={['CyyNote', '笔记标题', currentTitle]}
/>
<div
style={{
borderRadius: '10px',
border: '1px solid var(--semi-color-border)',
padding: '15px',
}}
<Skeleton
placeholder={<Skeleton.Paragraph rows={2} />}
loading={isNoteLoading}
>
<Skeleton
placeholder={<Skeleton.Paragraph rows={2} />}
loading={isNoteLoading}
<Input
placeholder="笔记标题"
size="large"
value={currentTitle}
onChange={handleTitleChange}
style={{ marginBottom: '12px' }}
/>
<div
style={{
borderRadius: '10px',
border: '1px solid var(--semi-color-border)',
overflow: 'clip',
}}
>
<Input
placeholder="笔记标题"
size="large"
value={currentTitle}
onChange={handleTitleChange}
/>
<br />
<br />
{currentNoteId !== null && currentContent !== null ? (
<PlaygroundApp
noteId={currentNoteId}
@@ -55,10 +54,10 @@ export default function NoteEditor() {
handleLawClick={handleRefreshTree}
/>
) : (
<div style={{ color: 'var(--semi-color-text-2)' }}></div>
<div style={{ padding: '15px', color: 'var(--semi-color-text-2)' }}></div>
)}
</Skeleton>
</div>
</div>
</Skeleton>
</>
);
}
@@ -189,7 +189,7 @@ export default function NoteTree({ onRefreshTree, onNavItemClick, onSearch }: No
searchPlaceholder=" "
showFilteredOnly
directory
virtualize
virtualize={{ itemSize: 28 }}
value={currentNoteId === null ? undefined : String(currentNoteId)}
treeData={treeData as unknown as Parameters<typeof Tree>[0]['treeData']}
renderLabel={renderLabel as Parameters<typeof Tree>[0]['renderLabel']}
+3 -6
View File
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router';
@@ -6,10 +5,8 @@ import './index.css';
import App from './App';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
<BrowserRouter>
<App />
</BrowserRouter>,
);
+8 -8
View File
@@ -131,8 +131,8 @@ export default function NotePage() {
const siderWidth = siderFlag ? '265px' : '0px';
return (
<Layout style={{ border: '1px solid var(--semi-color-border)' }}>
<Header style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
<Layout style={{ border: '1px solid var(--semi-color-border)', height: '100vh', overflow: 'hidden' }}>
<Header style={{ backgroundColor: 'var(--semi-color-bg-1)', flexShrink: 0 }}>
<Nav mode="horizontal" defaultSelectedKeys={['Home']}>
<Nav.Header>
<IconDescriptions style={{ height: '36px', fontSize: 36 }} />
@@ -181,9 +181,9 @@ export default function NotePage() {
</Nav>
</Header>
<Layout>
<Layout style={{ flex: 1, minHeight: 0 }}>
<Sider
style={{ width: siderWidth, backgroundColor: 'var(--semi-color-bg-1)' }}
style={{ width: siderWidth, backgroundColor: 'var(--semi-color-bg-1)', overflowY: 'auto' }}
breakpoint={['md']}
onBreakpoint={handleBreakpoint}
>
@@ -207,13 +207,13 @@ export default function NotePage() {
{/* Main content area */}
{activeView === 'note' && (
<Content style={{ padding: '10px', backgroundColor: 'var(--semi-color-bg-0)' }}>
<Content style={{ padding: '0 10px 10px', backgroundColor: 'var(--semi-color-bg-0)', overflowY: 'auto' }}>
<NoteEditor />
</Content>
)}
{activeView === 'home' && (
<Content style={{ padding: '10px', backgroundColor: 'var(--semi-color-bg-0)' }}>
<Content style={{ padding: '10px', backgroundColor: 'var(--semi-color-bg-0)', overflowY: 'auto' }}>
<Breadcrumb style={{ marginBottom: 24 }} routes={['首页']} />
<div
style={{
@@ -228,7 +228,7 @@ export default function NotePage() {
)}
{activeView === 'settings' && (
<Content style={{ padding: '10px', backgroundColor: 'var(--semi-color-bg-0)' }}>
<Content style={{ padding: '10px', backgroundColor: 'var(--semi-color-bg-0)', overflowY: 'auto' }}>
<Breadcrumb style={{ marginBottom: 24 }} routes={['设置']} />
<div
style={{
@@ -284,7 +284,7 @@ export default function NotePage() {
)}
{activeView === 'search' && (
<Content style={{ padding: 24, backgroundColor: 'var(--semi-color-bg-0)' }}>
<Content style={{ padding: 24, backgroundColor: 'var(--semi-color-bg-0)', overflowY: 'auto' }}>
<Breadcrumb style={{ marginBottom: 24 }} routes={['搜索结果']} />
<div
style={{
+1 -1
View File
@@ -16,7 +16,7 @@
"jsx": "react-jsx",
/* Path aliases */
"ignoreDeprecations": "6.0",
"ignoreDeprecations": "5.0",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
+3
View File
@@ -4,6 +4,9 @@ import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig(({ command }) => ({
plugins: [react()],
define: {
'process.env': {},
},
resolve: {
// new URL is pure ESM — no __dirname needed, no @types/node required
alias: { '@': new URL('./src', import.meta.url).pathname },
File diff suppressed because it is too large Load Diff