Skip to content

Commit 60e8c3f

Browse files
authored
Merge pull request #893 from streamich/link-formatting
Link formatting
2 parents 652e50a + 41c7559 commit 60e8c3f

File tree

118 files changed

+4947
-2455
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+4947
-2455
lines changed

biome.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
},
4545
"a11y": {
4646
"noSvgWithoutTitle": "off"
47+
},
48+
"performance": {
49+
"noDelete": "off"
4750
}
4851
}
4952
}

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@
106106
"@types/react": "^18.3.11",
107107
"@types/react-dom": "^18.3.0",
108108
"benchmark": "^2.1.4",
109+
"clipboard-copy": "^4.0.1",
110+
"collaborative-editor": "^2.8.0",
111+
"collaborative-input": "^1.6.1",
112+
"collaborative-ui": "^1.6.0",
109113
"config-galore": "^1.0.0",
110114
"editing-traces": "https://github.com/streamich/editing-traces#6494020428530a6e382378b98d1d7e31334e2d7b",
111115
"fast-diff": "^1.3.0",
@@ -127,6 +131,7 @@
127131
"tslint": "^6.1.3",
128132
"tslint-config-common": "^1.6.2",
129133
"typescript": "^5.4.5",
134+
"use-t": "^1.6.3",
130135
"webpack": "^5.95.0",
131136
"webpack-cli": "^5.1.4",
132137
"webpack-dev-server": "^5.1.0",

src/json-crdt-diff/JsonCrdtDiff.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export class JsonCrdtDiff {
182182
}
183183
}
184184

