Skip to content

Commit

Permalink
Editor Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
GuilhermeF03 committed Mar 22, 2024
1 parent 8cd99ce commit e300239
Show file tree
Hide file tree
Showing 28 changed files with 271 additions and 196 deletions.
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
prefix: 'deps(server):'
labels:
- dependencies

- package-ecosystem: npm
directory: /code/shared
schedule:
Expand All @@ -24,6 +25,7 @@
prefix: 'deps(shared):'
labels:
- dependencies

- package-ecosystem: github-actions
directory: /
schedule:
Expand Down
2 changes: 1 addition & 1 deletion code/client/dev-dist/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ define(['./workbox-a0f72815'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.c6jns41hh88"
"revision": "0.5aj285mkmh8"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
Expand Down
4 changes: 2 additions & 2 deletions code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"serve": "vite preview"
},
"dependencies": {
"@notespace/shared": "file:..\\shared",
"@notespace/shared": "link:..\\shared",
"eslint-plugin-playwright": "^1.5.4",
"lodash": "^4.17.21",
"react": "^18.2.0",
Expand Down Expand Up @@ -51,7 +51,7 @@
"prettier": "^3.2.5",
"sass": "^1.72.0",
"typescript": "^5.4.3",
"vite": "^5.2.2",
"vite": "^5.2.3",
"vite-plugin-pwa": "^0.19.6",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.4.0"
Expand Down
107 changes: 63 additions & 44 deletions code/client/src/editor/crdt/fugue.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { type DeleteOperation, type InsertOperation, type StyleOperation } from '@notespace/shared/crdt/operations';
import { type Node, type Id } from '@notespace/shared/crdt/types';
import { type Style } from '@notespace/shared/crdt/styles';
import { type DeleteOperation, type InsertOperation, type StyleOperation } from '@notespace/shared/crdt/types/operations';
import { type Node, type Id } from '@notespace/shared/crdt/types/nodes';
import { type Style } from '@notespace/shared/crdt/types/styles';
import { FugueTree } from '@notespace/shared/crdt/FugueTree';
import { generateReplicaId } from './utils';
import { socket } from '@src/socket/socket';
import { type InsertNode } from '@editor/crdt/types';
import {Cursor, Selection} from '@editor/slate/model/cursor'
import { isEmpty } from 'lodash';

/**
Expand Down Expand Up @@ -36,35 +37,38 @@ export class Fugue {
* @param start
* @param values
*/
insertLocal(start: number, ...values: InsertNode[]): InsertOperation[] {
insertLocal({start}: Selection, ...values: InsertNode[]): InsertOperation[] {
return values.map((value, i) => {
const msg = this.insertOne(start + i, value);
this.addNode(msg);
socket.emit('operation', msg); // FIXME: break data into data chunks - less network traffic
return msg;
const operation = this.insertOne({...start, column: start.column + i}, value);
this.addNode(operation);
socket.emit('operation', operation); // FIXME: break data into data chunks - less network traffic
return operation;
});
}

/**
* Inserts a new node in the tree based on the given message.
* @param message - the insert message
* Inserts a new node in the tree based on the given operation.
* @param operation - the insert operation
*/
insertRemote(message: InsertOperation): void {
this.addNode(message);
insertRemote(operation: InsertOperation): void {
this.addNode(operation);
}

/**
* Inserts a new node in the tree based on the given message.
* Inserts a new node in the tree based on the given operation.
* @param start - the index where the new node should be inserted
* @param value - the value of the new node
* @param styles
* @private
* @returns the insert message
* @returns the insert operation
*/
private insertOne(start: number, { value, styles }: InsertNode): InsertOperation {
private insertOne({ line, column } : Cursor, { value, styles }: InsertNode): InsertOperation {
const id = { sender: this.replicaId, counter: this.counter++ };
const leftOrigin = start === 0 ? this.tree.root : this.tree.getByIndex(this.tree.root, start - 1);
// leftOrigin has no right children, so we add the new node as a right child

const root = this.findNode('\n', line) || this.tree.root;

const leftOrigin = column === 0 ? root : this.tree.getByIndex(root, column - 1);

if (isEmpty(leftOrigin.rightChildren)) {
return {
type: 'insert',
Expand All @@ -75,16 +79,14 @@ export class Fugue {
styles,
};
}
// Otherwise, the new node is added as a left child of rightOrigin, which
// is the next node after leftOrigin *including tombstones*.
// In this case, rightOrigin is the leftmost descendant of leftOrigin's
// first right child.

const rightOrigin = this.tree.getLeftmostDescendant(leftOrigin.rightChildren[0]);
return { type: 'insert', id, value, parent: rightOrigin.id, side: 'L' };
}


/**
* Inserts a new node in the tree based on the given message.
* Inserts a new node in the tree based on the given operation.
* @param id
* @param value
* @param parent
Expand All @@ -100,45 +102,50 @@ export class Fugue {
* @param start
* @param end (exclusive)
*/
deleteLocal(start: number, end: number): void {
const deleteElement = (index: number) => {
const msg = this.deleteOne(index);
deleteLocal({start, end} : Selection): void {
const deleteElement = (id : Id) => {
const msg = this.deleteOne(id);
this.deleteNode(msg);
socket.emit('operation', msg); // FIXME: this should be done only once after all the deletes - less network traffic
};
if (start === end) {
deleteElement(end - 1);
return;

const startRoot = this.findNode('\n', start.line);
const startNode = this.tree.getByIndex(startRoot, start.column);

const endRoot = this.findNode('\n', end.line);
const endNode = this.tree.getByIndex(endRoot, end.column);

for (const node of this.tree.traverse(startNode)){
if (node === endNode) break;
deleteElement(node.id);
}
for (let i = end - 1; i >= start; i--) deleteElement(i);
}

/**
* Deletes the node based on the given message.
* @param message
* Deletes the node based on the given operation.
* @param operation
*/
deleteRemote(message: DeleteOperation): void {
this.deleteNode(message);
deleteRemote(operation: DeleteOperation): void {
this.deleteNode(operation);
}

/**
* Deletes the node at the given index.
* Returns the delete operation
* @param index
* @private
* @returns the delete message
* @returns the delete operation
*/
private deleteOne(index: number): DeleteOperation {
const node = this.tree.getByIndex(this.tree.root, index);
return { type: 'delete', id: node.id };
private deleteOne(id : Id): DeleteOperation {
return { type: 'delete', id };
}

/**
* Deletes the node based on the given message.
* @param message
* Deletes the node based on the given operation.
* @param operation
* @private
*/
private deleteNode(message: DeleteOperation): void {
this.tree.deleteNode(message.id);
private deleteNode({id}: DeleteOperation): void {
this.tree.deleteNode(id);
}

updateStyleLocal(start: number, end: number, value: boolean, format: string) {
Expand All @@ -165,15 +172,27 @@ export class Fugue {
/**
* Makes a full traversal of the tree.
*/
fullTraverse = () => this.tree.traverse(this.tree.root);
traverseTree = () => this.tree.traverse(this.tree.root);

findNode(value: string, skip: number): Node<string> {
let lastMatch: Node<string> = this.tree.root
for (const node of this.traverseTree()){
if(node.value === value && !node.isDeleted) {
lastMatch = node
if (--skip === 0) return lastMatch
}
}
return lastMatch
}


/**
* Returns the string representation of the tree.
* @returns the string representation of the tree.
*/
toString(): string {
const values: string[] = [];
for (const node of this.fullTraverse()) {
for (const node of this.traverseTree()) {
values.push(node.value!);
}
return values.join('');
Expand Down
4 changes: 2 additions & 2 deletions code/client/src/editor/crdt/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Style } from '@notespace/shared/crdt/styles';
import { type Style } from '@notespace/shared/crdt/types/styles';

export interface InsertNode {
export type InsertNode = {
value: string;
styles: Style[];
}
4 changes: 2 additions & 2 deletions code/client/src/editor/crdt/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _, { range } from 'lodash';
import type { Style } from '@notespace/shared/crdt/styles.ts';
import type { InsertNode } from '@editor/crdt/types.ts';
import type { Style } from '@notespace/shared/crdt/types/styles';
import type { InsertNode } from '@editor/crdt/types';

const BASE64CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const DEFAULT_REPLICA_ID_LENGTH = 10;
Expand Down
4 changes: 2 additions & 2 deletions code/client/src/editor/hooks/useEvents.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import useSocketListeners from '@src/socket/useSocketListeners';
import { type Fugue } from '@src/editor/crdt/fugue';
import { type Node } from '@notespace/shared/crdt/types';
import { type Operation } from '@notespace/shared/crdt/operations';
import { type Node } from '@notespace/shared/crdt/types/nodes';
import { type Operation } from '@notespace/shared/crdt/types/operations';

function useEvents(fugue: Fugue, onDone: () => void) {
function onOperation(operation: Operation) {
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/editor/hooks/useFugue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { Fugue } from '../crdt/fugue.ts';
import { Fugue } from '@editor/crdt/fugue';

/**
* A hook that returns a new replica of a FugueTree, as a FugueReplica.
Expand Down
65 changes: 0 additions & 65 deletions code/client/src/editor/hooks/useInputHandlers.ts

This file was deleted.

30 changes: 18 additions & 12 deletions code/client/src/editor/slate/SlateEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Editable, Slate, withReact } from 'slate-react';
import useInputHandlers from '@editor/hooks/useInputHandlers.ts';
import useFugue from '@editor/hooks/useFugue.ts';
import useEvents from '@editor/hooks/useEvents.ts';
import useRenderers from '@editor/slate/hooks/useRenderers.tsx';
import useInputHandlers from '@editor/slate/hooks/useInputHandlers';
import useFugue from '@editor/hooks/useFugue';
import useEvents from '@editor/hooks/useEvents';
import useRenderers from '@editor/slate/hooks/useRenderers';
import './SlateEditor.scss';
import Toolbar from '@editor/slate/toolbar/Toolbar.tsx';
import Toolbar from '@editor/slate/toolbar/Toolbar';
import { withHistory } from 'slate-history';
import useEditor from '@editor/slate/hooks/useEditor.ts';
import { withMarkdown } from '@editor/slate/plugins/markdown/withMarkdown.ts';
import { withNormalize } from '@editor/slate/plugins/normalize/withNormalize.ts';
import { toSlate } from '@editor/slate/utils.ts';
import useEditor from '@editor/slate/hooks/useEditor';
import { withMarkdown } from '@editor/slate/plugins/markdown/withMarkdown';
import { withNormalize } from './plugins/normalize/withNormalize';
import { toSlate } from '@editor/slate/utils';

const initialValue = [
{
type: 'paragraph',
children: [{ text: '' }],
},
]

function SlateEditor() {
const editor = useEditor(withHistory, withReact, withMarkdown, withNormalize);
Expand All @@ -18,8 +25,7 @@ function SlateEditor() {
const { renderElement, renderLeaf } = useRenderers();

useEvents(fugue, () => {
// force re-render of the editor with new text
editor.children = toSlate(fugue.fullTraverse)
editor.children = toSlate(fugue.traverseTree)
editor.onChange();
});

Expand All @@ -30,7 +36,7 @@ function SlateEditor() {
<h1>NoteSpace</h1>
</header>
<div className="container">
<Slate editor={editor} initialValue={[]}>
<Slate editor={editor} initialValue={initialValue}>
<Toolbar fugue={fugue} />
<Editable
renderElement={renderElement}
Expand Down
Loading

0 comments on commit e300239

Please sign in to comment.