@input/pen-core 0.1.9 → 0.2.1

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) {
@@ -8826,6 +8862,10 @@ function handleGraphemeCaret(editor, param, direction) {
8826
8862
  if (fromCell !== void 0) {
8827
8863
  return finishNonVertical(editor, fromCell);
8828
8864
  }
8865
+ const collapsed = collapseTextRange(editor, param, direction);
8866
+ if (collapsed !== void 0) {
8867
+ return finishNonVertical(editor, collapsed);
8868
+ }
8829
8869
  const focus = readTextFocus(editor);
8830
8870
  if (!focus) {
8831
8871
  return false;
@@ -8844,6 +8884,18 @@ function handleGraphemeCaret(editor, param, direction) {
8844
8884
  selection: extendSelection(editor, param.extend, next)
8845
8885
  });
8846
8886
  }
8887
+ function collapseTextRange(editor, param, direction) {
8888
+ const selection = editor.selection;
8889
+ if (param.extend || !selection || selection.type !== "text" || isCollapsed(selection)) {
8890
+ return void 0;
8891
+ }
8892
+ const ordered = documentOrderedTextPoints(editor, selection);
8893
+ if (!ordered) {
8894
+ return void 0;
8895
+ }
8896
+ const edge = direction === 1 ? ordered.end : ordered.start;
8897
+ return { selection: collapsedAt(edge.blockId, edge.offset) };
8898
+ }
8847
8899
  function handleWordCaret(editor, param, direction) {
8848
8900
  const fromCellEdit = handleCellEditingCaret(
8849
8901
  editor,
@@ -9077,7 +9129,7 @@ function crossBlock(editor, blockId, direction) {
9077
9129
  };
9078
9130
  }
9079
9131
  function documentEdgePoint(editor, edge) {
9080
- const order = editor.documentState.blockOrder;
9132
+ const order = getVisibleBlockIds(editor);
9081
9133
  if (order.length === 0) {
9082
9134
  return null;
9083
9135
  }
@@ -13335,7 +13387,9 @@ var EditorImpl = class {
13335
13387
  }
13336
13388
  // ── Decorations ──────────────────────────────────────────
13337
13389
  requestDecorationUpdate() {
13390
+ const previousGeneration = this._decorations.generation;
13338
13391
  const decoSet = this._refreshDecorations();
13392
+ if (decoSet.generation === previousGeneration) return;
13339
13393
  this._emitter.emit("decorationsChange", decoSet.generation);
13340
13394
  }
13341
13395
  getDecorations() {
@@ -13345,9 +13399,9 @@ var EditorImpl = class {
13345
13399
  return this._emitter.on(event, handler);
13346
13400
  }
13347
13401
  _refreshDecorations() {
13348
- this._decorations = this._extensions.collectDecorations(
13349
- this._documentState,
13350
- this
13402
+ this._decorations = reconcileDecorationSets(
13403
+ this._decorations,
13404
+ this._extensions.collectDecorations(this._documentState, this)
13351
13405
  );
13352
13406
  return this._decorations;
13353
13407
  }
package/dist/index.d.cts CHANGED
@@ -1102,6 +1102,7 @@ declare const assetProviderFacet: _input_pen_types.Facet<unknown, unknown>;
1102
1102
  declare const toolRuntimeFacet: _input_pen_types.Facet<unknown, unknown>;
1103
1103
  declare const announcerFacet: _input_pen_types.Facet<unknown, unknown>;
1104
1104
  declare const streamingTargetFacet: _input_pen_types.Facet<unknown, unknown>;
1105
+ /** Facet that publishes the smooth-stream controller, or null when the extension is not installed. */
1105
1106
  declare const smoothStreamControllerFacet: _input_pen_types.Facet<unknown, unknown>;
1106
1107
 
1107
1108
  declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "blockText" | "structural">, documentOrder?: readonly string[]): string[];
package/dist/index.d.ts CHANGED
@@ -1102,6 +1102,7 @@ declare const assetProviderFacet: _input_pen_types.Facet<unknown, unknown>;
1102
1102
  declare const toolRuntimeFacet: _input_pen_types.Facet<unknown, unknown>;
1103
1103
  declare const announcerFacet: _input_pen_types.Facet<unknown, unknown>;
1104
1104
  declare const streamingTargetFacet: _input_pen_types.Facet<unknown, unknown>;
1105
+ /** Facet that publishes the smooth-stream controller, or null when the extension is not installed. */
1105
1106
  declare const smoothStreamControllerFacet: _input_pen_types.Facet<unknown, unknown>;
1106
1107
 
1107
1108
  declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "blockText" | "structural">, documentOrder?: readonly string[]): string[];
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) {
@@ -8604,6 +8640,10 @@ function handleGraphemeCaret(editor, param, direction) {
8604
8640
  if (fromCell !== void 0) {
8605
8641
  return finishNonVertical(editor, fromCell);
8606
8642
  }
8643
+ const collapsed = collapseTextRange(editor, param, direction);
8644
+ if (collapsed !== void 0) {
8645
+ return finishNonVertical(editor, collapsed);
8646
+ }
8607
8647
  const focus = readTextFocus(editor);
8608
8648
  if (!focus) {
8609
8649
  return false;
@@ -8622,6 +8662,18 @@ function handleGraphemeCaret(editor, param, direction) {
8622
8662
  selection: extendSelection(editor, param.extend, next)
8623
8663
  });
8624
8664
  }
8665
+ function collapseTextRange(editor, param, direction) {
8666
+ const selection = editor.selection;
8667
+ if (param.extend || !selection || selection.type !== "text" || isCollapsed(selection)) {
8668
+ return void 0;
8669
+ }
8670
+ const ordered = documentOrderedTextPoints(editor, selection);
8671
+ if (!ordered) {
8672
+ return void 0;
8673
+ }
8674
+ const edge = direction === 1 ? ordered.end : ordered.start;
8675
+ return { selection: collapsedAt(edge.blockId, edge.offset) };
8676
+ }
8625
8677
  function handleWordCaret(editor, param, direction) {
8626
8678
  const fromCellEdit = handleCellEditingCaret(
8627
8679
  editor,
@@ -8855,7 +8907,7 @@ function crossBlock(editor, blockId, direction) {
8855
8907
  };
8856
8908
  }
8857
8909
  function documentEdgePoint(editor, edge) {
8858
- const order = editor.documentState.blockOrder;
8910
+ const order = getVisibleBlockIds(editor);
8859
8911
  if (order.length === 0) {
8860
8912
  return null;
8861
8913
  }
@@ -13138,7 +13190,9 @@ var EditorImpl = class {
13138
13190
  }
13139
13191
  // ── Decorations ──────────────────────────────────────────
13140
13192
  requestDecorationUpdate() {
13193
+ const previousGeneration = this._decorations.generation;
13141
13194
  const decoSet = this._refreshDecorations();
13195
+ if (decoSet.generation === previousGeneration) return;
13142
13196
  this._emitter.emit("decorationsChange", decoSet.generation);
13143
13197
  }
13144
13198
  getDecorations() {
@@ -13148,9 +13202,9 @@ var EditorImpl = class {
13148
13202
  return this._emitter.on(event, handler);
13149
13203
  }
13150
13204
  _refreshDecorations() {
13151
- this._decorations = this._extensions.collectDecorations(
13152
- this._documentState,
13153
- this
13205
+ this._decorations = reconcileDecorationSets(
13206
+ this._decorations,
13207
+ this._extensions.collectDecorations(this._documentState, this)
13154
13208
  );
13155
13209
  return this._decorations;
13156
13210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@input/pen-core",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
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.1.9",
47
- "@input/pen-types": "^0.1.9"
46
+ "@input/pen-yjs": "^0.2.1",
47
+ "@input/pen-types": "^0.2.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "tsup": "^8.4.0",