Skip to content

Commit 6ab990e

Browse files
authored
Merge pull request #735 from streamich/peritext-high-level-api
Peritext high-level API
2 parents 178a4be + a5553af commit 6ab990e

20 files changed

+2792
-284
lines changed

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"test:cli:patch": "./bin/json-patch-test.js ./bin/json-patch.js",
9595
"test:cli:pack": "./bin/json-pack-test.js ./bin/json-pack.js",
9696
"demo:json-patch": "npx ts-node src/json-patch/__demos__/json-patch.ts",
97+
"demo:ui:peritext": "webpack serve --config ./src/json-crdt-peritext-ui/__demos__/webpack.config.js",
9798
"bench:json-crdt:traces:crdt-libs": "cd src/json-crdt/__bench__ && yarn && yarn bench:traces:crdt-libs",
9899
"bench:json-crdt:traces:non-crdt-libs": "cd src/json-crdt/__bench__ && yarn && yarn bench:traces:non-crdt-libs",
99100
"bench:json-crdt:concurrent-traces": "cd src/json-crdt/__bench__ && yarn && yarn bench:concurrent-traces",
@@ -138,21 +139,31 @@
138139
"devDependencies": {
139140
"@types/benchmark": "^2.1.5",
140141
"@types/jest": "^29.5.12",
142+
"@types/react": "^18.3.11",
143+
"@types/react-dom": "^18.3.0",
141144
"benchmark": "^2.1.4",
142145
"config-housekeeping": "https://github.com/streamich/housekeeping#3532d2abeac159315ddf403d70517859d079c801",
143146
"editing-traces": "https://github.com/streamich/editing-traces#6494020428530a6e382378b98d1d7e31334e2d7b",
144147
"fast-json-patch": "^3.1.1",
148+
"html-webpack-plugin": "^5.6.0",
145149
"jest": "^29.7.0",
146150
"json-crdt-traces": "https://github.com/streamich/json-crdt-traces#ec825401dc05cbb74b9e0b3c4d6527399f54d54d",
147151
"json-logic-js": "^2.0.2",
152+
"nano-theme": "^1.4.3",
148153
"quill-delta": "^5.1.0",
154+
"react": "^18.3.1",
155+
"react-dom": "^18.3.1",
149156
"rxjs": "^7.8.1",
150157
"ts-jest": "^29.1.2",
158+
"ts-loader": "^9.5.1",
151159
"ts-node": "^10.9.2",
152160
"tslib": "^2.6.2",
153161
"tslint": "^6.1.3",
154162
"tslint-config-common": "^1.6.2",
155163
"typescript": "^5.4.5",
164+
"webpack": "^5.95.0",
165+
"webpack-cli": "^5.1.4",
166+
"webpack-dev-server": "^5.1.0",
156167
"yjs": "^13.6.18"
157168
},
158169
"jest": {

src/json-crdt-extensions/peritext/Peritext.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ export class Peritext<T = string> implements Printable {
207207
return Range.at(this.str, start, length);
208208
}
209209

210+
/**
211+
* Creates selection of relative start and end of the whole document.
212+
*
213+
* @returns Range, which selects the whole document, if any.
214+
*/
210215
public rangeAll(): Range<T> | undefined {
211216
const start = this.pointStart();
212217
const end = this.pointEnd();

src/json-crdt-extensions/peritext/__tests__/Peritext.cursor.spec.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {InlineAttrPos} from '../block/Inline';
2-
import {CursorAnchor, SliceTypes} from '../slice/constants';
1+
import {InlineAttrStartPoint, InlineAttrContained} from '../block/Inline';
2+
import {SliceTypes} from '../slice/constants';
33
import {setupKit} from './setup';
44

55
const setup = () => {
@@ -24,10 +24,8 @@ test('cursor at the start of string and slice annotation at the start of string'
2424
expect(inline1.text()).toBe('');
2525
expect(inline2.text()).toBe('a');
2626
expect(inline3.text()).toBe('b');
27-
expect(inline1.attr()).toEqual({
28-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
29-
});
30-
expect(inline2.attr()).toEqual({bold: [1, InlineAttrPos.Contained]});
27+
expect(inline1.attr()[SliceTypes.Cursor][0]).toBeInstanceOf(InlineAttrStartPoint);
28+
expect(inline2.attr().bold[0]).toBeInstanceOf(InlineAttrContained);
3129
expect(inline3.attr()).toEqual({});
3230
});
3331

@@ -49,10 +47,12 @@ test('cursor walking over character marked as bold', () => {
4947
expect(inline1.text()).toBe('');
5048
expect(inline2.text()).toBe('a');
5149
expect(inline3.text()).toBe('b');
52-
expect(inline2.attr()).toEqual({bold: [[void 0], InlineAttrPos.Contained]});
53-
expect(inline3.attr()).toEqual({
54-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
55-
});
50+
expect(inline2.attr().bold[0]).toBeInstanceOf(InlineAttrContained);
51+
expect(inline3.attr()[SliceTypes.Cursor][0]).toBeInstanceOf(InlineAttrStartPoint);
52+
// expect(inline2.attr()).toEqual({bold: [[void 0], InlineAttrPos.Contained]});
53+
// expect(inline3.attr()).toEqual({
54+
// [SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
55+
// });
5656
editor.cursor.move(1);
5757
peritext.refresh();
5858
});
@@ -75,11 +75,13 @@ test('cursor walking over character marked as bold and one more', () => {
7575
expect(inline2.text()).toBe('a');
7676
expect(inline3.text()).toBe('b');
7777
expect(inline4.text()).toBe('');
78-
expect(inline2.attr()).toEqual({bold: [1, InlineAttrPos.Contained]});
78+
expect(inline2.attr().bold[0]).toBeInstanceOf(InlineAttrContained);
79+
// expect(inline2.attr()).toEqual({bold: [1, InlineAttrPos.Contained]});
7980
expect(inline3.attr()).toEqual({});
80-
expect(inline4.attr()).toEqual({
81-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
82-
});
81+
expect(inline4.attr()[SliceTypes.Cursor][0]).toBeInstanceOf(InlineAttrStartPoint);
82+
// expect(inline4.attr()).toEqual({
83+
// [SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
84+
// });
8385
});
8486

8587
test('cursor can move across block boundary forwards', () => {
@@ -92,19 +94,20 @@ test('cursor can move across block boundary forwards', () => {
9294
expect(peritext.blocks.root.children.length).toBe(2);
9395
expect([...peritext.blocks.root.children[0].texts()].length).toBe(1);
9496
expect([...peritext.blocks.root.children[0].texts()][0].text()).toBe('a');
95-
expect([...peritext.blocks.root.children[0].texts()][0].attr()).toEqual({
96-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
97-
});
97+
expect([...peritext.blocks.root.children[0].texts()][0].attr()[SliceTypes.Cursor][0]).toBeInstanceOf(
98+
InlineAttrStartPoint,
99+
);
100+
98101
editor.cursor.move(1);
99102
peritext.refresh();
100103
expect(peritext.blocks.root.children.length).toBe(2);
101104
expect([...peritext.blocks.root.children[0].texts()].length).toBe(2);
102105
expect([...peritext.blocks.root.children[0].texts()][0].text()).toBe('a');
103106
expect([...peritext.blocks.root.children[0].texts()][0].attr()).toEqual({});
104107
expect([...peritext.blocks.root.children[0].texts()][1].text()).toBe('');
105-
expect([...peritext.blocks.root.children[0].texts()][1].attr()).toEqual({
106-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
107-
});
108+
expect([...peritext.blocks.root.children[0].texts()][1].attr()[SliceTypes.Cursor][0]).toBeInstanceOf(
109+
InlineAttrStartPoint,
110+
);
108111
expect([...peritext.blocks.root.children[1].texts()].length).toBe(1);
109112
expect([...peritext.blocks.root.children[1].texts()][0].text()).toBe('b');
110113
expect([...peritext.blocks.root.children[1].texts()][0].attr()).toEqual({});
@@ -118,9 +121,9 @@ test('cursor can move across block boundary forwards', () => {
118121
expect([...peritext.blocks.root.children[1].texts()][0].text()).toBe('');
119122
expect([...peritext.blocks.root.children[1].texts()][0].attr()).toEqual({});
120123
expect([...peritext.blocks.root.children[1].texts()][1].text()).toBe('b');
121-
expect([...peritext.blocks.root.children[1].texts()][1].attr()).toEqual({
122-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
123-
});
124+
expect([...peritext.blocks.root.children[1].texts()][1].attr()[SliceTypes.Cursor][0]).toBeInstanceOf(
125+
InlineAttrStartPoint,
126+
);
124127
editor.cursor.move(1);
125128
peritext.refresh();
126129
expect(peritext.blocks.root.children.length).toBe(2);
@@ -131,7 +134,7 @@ test('cursor can move across block boundary forwards', () => {
131134
expect([...peritext.blocks.root.children[1].texts()][0].text()).toBe('b');
132135
expect([...peritext.blocks.root.children[1].texts()][0].attr()).toEqual({});
133136
expect([...peritext.blocks.root.children[1].texts()][1].text()).toBe('');
134-
expect([...peritext.blocks.root.children[1].texts()][1].attr()).toEqual({
135-
[SliceTypes.Cursor]: [[[CursorAnchor.Start, void 0]], InlineAttrPos.Collapsed],
136-
});
137+
expect([...peritext.blocks.root.children[1].texts()][1].attr()[SliceTypes.Cursor][0]).toBeInstanceOf(
138+
InlineAttrStartPoint,
139+
);
137140
});

src/json-crdt-extensions/peritext/__tests__/Peritext.render-block.spec.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const runInlineSlicesTests = (
7979
"<>
8080
<0>
8181
"abcde" { }
82-
"fghij" { BOLD = [ 1, 3 ] }
82+
"fghij" { BOLD = [ !u ] }
8383
"klmno" { }
8484
<paragraph> [ ]
8585
"pqrstuvwxyz" { }
@@ -97,7 +97,7 @@ const runInlineSlicesTests = (
9797
"<>
9898
<0>
9999
"abcde" { }
100-
"fghij" { BOLD = [ 1, 3 ] }
100+
"fghij" { BOLD = [ !u ] }
101101
"" { }
102102
<paragraph> [ ]
103103
"klmnopqrstuvwxyz" { }
@@ -117,7 +117,7 @@ const runInlineSlicesTests = (
117117
"abcdefghij" { }
118118
<paragraph> [ ]
119119
"klmno" { }
120-
"pqrst" { BOLD = [ 1, 3 ] }
120+
"pqrst" { BOLD = [ !u ] }
121121
"uvwxyz" { }
122122
"
123123
`);
@@ -135,7 +135,7 @@ const runInlineSlicesTests = (
135135
"abcdefghijklmno" { }
136136
<paragraph> [ ]
137137
"" { }
138-
"pqrst" { BOLD = [ 1, 3 ] }
138+
"pqrst" { BOLD = [ !u ] }
139139
"uvwxyz" { }
140140
"
141141
`);
@@ -151,9 +151,9 @@ const runInlineSlicesTests = (
151151
"<>
152152
<0>
153153
"abcde" { }
154-
"fghij" { BOLD = [ 1, 1 ] }
154+
"fghij" { BOLD = [ !u ] }
155155
<paragraph> [ ]
156-
"klmno" { BOLD = [ 1, 2 ] }
156+
"klmno" { BOLD = [ !u ] }
157157
"pqrstuvwxyz" { }
158158
"
159159
`);
@@ -171,11 +171,11 @@ const runInlineSlicesTests = (
171171
"<>
172172
<0>
173173
"abcdefgh" { }
174-
"ij" { BOLD = [ 1, 1 ] }
174+
"ij" { BOLD = [ !u ] }
175175
<p> [ ]
176-
"klmn" { BOLD = [ 1, 0 ] }
176+
"klmn" { BOLD = [ !u ] }
177177
<p> [ ]
178-
"opqrstu" { BOLD = [ 1, 2 ] }
178+
"opqrstu" { BOLD = [ !u ] }
179179
"vwxyz" { }
180180
"
181181
`);

src/json-crdt-extensions/peritext/__tests__/Peritext.render-cursor-movement.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
3030
"<>
3131
<0>
3232
"a" { }
33-
"bcdefghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
33+
"bcdefghijklmnopqrstuvwxyz" { -1 = [ !u ] }
3434
"
3535
`);
3636
editor.cursor.move(1);
@@ -39,7 +39,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
3939
"<>
4040
<0>
4141
"ab" { }
42-
"cdefghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
42+
"cdefghijklmnopqrstuvwxyz" { -1 = [ !u ] }
4343
"
4444
`);
4545
editor.cursor.move(2);
@@ -48,7 +48,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
4848
"<>
4949
<0>
5050
"abcd" { }
51-
"efghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
51+
"efghijklmnopqrstuvwxyz" { -1 = [ !u ] }
5252
"
5353
`);
5454
});
@@ -61,7 +61,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
6161
expect(view()).toMatchInlineSnapshot(`
6262
"<>
6363
<0>
64-
"abcdefghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
64+
"abcdefghijklmnopqrstuvwxyz" { -1 = [ !u ] }
6565
"
6666
`);
6767
editor.cursor.move(1);
@@ -72,7 +72,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
7272
"<>
7373
<0>
7474
"a" { }
75-
"bcdefghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
75+
"bcdefghijklmnopqrstuvwxyz" { -1 = [ !u ] }
7676
"
7777
`);
7878
editor.cursor.move(2);
@@ -82,7 +82,7 @@ const runInlineSlicesTests = (desc: string, getKit: () => Kit) => {
8282
"<>
8383
<0>
8484
"abc" { }
85-
"defghijklmnopqrstuvwxyz" { -1 = [ [ [ 0, !u ] ], 4 ] }
85+
"defghijklmnopqrstuvwxyz" { -1 = [ !u ] }
8686
"
8787
`);
8888
});

0 commit comments

Comments
 (0)