From 517a14333a3e731190fb47a54ddd78f19253c498 Mon Sep 17 00:00:00 2001 From: Guilherme_F Date: Thu, 25 Apr 2024 18:09:51 +0100 Subject: [PATCH] Fixed traverseSelection & History Updates --- code/client/package.json | 16 ++-- code/client/src/editor/crdt/fugue.ts | 45 ++++++---- .../domain/document/history/operations.ts | 4 +- code/client/src/main.tsx | 1 - code/client/src/pwa/inject-config.ts | 12 +-- code/client/src/pwa/manifest-config.ts | 90 +++++++++---------- code/client/src/pwa/pwa-config.ts | 40 ++++----- code/client/src/pwa/sw.ts | 16 ++-- code/client/tests/editor/crdt/fugue.test.ts | 12 +-- code/client/tsconfig.json | 3 +- code/client/tsconfig.node.json | 1 + 11 files changed, 123 insertions(+), 117 deletions(-) diff --git a/code/client/package.json b/code/client/package.json index b344330c..4edba74f 100644 --- a/code/client/package.json +++ b/code/client/package.json @@ -25,8 +25,8 @@ "eslint-plugin-playwright": "^1.6.0", "lodash": "^4.17.21", "msw": "^2.2.14", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", "react-icons": "^5.1.0", "react-router-dom": "^6.23.0", "slate": "^0.103.0", @@ -47,14 +47,14 @@ "@typescript-eslint/parser": "^7.7.1", "@vite-pwa/assets-generator": "^0.2.4", "@vitejs/plugin-react": "^4.2.1", - "@vitest/coverage-v8": "^1.5.1", - "@vitest/ui": "^1.5.1", + "@vitest/coverage-v8": "^1.5.2", + "@vitest/ui": "^1.5.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^4.6.1", "eslint-plugin-react-refresh": "^0.4.6", "jsdom": "^24.0.0", "knip": "^5.10.0", @@ -62,12 +62,8 @@ "sass": "^1.75.0", "typescript": "^5.4.5", "vite": "^5.2.10", - "vite-plugin-pwa": "^0.19.8", "vite-plugin-qrcode": "^0.2.3", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.5.1", - "workbox-build": "^7.1.0", - "workbox-cli": "^7.1.0", - "workbox-precaching": "^7.1.0" + "vitest": "^1.5.2" } } diff --git a/code/client/src/editor/crdt/fugue.ts b/code/client/src/editor/crdt/fugue.ts index e97a402c..a4235633 100644 --- a/code/client/src/editor/crdt/fugue.ts +++ b/code/client/src/editor/crdt/fugue.ts @@ -15,6 +15,7 @@ import { type TraverseOptions = { returnDeleted?: boolean; + returnLineBreaks?: boolean; }; /** @@ -45,8 +46,7 @@ export class Fugue { * @param values */ insertLocal(cursor: Cursor, ...values: NodeInsert[] | string[]): InsertOperation[] { - let line = cursor.line; - let column = cursor.column; + let { line, column } = cursor; return values.map(value => { const node = typeof value === 'string' ? nodeInsert(value, []) : value; const operation = this.getInsertOperation({ line, column }, node); @@ -100,9 +100,10 @@ export class Fugue { /** * Relives the nodes from the given start index and given length. * @param selection + * @param options */ - reviveLocal(selection: Selection): ReviveOperation[] { - const nodes = Array.from(this.traverseBySelection(selection, { returnDeleted: true })); + reviveLocal(selection: Selection, options?: TraverseOptions): ReviveOperation[] { + const nodes = Array.from(this.traverseBySelection(selection, { ...options, returnDeleted: true })); return nodes.map(node => this.reviveNode(node.id)); } @@ -248,15 +249,15 @@ export class Fugue { */ *traverseBySelection(selection: Selection, options?: TraverseOptions): IterableIterator { const { start, end } = selection; - const { returnDeleted } = options || { returnDeleted: false }; - let lineCounter = 0; - let columnCounter = 0; - let inBounds = false; - let finished = false; + const { returnDeleted, returnLineBreaks } = { + returnDeleted: options?.returnDeleted || false, + returnLineBreaks: options?.returnLineBreaks || false, + }; + let lineCounter = 0, + columnCounter = 0; + let inBounds = false, + finished = false; for (const node of this.traverseTree(returnDeleted)) { - if (finished) break; - - // update counters if (node.value === '\n') { lineCounter++; columnCounter = 0; @@ -268,16 +269,24 @@ export class Fugue { } // yield node if in bounds - if (inBounds) { + if (inBounds && (returnLineBreaks || (!returnLineBreaks && node.value !== '\n'))) { yield node; } - // end condition - if (lineCounter === end.line && columnCounter === end.column) { - finished = true; - } + finished = + lineCounter === end.line && + columnCounter === end.column && + ((!returnLineBreaks && node.value !== '\n') || returnLineBreaks); + + if (finished) break; - columnCounter++; + // increment column counter + if (node.value !== '\n') { + columnCounter++; + } else if (returnLineBreaks) { + // don't ignore line breaks + columnCounter++; + } } } diff --git a/code/client/src/editor/domain/document/history/operations.ts b/code/client/src/editor/domain/document/history/operations.ts index 0913fdd8..da5e70be 100644 --- a/code/client/src/editor/domain/document/history/operations.ts +++ b/code/client/src/editor/domain/document/history/operations.ts @@ -103,7 +103,7 @@ export default (fugue: Fugue, communication: Communication): HistoryDomainOperat * @param cursor */ function splitNode({ cursor }: SplitNodeOperation) { - return fugue.reviveLocal({ start: cursor, end: cursor }); + return fugue.reviveLocal({ start: cursor, end: cursor }, { returnLineBreaks: true }); } /** @@ -121,7 +121,7 @@ export default (fugue: Fugue, communication: Communication): HistoryDomainOperat * @param set_mode */ function setNode({ selection, properties }: SetNodeOperation | UnsetNodeOperation, set_mode: boolean) { - const type = Object.keys(properties)[0]; + const type = properties.type; const styleType = getStyleType(type); return styleType === 'block' diff --git a/code/client/src/main.tsx b/code/client/src/main.tsx index 03ba0482..38c24575 100644 --- a/code/client/src/main.tsx +++ b/code/client/src/main.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; diff --git a/code/client/src/pwa/inject-config.ts b/code/client/src/pwa/inject-config.ts index 71cf901b..e9d59b82 100644 --- a/code/client/src/pwa/inject-config.ts +++ b/code/client/src/pwa/inject-config.ts @@ -1,6 +1,6 @@ -import { CustomInjectManifestOptions } from 'vite-plugin-pwa'; - -export const injectConfig: Partial = { - minify: false, - enableWorkboxModulesLogs: true, -}; +// import { CustomInjectManifestOptions } from 'vite-plugin-pwa'; +// +// export const injectConfig: Partial = { +// minify: false, +// enableWorkboxModulesLogs: true, +// }; diff --git a/code/client/src/pwa/manifest-config.ts b/code/client/src/pwa/manifest-config.ts index b8ca5d1b..03809e63 100644 --- a/code/client/src/pwa/manifest-config.ts +++ b/code/client/src/pwa/manifest-config.ts @@ -1,45 +1,45 @@ -import { ManifestOptions } from 'vite-plugin-pwa'; - -export const manifestConfig: Partial = { - name: 'NoteSpace', - short_name: 'NoteSpace', - start_url: '/', - scope: '.', - display: 'standalone', - background_color: '#ffffff', - theme_color: '#ffffff', - orientation: 'portrait-primary', - icons: [ - { - src: '/android-chrome-192x192.png', - sizes: '192x192', - type: 'image/png', - purpose: 'any maskable', - }, - { - src: '/android-chrome-512x512.png', - sizes: '512x512', - type: 'image/png', - purpose: 'any maskable', - }, - /* favicon */ - { - src: '/favicon-16x16.png', - sizes: '16x16', - type: 'image/png', - purpose: 'any maskable', - }, - { - src: '/favicon-32x32.png', - sizes: '32x32', - type: 'image/png', - purpose: 'any maskable', - }, - { - src: '/apple-touch-icon.png', - sizes: '180x180', - type: 'image/png', - purpose: 'any maskable', - }, - ], -}; +// import { ManifestOptions } from 'vite-plugin-pwa'; +// +// export const manifestConfig: Partial = { +// name: 'NoteSpace', +// short_name: 'NoteSpace', +// start_url: '/', +// scope: '.', +// display: 'standalone', +// background_color: '#ffffff', +// theme_color: '#ffffff', +// orientation: 'portrait-primary', +// icons: [ +// { +// src: '/android-chrome-192x192.png', +// sizes: '192x192', +// type: 'image/png', +// purpose: 'any maskable', +// }, +// { +// src: '/android-chrome-512x512.png', +// sizes: '512x512', +// type: 'image/png', +// purpose: 'any maskable', +// }, +// /* favicon */ +// { +// src: '/favicon-16x16.png', +// sizes: '16x16', +// type: 'image/png', +// purpose: 'any maskable', +// }, +// { +// src: '/favicon-32x32.png', +// sizes: '32x32', +// type: 'image/png', +// purpose: 'any maskable', +// }, +// { +// src: '/apple-touch-icon.png', +// sizes: '180x180', +// type: 'image/png', +// purpose: 'any maskable', +// }, +// ], +// }; diff --git a/code/client/src/pwa/pwa-config.ts b/code/client/src/pwa/pwa-config.ts index 892b03ef..2450c7ce 100644 --- a/code/client/src/pwa/pwa-config.ts +++ b/code/client/src/pwa/pwa-config.ts @@ -1,20 +1,20 @@ -import { injectConfig } from './inject-config.ts'; -import { manifestConfig } from './manifest-config.ts'; -import { VitePWAOptions } from 'vite-plugin-pwa'; - -export const pwaConfig: Partial = { - mode: 'development', - base: '/', - strategies: 'injectManifest', - srcDir: './src/pwa', - filename: 'sw.ts', - //injectRegister: false, - injectManifest: injectConfig, - registerType: 'autoUpdate', - manifest: manifestConfig, - devOptions: { - enabled: true, - navigateFallback: '../../index.html', - type: 'module', - }, -}; +// import { injectConfig } from './inject-config.ts'; +// import { manifestConfig } from './manifest-config.ts'; +// import { VitePWAOptions } from 'vite-plugin-pwa'; +// +// export const pwaConfig: Partial = { +// mode: 'development', +// base: '/', +// strategies: 'injectManifest', +// srcDir: './src/pwa', +// filename: 'sw.ts', +// //injectRegister: false, +// injectManifest: injectConfig, +// registerType: 'autoUpdate', +// manifest: manifestConfig, +// devOptions: { +// enabled: true, +// navigateFallback: '../../index.html', +// type: 'module', +// }, +// }; diff --git a/code/client/src/pwa/sw.ts b/code/client/src/pwa/sw.ts index 4594fdcd..0fa5c2d9 100644 --- a/code/client/src/pwa/sw.ts +++ b/code/client/src/pwa/sw.ts @@ -1,8 +1,8 @@ -import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'; - -declare let self: ServiceWorkerGlobalScope; - -precacheAndRoute(self.__WB_MANIFEST); - -// this will clean up old caches that are not needed anymore -cleanupOutdatedCaches(); +// import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'; +// +// declare let self: ServiceWorkerGlobalScope; +// +// precacheAndRoute(self.__WB_MANIFEST); +// +// // this will clean up old caches that are not needed anymore +// cleanupOutdatedCaches(); diff --git a/code/client/tests/editor/crdt/fugue.test.ts b/code/client/tests/editor/crdt/fugue.test.ts index 16b34c39..2a30d2ec 100644 --- a/code/client/tests/editor/crdt/fugue.test.ts +++ b/code/client/tests/editor/crdt/fugue.test.ts @@ -175,8 +175,8 @@ describe('Fugue', () => { const nodes = Array.from(fugue.traverseBySelection(selection)); // then - expect(nodes).toHaveLength(2); - expect(nodes.map(node => node.value).join('')).toEqual('ab'); + expect(nodes).toHaveLength(3); + expect(nodes.map(node => node.value).join('')).toEqual('abc'); }); it('should return the nodes in the given selections', () => { @@ -184,9 +184,9 @@ describe('Fugue', () => { const cursor: Cursor = { line: 0, column: 0 }; const line1 = 'abcdef'; const line2 = 'ghijkl'; - const selection1: Selection = { start: { line: 0, column: 1 }, end: { line: 0, column: 3 } }; - const selection2: Selection = { start: { line: 0, column: 3 }, end: { line: 0, column: 5 } }; - const selection3: Selection = { start: { line: 0, column: 3 }, end: { line: 1, column: 4 } }; + const selection1: Selection = { start: { line: 0, column: 1 }, end: { line: 0, column: 2 } }; + const selection2: Selection = { start: { line: 0, column: 3 }, end: { line: 0, column: 4 } }; + const selection3: Selection = { start: { line: 0, column: 3 }, end: { line: 1, column: 3 } }; // when fugue.insertLocal(cursor, ...line1.split('')); @@ -200,7 +200,7 @@ describe('Fugue', () => { // then expect(nodes1.map(node => node.value).join('')).toEqual('bc'); expect(nodes2.map(node => node.value).join('')).toEqual('de'); - expect(nodes3.map(node => node.value).join('')).toEqual('def\nghij'); + expect(nodes3.map(node => node.value).join('')).toEqual('defghij'); }); it('should return all nodes until the given separator', () => { diff --git a/code/client/tsconfig.json b/code/client/tsconfig.json index eb47869b..09b5a85d 100644 --- a/code/client/tsconfig.json +++ b/code/client/tsconfig.json @@ -1,12 +1,13 @@ { "compilerOptions": { - "target": "ESNext", + "target": "ES2022", "useDefineForClassFields": true, "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": false, "skipLibCheck": false, "esModuleInterop": false, "allowSyntheticDefaultImports": true, + "allowImportingTsExtensions": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", diff --git a/code/client/tsconfig.node.json b/code/client/tsconfig.node.json index b2c981eb..8501422e 100644 --- a/code/client/tsconfig.node.json +++ b/code/client/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "target": "es2022", "composite": true, "module": "esnext", "moduleResolution": "nodenext",