@input/pen-core 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4353,6 +4353,12 @@ var DecorationSetImpl = class _DecorationSetImpl {
4353
4353
  get decorations() {
4354
4354
  return this._decorations;
4355
4355
  }
4356
+ get blockIndex() {
4357
+ return this._blockIndex;
4358
+ }
4359
+ get isReleased() {
4360
+ return this._released;
4361
+ }
4356
4362
  forBlock(blockId) {
4357
4363
  return this._blockIndex.get(blockId) ?? EMPTY_ARRAY;
4358
4364
  }
@@ -4428,6 +4434,36 @@ function mergeDecorationSets(...sets) {
4428
4434
  if (all.length === 0) return EMPTY_SET;
4429
4435
  return new DecorationSetImpl(all);
4430
4436
  }
4437
+ function reconcileDecorationSets(previous, next) {
4438
+ if (previous === next) return previous;
4439
+ if (!(previous instanceof DecorationSetImpl) || !(next instanceof DecorationSetImpl) || previous.isReleased) {
4440
+ return next;
4441
+ }
4442
+ const previousIndex = previous.blockIndex;
4443
+ const nextIndex = next.blockIndex;
4444
+ if (previousIndex.size !== nextIndex.size) {
4445
+ return reuseUnchangedBlocks(previousIndex, nextIndex);
4446
+ }
4447
+ for (const [blockId, nextList] of nextIndex) {
4448
+ const previousList = previousIndex.get(blockId);
4449
+ if (!previousList || !decorationsListEqual(previousList, nextList)) {
4450
+ return reuseUnchangedBlocks(previousIndex, nextIndex);
4451
+ }
4452
+ }
4453
+ return previous;
4454
+ }
4455
+ function reuseUnchangedBlocks(previousIndex, nextIndex) {
4456
+ const index = /* @__PURE__ */ new Map();
4457
+ const flat = [];
4458
+ for (const [blockId, nextList] of nextIndex) {
4459
+ const previousList = previousIndex.get(blockId);
4460
+ const list = previousList && decorationsListEqual(previousList, nextList) ? previousList : nextList;
4461
+ index.set(blockId, list);
4462
+ flat.push(...list);
4463
+ }
4464
+ if (flat.length === 0) return EMPTY_SET;
4465
+ return new DecorationSetImpl(flat, void 0, index);
4466
+ }
4431
4467
  function buildBlockIndex(decorations) {
4432
4468
  const index = /* @__PURE__ */ new Map();
4433
4469
  for (const dec of decorations) {
@@ -7990,6 +8026,13 @@ function readTextFocus(editor) {
7990
8026
  }
7991
8027
  return selection.focus;
7992
8028
  }
8029
+ function readTextAffinity(editor) {
8030
+ const selection = editor.selection;
8031
+ if (!selection || selection.type !== "text") {
8032
+ return "downstream";
8033
+ }
8034
+ return selection.affinity ?? "downstream";
8035
+ }
7993
8036
  function readTextAnchor(editor) {
7994
8037
  const selection = editor.selection;
7995
8038
  if (!selection || selection.type !== "text") {
@@ -9153,7 +9196,11 @@ function handleVerticalCaret(editor, param, direction) {
9153
9196
  if (!focus) {
9154
9197
  return false;
9155
9198
  }
9156
- const measured = measureVerticalStep(editor, focus, direction);
9199
+ const measured = measureVerticalStep(
9200
+ editor,
9201
+ { ...focus, affinity: readTextAffinity(editor) },
9202
+ direction
9203
+ );
9157
9204
  if (measured) {
9158
9205
  const measuredBlockId = measured.point.blockId;
9159
9206
  if (!isEditableTextBlock(editor, measuredBlockId) && getBlockInputMode(editor, measuredBlockId) !== "table") {
@@ -13351,7 +13398,9 @@ var EditorImpl = class {
13351
13398
  }
13352
13399
  // ── Decorations ──────────────────────────────────────────
13353
13400
  requestDecorationUpdate() {
13401
+ const previousGeneration = this._decorations.generation;
13354
13402
  const decoSet = this._refreshDecorations();
13403
+ if (decoSet.generation === previousGeneration) return;
13355
13404
  this._emitter.emit("decorationsChange", decoSet.generation);
13356
13405
  }
13357
13406
  getDecorations() {
@@ -13361,9 +13410,9 @@ var EditorImpl = class {
13361
13410
  return this._emitter.on(event, handler);
13362
13411
  }
13363
13412
  _refreshDecorations() {
13364
- this._decorations = this._extensions.collectDecorations(
13365
- this._documentState,
13366
- this
13413
+ this._decorations = reconcileDecorationSets(
13414
+ this._decorations,
13415
+ this._extensions.collectDecorations(this._documentState, this)
13367
13416
  );
13368
13417
  return this._decorations;
13369
13418
  }
package/dist/index.d.cts CHANGED
@@ -939,6 +939,13 @@ type VerticalCaretDirection = "up" | "down";
939
939
  type VerticalCaretPoint = {
940
940
  readonly blockId: string;
941
941
  readonly offset: number;
942
+ /**
943
+ * Side the caret is drawn on at a line boundary. The measure must read
944
+ * the current line from this, not from the motion direction: an offset
945
+ * right after a wrap or `\n` is upstream the end of the line above and
946
+ * downstream the start of the line below. Absent means `"downstream"`.
947
+ */
948
+ readonly affinity?: Affinity;
942
949
  };
943
950
  type VerticalCaretMeasureResult = {
944
951
  readonly point: VerticalCaretPoint;
package/dist/index.d.ts CHANGED
@@ -939,6 +939,13 @@ type VerticalCaretDirection = "up" | "down";
939
939
  type VerticalCaretPoint = {
940
940
  readonly blockId: string;
941
941
  readonly offset: number;
942
+ /**
943
+ * Side the caret is drawn on at a line boundary. The measure must read
944
+ * the current line from this, not from the motion direction: an offset
945
+ * right after a wrap or `\n` is upstream the end of the line above and
946
+ * downstream the start of the line below. Absent means `"downstream"`.
947
+ */
948
+ readonly affinity?: Affinity;
942
949
  };
943
950
  type VerticalCaretMeasureResult = {
944
951
  readonly point: VerticalCaretPoint;
package/dist/index.mjs CHANGED
@@ -4118,6 +4118,12 @@ var DecorationSetImpl = class _DecorationSetImpl {
4118
4118
  get decorations() {
4119
4119
  return this._decorations;
4120
4120
  }
4121
+ get blockIndex() {
4122
+ return this._blockIndex;
4123
+ }
4124
+ get isReleased() {
4125
+ return this._released;
4126
+ }
4121
4127
  forBlock(blockId) {
4122
4128
  return this._blockIndex.get(blockId) ?? EMPTY_ARRAY;
4123
4129
  }
@@ -4193,6 +4199,36 @@ function mergeDecorationSets(...sets) {
4193
4199
  if (all.length === 0) return EMPTY_SET;
4194
4200
  return new DecorationSetImpl(all);
4195
4201
  }
4202
+ function reconcileDecorationSets(previous, next) {
4203
+ if (previous === next) return previous;
4204
+ if (!(previous instanceof DecorationSetImpl) || !(next instanceof DecorationSetImpl) || previous.isReleased) {
4205
+ return next;
4206
+ }
4207
+ const previousIndex = previous.blockIndex;
4208
+ const nextIndex = next.blockIndex;
4209
+ if (previousIndex.size !== nextIndex.size) {
4210
+ return reuseUnchangedBlocks(previousIndex, nextIndex);
4211
+ }
4212
+ for (const [blockId, nextList] of nextIndex) {
4213
+ const previousList = previousIndex.get(blockId);
4214
+ if (!previousList || !decorationsListEqual(previousList, nextList)) {
4215
+ return reuseUnchangedBlocks(previousIndex, nextIndex);
4216
+ }
4217
+ }
4218
+ return previous;
4219
+ }
4220
+ function reuseUnchangedBlocks(previousIndex, nextIndex) {
4221
+ const index = /* @__PURE__ */ new Map();
4222
+ const flat = [];
4223
+ for (const [blockId, nextList] of nextIndex) {
4224
+ const previousList = previousIndex.get(blockId);
4225
+ const list = previousList && decorationsListEqual(previousList, nextList) ? previousList : nextList;
4226
+ index.set(blockId, list);
4227
+ flat.push(...list);
4228
+ }
4229
+ if (flat.length === 0) return EMPTY_SET;
4230
+ return new DecorationSetImpl(flat, void 0, index);
4231
+ }
4196
4232
  function buildBlockIndex(decorations) {
4197
4233
  const index = /* @__PURE__ */ new Map();
4198
4234
  for (const dec of decorations) {
@@ -7768,6 +7804,13 @@ function readTextFocus(editor) {
7768
7804
  }
7769
7805
  return selection.focus;
7770
7806
  }
7807
+ function readTextAffinity(editor) {
7808
+ const selection = editor.selection;
7809
+ if (!selection || selection.type !== "text") {
7810
+ return "downstream";
7811
+ }
7812
+ return selection.affinity ?? "downstream";
7813
+ }
7771
7814
  function readTextAnchor(editor) {
7772
7815
  const selection = editor.selection;
7773
7816
  if (!selection || selection.type !== "text") {
@@ -8931,7 +8974,11 @@ function handleVerticalCaret(editor, param, direction) {
8931
8974
  if (!focus) {
8932
8975
  return false;
8933
8976
  }
8934
- const measured = measureVerticalStep(editor, focus, direction);
8977
+ const measured = measureVerticalStep(
8978
+ editor,
8979
+ { ...focus, affinity: readTextAffinity(editor) },
8980
+ direction
8981
+ );
8935
8982
  if (measured) {
8936
8983
  const measuredBlockId = measured.point.blockId;
8937
8984
  if (!isEditableTextBlock(editor, measuredBlockId) && getBlockInputMode(editor, measuredBlockId) !== "table") {
@@ -13154,7 +13201,9 @@ var EditorImpl = class {
13154
13201
  }
13155
13202
  // ── Decorations ──────────────────────────────────────────
13156
13203
  requestDecorationUpdate() {
13204
+ const previousGeneration = this._decorations.generation;
13157
13205
  const decoSet = this._refreshDecorations();
13206
+ if (decoSet.generation === previousGeneration) return;
13158
13207
  this._emitter.emit("decorationsChange", decoSet.generation);
13159
13208
  }
13160
13209
  getDecorations() {
@@ -13164,9 +13213,9 @@ var EditorImpl = class {
13164
13213
  return this._emitter.on(event, handler);
13165
13214
  }
13166
13215
  _refreshDecorations() {
13167
- this._decorations = this._extensions.collectDecorations(
13168
- this._documentState,
13169
- this
13216
+ this._decorations = reconcileDecorationSets(
13217
+ this._decorations,
13218
+ this._extensions.collectDecorations(this._documentState, this)
13170
13219
  );
13171
13220
  return this._decorations;
13172
13221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@input/pen-core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Headless, extension-first editor engine for human-AI co-authoring",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/input-systems/pen#readme",
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "sideEffects": false,
45
45
  "dependencies": {
46
- "@input/pen-yjs": "^0.2.0",
47
- "@input/pen-types": "^0.2.0"
46
+ "@input/pen-yjs": "^0.2.2",
47
+ "@input/pen-types": "^0.2.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "tsup": "^8.4.0",