185-
public diffAny(src: JsonNode, dst: unknown): void {
185+
protected diffAny(src: JsonNode, dst: unknown): void {
186186
if (src instanceof ConNode) {
187187
const val = src.val;
188188
if (val !== dst && !deepEqual(src.val, dst)) throw new DiffError();

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ import type {SliceSchema, SliceType} from './slice/types';
2323
import type {SchemaToJsonNode} from '../../json-crdt/schema/types';
2424
import type {AbstractRga} from '../../json-crdt/nodes/rga';
2525
import type {ChunkSlice} from './util/ChunkSlice';
26+
import type {Stateful} from './types';
2627

2728
const EXTRA_SLICES_SCHEMA = s.vec(s.arr<SliceSchema>([]));
29+
const LOCAL_DATA_SCHEMA = EXTRA_SLICES_SCHEMA;
2830

29-
type SlicesModel = Model<SchemaToJsonNode<typeof EXTRA_SLICES_SCHEMA>>;
31+
export type ExtraSlicesModel = Model<SchemaToJsonNode<typeof EXTRA_SLICES_SCHEMA>>;
32+
export type LocalModel = Model<SchemaToJsonNode<typeof LOCAL_DATA_SCHEMA>>;
3033

3134
/**
3235
* Context for a Peritext instance. Contains all the data and methods needed to
3336
* interact with the text.
3437
*/
35-
export class Peritext<T = string> implements Printable {
38+
export class Peritext<T = string> implements Printable, Stateful {
3639
/**
3740
* *Slices* are rich-text annotations that appear in the text. The "saved"
3841
* slices are the ones that are persisted in the document.
@@ -76,8 +79,8 @@ export class Peritext<T = string> implements Printable {
7679
public readonly str: AbstractRga<T>,
7780
slices: ArrNode,
7881
// TODO: Add test that verifies that SIDs are different across all three models.
79-
extraSlicesModel: SlicesModel = Model.create(EXTRA_SLICES_SCHEMA, model.clock.sid - 1),
80-
localSlicesModel: SlicesModel = Model.create(EXTRA_SLICES_SCHEMA, SESSION.LOCAL),
82+
extraSlicesModel: ExtraSlicesModel = Model.create(EXTRA_SLICES_SCHEMA, model.clock.sid - 1),
83+
localSlicesModel: LocalModel = Model.create(LOCAL_DATA_SCHEMA, SESSION.LOCAL),
8184
) {
8285
this.savedSlices = new Slices(this, slices);
8386
this.extraSlices = new ExtraSlices(this, extraSlicesModel.root.node().get(0)!);
@@ -327,7 +330,7 @@ export class Peritext<T = string> implements Printable {
327330
api.apply();
328331
}
329332

330-
// ---------------------------------------------------------------- Printable
333+
/** ----------------------------------------------------- {@link Printable} */
331334

332335
public toString(tab: string = ''): string {
333336
const nl = () => '';
@@ -348,7 +351,7 @@ export class Peritext<T = string> implements Printable {
348351
);
349352
}
350353

351-
// ----------------------------------------------------------------- Stateful
354+
/** ------------------------------------------------------ {@link Stateful} */
352355

353356
public hash: number = 0;
354357

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ const runInlineSlicesTests = (
6262
<0>
6363
"abcdefghijklmnopqrstuvwxyz" { }
6464
<unfurl> { link = "foobar" }
65-
"" { }
6665
"
6766
`);
6867
});
@@ -132,7 +131,6 @@ const runInlineSlicesTests = (
132131
<0>
133132
"abcdefghijklmno" { }
134133
<paragraph> [ ]
135-
"" { }
136134
"pqrst" { BOLD = [ !u ] }
137135
"uvwxyz" { }
138136
"

src/json-crdt-extensions/peritext/block/Block.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {CONST, updateJson, updateNum} from '../../../json-hash/hash';
33
import {MarkerOverlayPoint} from '../overlay/MarkerOverlayPoint';
44
import {UndefEndIter, type UndefIterator} from '../../../util/iterator';
55
import {Inline} from './Inline';
6-
import {formatType} from '../slice/util';
6+
import {formatType, getTag} from '../slice/util';
77
import {Range} from '../rga/Range';
88
import type {Point} from '../rga/Point';
99
import type {OverlayPoint} from '../overlay/OverlayPoint';
@@ -45,11 +45,7 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
4545
}
4646

4747
public tag(): number | string {
48-
const path = this.path;
49-
const length = path.length;
50-
if (!length) return '';
51-
const step = path[length - 1];
52-
return Array.isArray(step) ? step[0] : step;
48+
return getTag(this.path);
5349
}
5450

5551
public attr(): Attr | undefined {
@@ -109,15 +105,16 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
109105
*/
110106
public texts0(): UndefIterator<Inline<T>> {
111107
const txt = this.txt;
108+
const overlay = txt.overlay;
112109
const iterator = this.tuples0();
113110
const start = this.start;
114111
const end = this.end;
115-
const startIsMarker = txt.overlay.isMarker(start.id);
116-
const endIsMarker = txt.overlay.isMarker(end.id);
112+
const startIsMarker = overlay.isMarker(start.id);
113+
const endIsMarker = overlay.isMarker(end.id);
117114
let isFirst = true;
118115
let next = iterator();
119116
let closed = false;
120-
return () => {
117+
const newIterator: UndefIterator<Inline<T>> = () => {
121118
if (closed) return;
122119
const pair = next;
123120
next = iterator();
@@ -131,6 +128,9 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
131128
if (startIsMarker) {
132129
point1 = point1.clone();
133130
point1.step(1);
131+
// Skip condition when inline annotations tarts immediately at th
132+
// beginning of the block.
133+
if (point1.cmp(point2) === 0) return newIterator();
134134
}
135135
}
136136
if (!endIsMarker && end.cmpSpatial(overlayPoint2) < 0) {
@@ -139,6 +139,7 @@ export class Block<T = string, Attr = unknown> extends Range<T> implements IBloc
139139
}
140140
return new Inline(txt, overlayPoint1, overlayPoint2, point1, point2);
141141
};
142+
return newIterator;
142143
}
143144

144145
/**

src/json-crdt-extensions/peritext/block/Inline.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {printTree} from 'tree-dump/lib/printTree';
22
import {stringify} from '../../../json-text/stringify';
3-
import {SliceBehavior, SliceTypeName} from '../slice/constants';
3+
import {SliceStacking, SliceTypeName} from '../slice/constants';
44
import {Range} from '../rga/Range';
55
import {ChunkSlice} from '../util/ChunkSlice';
66
import {Cursor} from '../editor/Cursor';
@@ -160,8 +160,8 @@ export class Inline<T = string> extends Range<T> implements Printable {
160160
public attr(): InlineAttrs<T> {
161161
if (this._attr) return this._attr;
162162
const attr: InlineAttrs<T> = (this._attr = {});
163-
const p1 = this.p1 as OverlayPoint<T>;
164-
const p2 = this.p2 as OverlayPoint<T>;
163+
const p1 = this.p1;
164+
const p2 = this.p2;
165165
const slices1 = p1.layers;
166166
const slices2 = p1.markers;
167167
const slices3 = p2.isAbsEnd() ? p2.markers : [];
@@ -174,22 +174,22 @@ export class Inline<T = string> extends Range<T> implements Printable {
174174
const slice = i >= length12 ? slices3[i - length12] : i >= length1 ? slices2[i - length1] : slices1[i];
175175
if (slice instanceof Range) {
176176
const type = slice.type as PathStep;
177-
switch (slice.behavior) {
178-
case SliceBehavior.Cursor: {
177+
switch (slice.stacking) {
178+
case SliceStacking.Cursor: {
179179
const stack: InlineAttrStack<T> = attr[SliceTypeName.Cursor] ?? (attr[SliceTypeName.Cursor] = []);
180180
stack.push(this.createAttr(slice));
181181
break;
182182
}
183-
case SliceBehavior.Many: {
183+
case SliceStacking.Many: {
184184
const stack: InlineAttrStack<T> = attr[type] ?? (attr[type] = []);
185185
stack.push(this.createAttr(slice));
186186
break;
187187
}
188-
case SliceBehavior.One: {
188+
case SliceStacking.One: {
189189
attr[type] = [this.createAttr(slice)];
190190
break;
191191
}
192-
case SliceBehavior.Erase: {
192+
case SliceStacking.Erase: {
193193
delete attr[type];
194194
break;
195195
}

src/json-crdt-extensions/peritext/block/__tests__/Fragment-refresh.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
setupAlphabetWithDeletesKit,
66
setupAlphabetWithTwoChunksKit,
77
} from '../../__tests__/setup';
8+
import {LeafBlock} from '../LeafBlock';
89

910
const runTests = (setup: () => Kit) => {
1011
test('updates block hash only where something was changed - leading block', () => {
@@ -63,6 +64,24 @@ const runTests = (setup: () => Kit) => {
6364
expect(rootHash1).not.toBe(rootHash2);
6465
expect(firstBlockHash1).not.toBe(firstBlockHash2);
6566
});
67+
68+
test('does not create collapsed Inline at the beginning of block', () => {
69+
const {editor, peritext} = setup();
70+
editor.cursor.setAt(0);
71+
editor.saved.insMarker('p');
72+
editor.cursor.setAt(1, 4);
73+
editor.saved.insOne('strong');
74+
peritext.refresh();
75+
const fragment = peritext.blocks.root;
76+
const block = fragment.children[0];
77+
expect(block instanceof LeafBlock).toBe(true);
78+
expect(block.tag()).toBe('p');
79+
const [inline0, inline1] = block.texts();
80+
expect(inline0.text()).toBe('abcd');
81+
expect(!!inline0.attr().strong).toBe(true);
82+
expect(inline1.text()).toBe('efghijklmnopqrstuvwxyz');
83+
expect(!!inline1.attr().strong).toBe(false);
84+
});
6685
};
6786

6887
describe('Blocks.refresh()', () => {

src/json-crdt-extensions/peritext/block/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {SliceBehavior} from '../slice/constants';
1+
import type {SliceStacking} from '../slice/constants';
22

33
export type PeritextMlNode = string | PeritextMlElement;
44

@@ -11,5 +11,5 @@ export type PeritextMlElement<Tag extends string | number = string | number, Dat
1111
export interface PeritextMlAttributes<Data = unknown, Inline = boolean> {
1212
data?: Data;
1313
inline?: Inline;
14-
behavior?: SliceBehavior;
14+
stacking?: SliceStacking;
1515
}

0 commit comments

Comments
 (0)