@input/pen-core 0.1.7 → 0.1.9

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
@@ -134,6 +134,7 @@ __export(index_exports, {
134
134
  historyUndo: () => historyUndo,
135
135
  hookPriorityToPrecedence: () => hookPriorityToPrecedence,
136
136
  indent: () => indent,
137
+ inlineLogicalText: () => inlineLogicalText,
137
138
  inputRulesEngineFacet: () => inputRulesEngineFacet,
138
139
  inputRulesFacet: () => inputRulesFacet,
139
140
  insertLineBreak: () => insertLineBreak,
@@ -189,6 +190,7 @@ __export(index_exports, {
189
190
  resolveSchema: () => resolveSchema,
190
191
  resolveSchemaA11y: () => resolveSchemaA11y,
191
192
  resolveSelectionTargetBlockIds: () => resolveSelectionTargetBlockIds,
193
+ resolveSuggestionMenuTarget: () => resolveSuggestionMenuTarget,
192
194
  runMigrations: () => runMigrations,
193
195
  searchControllerFacet: () => searchControllerFacet,
194
196
  selectAdjacentInlineAtom: () => selectAdjacentInlineAtom,
@@ -206,6 +208,7 @@ __export(index_exports, {
206
208
  shouldShowBlockInDefaultMenus: () => shouldShowBlockInDefaultMenus,
207
209
  singleController: () => singleController,
208
210
  slashMenuGroupOf: () => slashMenuGroupOf,
211
+ smoothStreamControllerFacet: () => smoothStreamControllerFacet,
209
212
  snapToNormalPosition: () => snapToNormalPosition,
210
213
  snapshotsControllerFacet: () => snapshotsControllerFacet,
211
214
  sortDeltaAttributes: () => sortDeltaAttributes,
@@ -589,6 +592,47 @@ function filterOpsForDocumentProfile(ops, documentProfile, registry) {
589
592
  };
590
593
  }
591
594
 
595
+ // src/schema/generateValidator.ts
596
+ function generateValidator(propSchemas) {
597
+ return (raw) => {
598
+ const result = {};
599
+ for (const [key, schema] of Object.entries(propSchemas)) {
600
+ let value = raw[key];
601
+ if (value === void 0 || value === null) {
602
+ result[key] = schema.default;
603
+ continue;
604
+ }
605
+ const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type;
606
+ if (schemaType === "number" && typeof value === "string") {
607
+ const parsed = Number(value);
608
+ if (!Number.isNaN(parsed)) value = parsed;
609
+ }
610
+ if (schemaType === "boolean" && typeof value === "string") {
611
+ value = value === "true";
612
+ }
613
+ const matchesSchemaType = schemaType === "array" ? Array.isArray(value) : typeof value === schemaType;
614
+ if (schema.type && schemaType !== void 0 && !matchesSchemaType) {
615
+ result[key] = schema.default;
616
+ continue;
617
+ }
618
+ if (typeof value === "number") {
619
+ if (schema.minimum !== void 0 && value < schema.minimum) {
620
+ value = schema.minimum;
621
+ }
622
+ if (typeof value === "number" && schema.maximum !== void 0 && value > schema.maximum) {
623
+ value = schema.maximum;
624
+ }
625
+ }
626
+ if (schema.enum && !schema.enum.includes(value)) {
627
+ result[key] = schema.default;
628
+ continue;
629
+ }
630
+ result[key] = value;
631
+ }
632
+ return result;
633
+ };
634
+ }
635
+
592
636
  // src/schema/prop.ts
593
637
  var PropChainImpl = class {
594
638
  _schema;
@@ -895,6 +939,10 @@ var SchemaRegistryImpl = class _SchemaRegistryImpl {
895
939
  if (patch.serialize) {
896
940
  merged.serialize = { ...existing.serialize, ...patch.serialize };
897
941
  }
942
+ if ("propSchema" in patch && !("validateProps" in patch)) {
943
+ const propSchema = merged.propSchema ?? {};
944
+ merged.validateProps = Object.keys(propSchema).length > 0 ? generateValidator(propSchema) : void 0;
945
+ }
898
946
  const blocks = new Map(this._blocks);
899
947
  blocks.set(type, merged);
900
948
  return new _SchemaRegistryImpl({
@@ -989,45 +1037,6 @@ function generateAIDescription(type, props) {
989
1037
  }).join(", ");
990
1038
  return `${type}: ${propDescriptions}`;
991
1039
  }
992
- function generateValidator(propSchemas) {
993
- return (raw) => {
994
- const result = {};
995
- for (const [key, schema] of Object.entries(propSchemas)) {
996
- let value = raw[key];
997
- if (value === void 0 || value === null) {
998
- result[key] = schema.default;
999
- continue;
1000
- }
1001
- const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type;
1002
- if (schemaType === "number" && typeof value === "string") {
1003
- const parsed = Number(value);
1004
- if (!Number.isNaN(parsed)) value = parsed;
1005
- }
1006
- if (schemaType === "boolean" && typeof value === "string") {
1007
- value = value === "true";
1008
- }
1009
- const matchesSchemaType = schemaType === "array" ? Array.isArray(value) : typeof value === schemaType;
1010
- if (schema.type && schemaType !== void 0 && !matchesSchemaType) {
1011
- result[key] = schema.default;
1012
- continue;
1013
- }
1014
- if (typeof value === "number") {
1015
- if (schema.minimum !== void 0 && value < schema.minimum) {
1016
- value = schema.minimum;
1017
- }
1018
- if (typeof value === "number" && schema.maximum !== void 0 && value > schema.maximum) {
1019
- value = schema.maximum;
1020
- }
1021
- }
1022
- if (schema.enum && !schema.enum.includes(value)) {
1023
- result[key] = schema.default;
1024
- continue;
1025
- }
1026
- result[key] = value;
1027
- }
1028
- return result;
1029
- };
1030
- }
1031
1040
  function defineBlock(typeOrConfig, maybeConfig) {
1032
1041
  const type = typeof typeOrConfig === "string" ? typeOrConfig : typeOrConfig.type;
1033
1042
  const config = typeof typeOrConfig === "string" ? maybeConfig : typeOrConfig;
@@ -3654,6 +3663,18 @@ function snapshotPlain(value) {
3654
3663
  writable: true
3655
3664
  });
3656
3665
  }
3666
+ for (const key of Object.getOwnPropertySymbols(value)) {
3667
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3668
+ if (!descriptor?.enumerable) {
3669
+ continue;
3670
+ }
3671
+ Object.defineProperty(next, key, {
3672
+ value: snapshotPlain(descriptor.value),
3673
+ enumerable: true,
3674
+ configurable: true,
3675
+ writable: true
3676
+ });
3677
+ }
3657
3678
  return next;
3658
3679
  }
3659
3680
  function snapshotOps(ops) {
@@ -5087,17 +5108,54 @@ function normalizeFieldEditorType(schema) {
5087
5108
  return "none";
5088
5109
  }
5089
5110
 
5111
+ // src/editor/documentPreorder.ts
5112
+ function documentPreorderBlockIds(editor) {
5113
+ return documentPreorderBlockIdsFromState(editor.documentState);
5114
+ }
5115
+ function documentPreorderBlockIdsFromState(state) {
5116
+ const ids = [];
5117
+ for (const block of state.blocks) {
5118
+ ids.push(block.id);
5119
+ }
5120
+ return ids;
5121
+ }
5122
+ function documentPreorderBlockIdsFromDoc(doc) {
5123
+ const ids = [];
5124
+ const seen = /* @__PURE__ */ new Set();
5125
+ const blocks = doc.blocks;
5126
+ const order = doc.blockOrder;
5127
+ const walk = (id) => {
5128
+ if (seen.has(id)) {
5129
+ return;
5130
+ }
5131
+ seen.add(id);
5132
+ ids.push(id);
5133
+ const blockMap = blocks.get(id);
5134
+ const children = blockMap?.get("children");
5135
+ if (!children) {
5136
+ return;
5137
+ }
5138
+ for (let i = 0; i < children.length; i++) {
5139
+ walk(children.get(i));
5140
+ }
5141
+ };
5142
+ for (let i = 0; i < order.length; i++) {
5143
+ walk(order.get(i));
5144
+ }
5145
+ return ids;
5146
+ }
5147
+
5090
5148
  // src/editor/range.ts
5091
5149
  var DocumentRangeImpl = class {
5092
5150
  start;
5093
5151
  end;
5094
5152
  _anchor;
5095
5153
  _focus;
5096
- _doc;
5154
+ _order;
5097
5155
  constructor(anchor, focus, doc) {
5098
5156
  this._anchor = anchor;
5099
5157
  this._focus = focus;
5100
- this._doc = doc;
5158
+ this._order = documentPreorderBlockIdsFromDoc(doc);
5101
5159
  const anchorIdx = this._indexOfBlock(anchor.blockId);
5102
5160
  const focusIdx = this._indexOfBlock(focus.blockId);
5103
5161
  if (anchorIdx < focusIdx || anchorIdx === focusIdx && (anchor.offset ?? 0) <= (focus.offset ?? 0)) {
@@ -5126,13 +5184,10 @@ var DocumentRangeImpl = class {
5126
5184
  get blockRange() {
5127
5185
  const startIdx = this._indexOfBlock(this.start.blockId);
5128
5186
  const endIdx = this._indexOfBlock(this.end.blockId);
5129
- const result = [];
5130
- for (let i = startIdx; i <= endIdx; i++) {
5131
- result.push(
5132
- this._doc.blockOrder.get(i)
5133
- );
5187
+ if (startIdx < 0 || endIdx < 0) {
5188
+ return [];
5134
5189
  }
5135
- return result;
5190
+ return this._order.slice(startIdx, endIdx + 1);
5136
5191
  }
5137
5192
  contains(point) {
5138
5193
  const idx = this._indexOfBlock(point.blockId);
@@ -5168,10 +5223,7 @@ var DocumentRangeImpl = class {
5168
5223
  };
5169
5224
  }
5170
5225
  _indexOfBlock(blockId) {
5171
- for (let i = 0; i < this._doc.blockOrder.length; i++) {
5172
- if (this._doc.blockOrder.get(i) === blockId) return i;
5173
- }
5174
- return -1;
5226
+ return this._order.indexOf(blockId);
5175
5227
  }
5176
5228
  };
5177
5229
 
@@ -5235,13 +5287,10 @@ function blockIdsFromOrder(order, anchorId, focusId) {
5235
5287
  );
5236
5288
  }
5237
5289
  function blockIdsBetween(doc, anchorId, focusId) {
5238
- const order = doc.blockOrder;
5239
- return sliceBlockIds(
5240
- indexOfBlock(order, anchorId),
5241
- indexOfBlock(order, focusId),
5290
+ return blockIdsFromOrder(
5291
+ documentPreorderBlockIdsFromDoc(doc),
5242
5292
  anchorId,
5243
- focusId,
5244
- (index) => order.get(index)
5293
+ focusId
5245
5294
  );
5246
5295
  }
5247
5296
  function sliceBlockIds(anchorIndex, focusIndex, anchorId, focusId, idAt) {
@@ -5262,14 +5311,6 @@ function sliceBlockIds(anchorIndex, focusIndex, anchorId, focusId, idAt) {
5262
5311
  }
5263
5312
  return ids;
5264
5313
  }
5265
- function indexOfBlock(order, blockId) {
5266
- for (let i = 0; i < order.length; i++) {
5267
- if (order.get(i) === blockId) {
5268
- return i;
5269
- }
5270
- }
5271
- return -1;
5272
- }
5273
5314
 
5274
5315
  // src/editor/anchorRepair.ts
5275
5316
  function sitsInMovedRange(offset, assoc, range) {
@@ -7957,7 +7998,7 @@ function readTextAnchor(editor) {
7957
7998
  return selection.anchor;
7958
7999
  }
7959
8000
  function documentOrderedTextPoints(editor, selection) {
7960
- const order = editor.documentState.blockOrder;
8001
+ const order = documentPreorderBlockIds(editor);
7961
8002
  const anchorIndex = order.indexOf(selection.anchor.blockId);
7962
8003
  const focusIndex = order.indexOf(selection.focus.blockId);
7963
8004
  if (anchorIndex < 0 || focusIndex < 0) {
@@ -7971,7 +8012,7 @@ function documentOrderedTextPoints(editor, selection) {
7971
8012
 
7972
8013
  // src/commands/commandSnapshots.ts
7973
8014
  function buildNormalPositionSnapshot(editor) {
7974
- const blockOrder = [...editor.documentState.blockOrder];
8015
+ const blockOrder = [...getVisibleBlockIds(editor)];
7975
8016
  const blocks = {};
7976
8017
  for (const blockId of blockOrder) {
7977
8018
  const block = editor.getBlock(blockId);
@@ -7992,7 +8033,7 @@ function buildNormalPositionSnapshot(editor) {
7992
8033
  return { blockOrder, blocks };
7993
8034
  }
7994
8035
  function buildTransitionSnapshot(editor) {
7995
- const blockOrder = [...editor.documentState.blockOrder];
8036
+ const blockOrder = [...getVisibleBlockIds(editor)];
7996
8037
  const blocks = {};
7997
8038
  for (const blockId of blockOrder) {
7998
8039
  const block = editor.getBlock(blockId);
@@ -8289,7 +8330,7 @@ function replaceSingleBlockRange(blockId, start, end, text, marks) {
8289
8330
  };
8290
8331
  }
8291
8332
  function replaceMultiBlockRange(editor, start, end, text, marks) {
8292
- const order = editor.documentState.blockOrder;
8333
+ const order = documentPreorderBlockIds(editor);
8293
8334
  const startIndex = order.indexOf(start.blockId);
8294
8335
  const endIndex = order.indexOf(end.blockId);
8295
8336
  if (startIndex < 0 || endIndex < 0 || startIndex >= endIndex) {
@@ -8328,7 +8369,7 @@ function replaceMultiBlockRange(editor, start, end, text, marks) {
8328
8369
  );
8329
8370
  }
8330
8371
  function replaceTextToTextRange(editor, start, end, text, marks, startIndex, endIndex, startLength) {
8331
- const order = editor.documentState.blockOrder;
8372
+ const order = documentPreorderBlockIds(editor);
8332
8373
  const ops = [];
8333
8374
  if (start.offset < startLength) {
8334
8375
  ops.push(
@@ -8390,7 +8431,7 @@ function replaceTextToTextRange(editor, start, end, text, marks, startIndex, end
8390
8431
  };
8391
8432
  }
8392
8433
  function replaceMixedBoundaryRange(editor, start, end, text, marks, startIndex, endIndex, startEditable, endEditable, startLength) {
8393
- const order = editor.documentState.blockOrder;
8434
+ const order = documentPreorderBlockIds(editor);
8394
8435
  const ops = [];
8395
8436
  if (startEditable) {
8396
8437
  if (start.offset < startLength) {
@@ -9280,11 +9321,10 @@ var multiplayerControllerFacet = singleController(
9280
9321
  );
9281
9322
  var snapshotsControllerFacet = singleController("history.controller");
9282
9323
  var assetProviderFacet = singleController("pen.assetProvider");
9283
- var toolRuntimeFacet = singleController(
9284
- "tools.toolRuntime"
9285
- );
9324
+ var toolRuntimeFacet = singleController("tools.toolRuntime");
9286
9325
  var announcerFacet = singleController("pen.announcer");
9287
9326
  var streamingTargetFacet = singleController("deltaStream.target");
9327
+ var smoothStreamControllerFacet = singleController("ai.smoothStream");
9288
9328
 
9289
9329
  // src/commands/history.ts
9290
9330
  var historyUndo = defineCommand("history.undo");
@@ -10065,7 +10105,7 @@ function toggleMarkAcrossBlocks(editor, selection, param) {
10065
10105
  if (!range) {
10066
10106
  return false;
10067
10107
  }
10068
- const order = editor.documentState.blockOrder;
10108
+ const order = documentPreorderBlockIds(editor);
10069
10109
  const startIndex = order.indexOf(range.start.blockId);
10070
10110
  const endIndex = order.indexOf(range.end.blockId);
10071
10111
  if (startIndex < 0 || endIndex < 0) {
@@ -11076,6 +11116,7 @@ var FACET_BY_SLOT_KEY = {
11076
11116
  "pen.messages": messagesFacet,
11077
11117
  "pen.a11yLabel": a11yLabelFacet,
11078
11118
  "delta-stream:target": streamingTargetFacet,
11119
+ "smooth-stream:controller": smoothStreamControllerFacet,
11079
11120
  [import_pen_types7.ANNOUNCER_SLOT_KEY]: announcerFacet
11080
11121
  };
11081
11122
  function writeAssignedSlot(self, key, value) {
@@ -14475,6 +14516,77 @@ function emitMissingBlock(editor, blockId) {
14475
14516
  emit("diagnostic", event);
14476
14517
  }
14477
14518
 
14519
+ // src/suggestion/resolveSuggestionMenuTarget.ts
14520
+ var DEFAULT_LOOKBEHIND = 80;
14521
+ function inlineLogicalText(block) {
14522
+ return logicalInline(block).text;
14523
+ }
14524
+ function resolveSuggestionMenuTarget(editor, trigger) {
14525
+ if (trigger.char.length === 0) {
14526
+ return null;
14527
+ }
14528
+ const selection = editor.selection;
14529
+ if (selection?.type !== "text" || !isCollapsed(selection)) {
14530
+ return null;
14531
+ }
14532
+ if (selection.anchor.blockId !== selection.focus.blockId) {
14533
+ return null;
14534
+ }
14535
+ const block = editor.getBlock(selection.focus.blockId);
14536
+ if (!block) {
14537
+ return null;
14538
+ }
14539
+ const offset = selection.focus.offset;
14540
+ const lookbehind = trigger.lookbehind ?? DEFAULT_LOOKBEHIND;
14541
+ const prefixStartOffset = Math.max(0, offset - lookbehind);
14542
+ const { text, atoms } = logicalInline(block);
14543
+ const textBefore = text.slice(prefixStartOffset, offset);
14544
+ const triggerIndex = textBefore.lastIndexOf(trigger.char);
14545
+ if (triggerIndex < 0) {
14546
+ return null;
14547
+ }
14548
+ if (trigger.boundary === "whitespace") {
14549
+ const previousChar = textBefore[triggerIndex - 1];
14550
+ if (previousChar && !/\s/.test(previousChar)) {
14551
+ return null;
14552
+ }
14553
+ }
14554
+ const query = textBefore.slice(triggerIndex + trigger.char.length);
14555
+ const startOffset = prefixStartOffset + triggerIndex;
14556
+ const queryStartOffset = startOffset + trigger.char.length;
14557
+ if (queryRangeContainsAtom(atoms, queryStartOffset, offset)) {
14558
+ return null;
14559
+ }
14560
+ if (!trigger.allowSpaces && /\s/.test(query)) {
14561
+ return null;
14562
+ }
14563
+ if (trigger.closingChar && query.includes(trigger.closingChar)) {
14564
+ return null;
14565
+ }
14566
+ if (query.length < (trigger.minQueryLength ?? 0)) {
14567
+ return null;
14568
+ }
14569
+ if (trigger.maxQueryLength !== void 0 && query.length > trigger.maxQueryLength) {
14570
+ return null;
14571
+ }
14572
+ if (trigger.queryPattern) {
14573
+ trigger.queryPattern.lastIndex = 0;
14574
+ if (!trigger.queryPattern.test(query)) {
14575
+ return null;
14576
+ }
14577
+ }
14578
+ return {
14579
+ blockId: selection.focus.blockId,
14580
+ startOffset,
14581
+ endOffset: offset,
14582
+ query,
14583
+ trigger: trigger.char
14584
+ };
14585
+ }
14586
+ function queryRangeContainsAtom(atoms, queryStart, queryEnd) {
14587
+ return atoms.some((atom) => atom.start < queryEnd && atom.end > queryStart);
14588
+ }
14589
+
14478
14590
  // src/commands/resolveDirectedBinding.ts
14479
14591
  function resolveDirectedBinding(editor, binding) {
14480
14592
  const direction = resolveFocusBlockDirection(editor);
@@ -15091,6 +15203,7 @@ function mapOffsetThroughSplices(splices, offset, assoc) {
15091
15203
  historyUndo,
15092
15204
  hookPriorityToPrecedence,
15093
15205
  indent,
15206
+ inlineLogicalText,
15094
15207
  inputRulesEngineFacet,
15095
15208
  inputRulesFacet,
15096
15209
  insertLineBreak,
@@ -15146,6 +15259,7 @@ function mapOffsetThroughSplices(splices, offset, assoc) {
15146
15259
  resolveSchema,
15147
15260
  resolveSchemaA11y,
15148
15261
  resolveSelectionTargetBlockIds,
15262
+ resolveSuggestionMenuTarget,
15149
15263
  runMigrations,
15150
15264
  searchControllerFacet,
15151
15265
  selectAdjacentInlineAtom,
@@ -15163,6 +15277,7 @@ function mapOffsetThroughSplices(splices, offset, assoc) {
15163
15277
  shouldShowBlockInDefaultMenus,
15164
15278
  singleController,
15165
15279
  slashMenuGroupOf,
15280
+ smoothStreamControllerFacet,
15166
15281
  snapToNormalPosition,
15167
15282
  snapshotsControllerFacet,
15168
15283
  sortDeltaAttributes,
package/dist/index.d.cts CHANGED
@@ -304,7 +304,7 @@ declare class DocumentRangeImpl implements DocumentRange {
304
304
  };
305
305
  private readonly _anchor;
306
306
  private readonly _focus;
307
- private readonly _doc;
307
+ private readonly _order;
308
308
  constructor(anchor: {
309
309
  blockId: string;
310
310
  offset?: number;
@@ -456,9 +456,10 @@ declare function createTextSelection(input: {
456
456
  declare function isCollapsed(sel: ReadonlySelectionState): boolean;
457
457
  declare function isMultiBlock(sel: ReadonlySelectionState): boolean;
458
458
  /**
459
- * Document-order block ids covered by `sel`. Pass a plain `blockOrder`
460
- * snapshot (`editor.documentState.blockOrder`) from a renderer effect —
461
- * walking a live `Y.Array` through a deep-proxied document writes back.
459
+ * Document-order block ids covered by `sel`. A live `PenDocument` walks
460
+ * nested `children` as well as top-level `blockOrder`. Pass a plain id
461
+ * snapshot from a renderer effect — walking a live `Y.Array` through a
462
+ * deep-proxied document writes back.
462
463
  */
463
464
  declare function getSelectionBlockRange(doc: PenDocument | readonly string[], sel: ReadonlySelectionState): string[];
464
465
  declare function isBlockSelected(blockOrder: readonly string[], sel: ReadonlySelectionState, blockId: string): boolean;
@@ -722,6 +723,71 @@ declare function resolveBlockDirection(editor: Editor, block: BlockHandle): Bloc
722
723
  */
723
724
  declare function blockLogicalText(editor: Editor, blockId: string): string;
724
725
 
726
+ /** Whether a trigger may sit after a non-whitespace character. */
727
+ type SuggestionMenuBoundary = "any" | "whitespace";
728
+ /**
729
+ * Match constraints for {@link resolveSuggestionMenuTarget}.
730
+ *
731
+ * Offsets are logical (N6): each inline atom is one unit.
732
+ */
733
+ interface SuggestionMenuTrigger {
734
+ /** Trigger string to find; empty `char` never matches. */
735
+ char: string;
736
+ /** Minimum query length after the trigger. @default 0 */
737
+ minQueryLength?: number;
738
+ /** Maximum query length after the trigger. @default unlimited */
739
+ maxQueryLength?: number;
740
+ /** How many logical offsets before the caret to search. @default 80 */
741
+ lookbehind?: number;
742
+ /** When false, a query containing whitespace is refused. @default false */
743
+ allowSpaces?: boolean;
744
+ /**
745
+ * `"whitespace"` requires start-of-prefix or a whitespace character before
746
+ * the trigger (an atom is not whitespace). `"any"` does not.
747
+ * @default "any"
748
+ */
749
+ boundary?: SuggestionMenuBoundary;
750
+ /** When set, a query containing this character is refused. @default none */
751
+ closingChar?: string;
752
+ /** When set, the query must match; `lastIndex` is reset first. @default none */
753
+ queryPattern?: RegExp;
754
+ }
755
+ /** Resolved trigger range in the logical offset domain (N6). */
756
+ interface SuggestionMenuTarget {
757
+ blockId: string;
758
+ startOffset: number;
759
+ endOffset: number;
760
+ query: string;
761
+ trigger: string;
762
+ }
763
+ /**
764
+ * Logical inline text of a block: stored string text plus one U+FFFC per
765
+ * inline atom, in the same offset domain as caret offsets and
766
+ * `block.length()` (N6).
767
+ *
768
+ * @param block - Live block handle to read.
769
+ * @returns The logical string. Empty blocks return `""`.
770
+ * @throws Never.
771
+ */
772
+ declare function inlineLogicalText(block: BlockHandle): string;
773
+ /**
774
+ * Resolves a collapsed caret to a suggestion-menu trigger range.
775
+ *
776
+ * Matching uses the logical offset domain (N6), not `block.textContent()`.
777
+ * Each inline atom occupies one offset (U+FFFC in {@link inlineLogicalText}).
778
+ * A query range that contains an atom is refused. A trigger immediately after
779
+ * an atom starts at the offset after that atom; `boundary: "whitespace"` still
780
+ * rejects when the preceding unit is the atom.
781
+ *
782
+ * @param editor - Editor whose collapsed text caret is read.
783
+ * @param trigger - Trigger character and match constraints. See
784
+ * {@link SuggestionMenuTrigger} for field defaults.
785
+ * @returns The trigger range in logical offsets, or `null` when the caret is
786
+ * not a collapsed in-block text selection or the prefix does not match.
787
+ * @throws Never. Non-matches return `null`.
788
+ */
789
+ declare function resolveSuggestionMenuTarget(editor: Editor, trigger: SuggestionMenuTrigger): SuggestionMenuTarget | null;
790
+
725
791
  type DefaultKeymapContext = "text" | "cell" | "block" | "any";
726
792
  type KeymapPlatform = "macos" | "windows" | "linux";
727
793
  interface DefaultKeymapBinding {
@@ -1036,6 +1102,7 @@ declare const assetProviderFacet: _input_pen_types.Facet<unknown, unknown>;
1036
1102
  declare const toolRuntimeFacet: _input_pen_types.Facet<unknown, unknown>;
1037
1103
  declare const announcerFacet: _input_pen_types.Facet<unknown, unknown>;
1038
1104
  declare const streamingTargetFacet: _input_pen_types.Facet<unknown, unknown>;
1105
+ declare const smoothStreamControllerFacet: _input_pen_types.Facet<unknown, unknown>;
1039
1106
 
1040
1107
  declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "blockText" | "structural">, documentOrder?: readonly string[]): string[];
1041
1108
 
@@ -1048,4 +1115,4 @@ declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "block
1048
1115
  */
1049
1116
  declare function mapOffsetThroughSplices(splices: readonly TextSplice[], offset: number, assoc: Assoc): number;
1050
1117
 
1051
- export { A11Y_MISSING_LABEL_CODE, APPLY_STORM_CODE, APPLY_STORM_QUEUE_LIMIT, type BeforeApplyHook, type BlockDirection, type BlockDirectionResolver, type BlockDirectionSetting, type CaretMotionParam, type CellCaretFocus, type CellCaretWrite, type ClipboardHandler, type CommandDispatchContext, type CommandHandlerTable, type CommandRegistry, type ContentMove, type ConvertBlockParam, type CreateCommandRegistryOptions, type CreateFacetRegistryOptions, type CreateHeadlessEditorOptions, type DecorationSource, type DefaultKeymapBinding, type DefaultKeymapContext, type DefinedBlockSchema, type DeleteGranularity, type DeleteParam, type DocumentMigration, DocumentRangeImpl, type EditorA11yLabelAttrs, EventEmitter, ExtensionManagerImpl, type FacetRegistry, type FacetSettleInput, HOOK_PRIORITIES, type InsertTextParam, type Keymap, type KeymapPlatform, type MigrationReport, type NormalPositionSnapshot, PIPELINE_PHASES, PSEUDO_LOCALE_CLOSE, PSEUDO_LOCALE_OPEN, type PendingBlock, type PendingBlockImportPolicyViolation, type PendingBlockProfilePolicyViolation, type ProfilePolicyViolation, type SchemaA11yAttrs, type SchemaA11yKind, SchemaEngineImpl, type SchemaRegistryConfig, SchemaRegistryImpl, type SelectBlockParam, SelectionAuthorityImpl as SelectionAuthority, type StructureBlockParam, type ToggleMarkParam, type UrlContext, type UrlPolicy, type VerticalCaretDirection, type VerticalCaretMeasure, type VerticalCaretMeasureResult, type VerticalCaretPoint, type WordRange, a11yLabelFacet, affectedBlockIdsFromSummary, aiAutocompleteControllerFacet, aiControllerFacet, aiEgressExtension, aiEgressFacet, aiInlineCompletionFacet, aiInlineHistoryFacet, aiReviewControllerFacet, aiSuggestionsControllerFacet, announceEditorA11y, announcerFacet, applyDirectedBinding, applyMergeBlocks, applySplitBlock, ariaReadOnlyFacet, assetProviderFacet, beforeApplyFacet, blockDirectionFacet, blockLogicalText, blocksToOps, buildMergeBlocksRecipe, buildNormalPositionSnapshot, buildSplitBlockRecipe, buildTableChildren, builtinCommandHandlers, caretBlockEnd, caretBlockStart, caretDocEnd, caretDocStart, caretDown, caretLeft, caretLineEnd, caretLineStart, caretRight, caretUp, caretWordLeft, caretWordRight, clipboardFacet, collectEditorKeyBindings, collectToolExecutionOutput, commandHandler, commandsFacet, convertBlock, convertBlockOps, createAppHandle, createBlockHandle, createCommandRegistry, createDecorationSet, createDocumentSession, createEditor, createEmptySchema, createFacetRegistry, createHeadlessEditor, createImportResult, createMutationGroupMetadata, createPseudoLocaleCatalog, createTextSelection, decorationsFacet, deepEqual, defaultDirectionFacet, defaultKeymapBindings, defineBlock, defineCommand, defineExtension, defineFacet, delegatesToGridEditing, deleteAdjacentInlineAtom, deleteBackward, deleteBlock, deleteForward, deriveContentMoves, duplicateBlock, emptyDecorationSet, ensureInlineCompletionController, fieldEditorHostFacet, filterAIRequest, filterOpsForDocumentProfile, filterPendingBlocksForDocumentProfile, foldAndNormalize, getApplyOptionsGroupId, getBlockSelectionRoleFromSchema, getBlockSelectionRoleFromType, getCellCaretFocus, getCommandRegistry, getEditorSelectionRecord, getFlowCapabilityFromSchema, getFlowCapabilityFromType, getInlineCompletionController, getNumberedListItemValue, getOpOriginGroupId, getOpOriginType, getSelectionBlockRange, getVerticalCaretGoalX, getVerticalCaretMeasure, hasFieldEditorSurface, hasIndexedCellSelectionMetadata, historyRedo, historyUndo, hookPriorityToPrecedence, indent, inputRulesEngineFacet, inputRulesFacet, insertLineBreak, insertText, interpolateMessage, isBlockSelected, isCollapsed, isContainerBlock, isContainerBlockType, isContinuousTextFlowCapability, isMultiBlock, isPseudoLocaleText, keyBindingPriorityToPrecedence, keymapFacet, localeFacet, mapOffsetThroughSplices, mergeDecorationSets, mergeSchemas, messagesFacet, moveBlockDown, moveBlockUp, multiplayerControllerFacet, nextGraphemeBoundary, nextWordBoundary, normalizePendingBlocksForImport, orderSlashMenuItemsByGroup, outdent, previousGraphemeBoundary, previousWordBoundary, priorityToPrecedence, prop, renderSelectionTargetBlockText, renderSelectionTargetText, repairAnchor, reportPendingBlockImportViolations, reportPendingBlockProfileViolations, resolveA11yBlockTypeLabel, resolveA11ySpec, resolveBlockDirection, resolveBlockFlowCapability, resolveCellSelectionCoord, resolveCellSelectionMatrix, resolveDefaultKeymap, resolveDirectedBinding, resolveDirectedCommand, resolveEditorA11yLabel, resolveEditorMessage, resolveEditorSchema, resolveFieldEditorBehavior, resolveFieldEditorInputMode, resolveFocusBlockDirection, resolveMessage, resolveSchema, resolveSchemaA11y, resolveSelectionTargetBlockIds, runMigrations, searchControllerFacet, selectAdjacentInlineAtom, selectAll, selectBlock, selectionToRange, setCellCaretFocus, setVerticalCaretGoalX, setVerticalCaretMeasure, shouldAllowDirectBlockPaste, shouldAllowFlowInsertionInSlashMenu, shouldExposeBlockInTooling, shouldForceBlockScopedSelectAll, shouldRenderContainerChildren, shouldShowBlockInDefaultMenus, singleController, slashMenuGroupOf, snapToNormalPosition, snapshotsControllerFacet, sortDeltaAttributes, spliceDeleteOp, spliceInsertOp, splitBlock, streamThroughEgress, streamingTargetFacet, suggestion, supportsInlineInputRules, supportsInlineMarks, tableCellDown, tableCellNext, tableCellPrev, tableEscapeGrid, toPseudoLocaleText, toggleMark, toolRuntimeFacet, undoManagerFacet, undoMetadataControllerFacet, undoRestoreControllerFacet, urlPolicy, urlPolicyFacet, usesInlineTextSelection, wordRangeAt };
1118
+ export { A11Y_MISSING_LABEL_CODE, APPLY_STORM_CODE, APPLY_STORM_QUEUE_LIMIT, type BeforeApplyHook, type BlockDirection, type BlockDirectionResolver, type BlockDirectionSetting, type CaretMotionParam, type CellCaretFocus, type CellCaretWrite, type ClipboardHandler, type CommandDispatchContext, type CommandHandlerTable, type CommandRegistry, type ContentMove, type ConvertBlockParam, type CreateCommandRegistryOptions, type CreateFacetRegistryOptions, type CreateHeadlessEditorOptions, type DecorationSource, type DefaultKeymapBinding, type DefaultKeymapContext, type DefinedBlockSchema, type DeleteGranularity, type DeleteParam, type DocumentMigration, DocumentRangeImpl, type EditorA11yLabelAttrs, EventEmitter, ExtensionManagerImpl, type FacetRegistry, type FacetSettleInput, HOOK_PRIORITIES, type InsertTextParam, type Keymap, type KeymapPlatform, type MigrationReport, type NormalPositionSnapshot, PIPELINE_PHASES, PSEUDO_LOCALE_CLOSE, PSEUDO_LOCALE_OPEN, type PendingBlock, type PendingBlockImportPolicyViolation, type PendingBlockProfilePolicyViolation, type ProfilePolicyViolation, type SchemaA11yAttrs, type SchemaA11yKind, SchemaEngineImpl, type SchemaRegistryConfig, SchemaRegistryImpl, type SelectBlockParam, SelectionAuthorityImpl as SelectionAuthority, type StructureBlockParam, type SuggestionMenuBoundary, type SuggestionMenuTarget, type SuggestionMenuTrigger, type ToggleMarkParam, type UrlContext, type UrlPolicy, type VerticalCaretDirection, type VerticalCaretMeasure, type VerticalCaretMeasureResult, type VerticalCaretPoint, type WordRange, a11yLabelFacet, affectedBlockIdsFromSummary, aiAutocompleteControllerFacet, aiControllerFacet, aiEgressExtension, aiEgressFacet, aiInlineCompletionFacet, aiInlineHistoryFacet, aiReviewControllerFacet, aiSuggestionsControllerFacet, announceEditorA11y, announcerFacet, applyDirectedBinding, applyMergeBlocks, applySplitBlock, ariaReadOnlyFacet, assetProviderFacet, beforeApplyFacet, blockDirectionFacet, blockLogicalText, blocksToOps, buildMergeBlocksRecipe, buildNormalPositionSnapshot, buildSplitBlockRecipe, buildTableChildren, builtinCommandHandlers, caretBlockEnd, caretBlockStart, caretDocEnd, caretDocStart, caretDown, caretLeft, caretLineEnd, caretLineStart, caretRight, caretUp, caretWordLeft, caretWordRight, clipboardFacet, collectEditorKeyBindings, collectToolExecutionOutput, commandHandler, commandsFacet, convertBlock, convertBlockOps, createAppHandle, createBlockHandle, createCommandRegistry, createDecorationSet, createDocumentSession, createEditor, createEmptySchema, createFacetRegistry, createHeadlessEditor, createImportResult, createMutationGroupMetadata, createPseudoLocaleCatalog, createTextSelection, decorationsFacet, deepEqual, defaultDirectionFacet, defaultKeymapBindings, defineBlock, defineCommand, defineExtension, defineFacet, delegatesToGridEditing, deleteAdjacentInlineAtom, deleteBackward, deleteBlock, deleteForward, deriveContentMoves, duplicateBlock, emptyDecorationSet, ensureInlineCompletionController, fieldEditorHostFacet, filterAIRequest, filterOpsForDocumentProfile, filterPendingBlocksForDocumentProfile, foldAndNormalize, getApplyOptionsGroupId, getBlockSelectionRoleFromSchema, getBlockSelectionRoleFromType, getCellCaretFocus, getCommandRegistry, getEditorSelectionRecord, getFlowCapabilityFromSchema, getFlowCapabilityFromType, getInlineCompletionController, getNumberedListItemValue, getOpOriginGroupId, getOpOriginType, getSelectionBlockRange, getVerticalCaretGoalX, getVerticalCaretMeasure, hasFieldEditorSurface, hasIndexedCellSelectionMetadata, historyRedo, historyUndo, hookPriorityToPrecedence, indent, inlineLogicalText, inputRulesEngineFacet, inputRulesFacet, insertLineBreak, insertText, interpolateMessage, isBlockSelected, isCollapsed, isContainerBlock, isContainerBlockType, isContinuousTextFlowCapability, isMultiBlock, isPseudoLocaleText, keyBindingPriorityToPrecedence, keymapFacet, localeFacet, mapOffsetThroughSplices, mergeDecorationSets, mergeSchemas, messagesFacet, moveBlockDown, moveBlockUp, multiplayerControllerFacet, nextGraphemeBoundary, nextWordBoundary, normalizePendingBlocksForImport, orderSlashMenuItemsByGroup, outdent, previousGraphemeBoundary, previousWordBoundary, priorityToPrecedence, prop, renderSelectionTargetBlockText, renderSelectionTargetText, repairAnchor, reportPendingBlockImportViolations, reportPendingBlockProfileViolations, resolveA11yBlockTypeLabel, resolveA11ySpec, resolveBlockDirection, resolveBlockFlowCapability, resolveCellSelectionCoord, resolveCellSelectionMatrix, resolveDefaultKeymap, resolveDirectedBinding, resolveDirectedCommand, resolveEditorA11yLabel, resolveEditorMessage, resolveEditorSchema, resolveFieldEditorBehavior, resolveFieldEditorInputMode, resolveFocusBlockDirection, resolveMessage, resolveSchema, resolveSchemaA11y, resolveSelectionTargetBlockIds, resolveSuggestionMenuTarget, runMigrations, searchControllerFacet, selectAdjacentInlineAtom, selectAll, selectBlock, selectionToRange, setCellCaretFocus, setVerticalCaretGoalX, setVerticalCaretMeasure, shouldAllowDirectBlockPaste, shouldAllowFlowInsertionInSlashMenu, shouldExposeBlockInTooling, shouldForceBlockScopedSelectAll, shouldRenderContainerChildren, shouldShowBlockInDefaultMenus, singleController, slashMenuGroupOf, smoothStreamControllerFacet, snapToNormalPosition, snapshotsControllerFacet, sortDeltaAttributes, spliceDeleteOp, spliceInsertOp, splitBlock, streamThroughEgress, streamingTargetFacet, suggestion, supportsInlineInputRules, supportsInlineMarks, tableCellDown, tableCellNext, tableCellPrev, tableEscapeGrid, toPseudoLocaleText, toggleMark, toolRuntimeFacet, undoManagerFacet, undoMetadataControllerFacet, undoRestoreControllerFacet, urlPolicy, urlPolicyFacet, usesInlineTextSelection, wordRangeAt };
package/dist/index.d.ts CHANGED
@@ -304,7 +304,7 @@ declare class DocumentRangeImpl implements DocumentRange {
304
304
  };
305
305
  private readonly _anchor;
306
306
  private readonly _focus;
307
- private readonly _doc;
307
+ private readonly _order;
308
308
  constructor(anchor: {
309
309
  blockId: string;
310
310
  offset?: number;
@@ -456,9 +456,10 @@ declare function createTextSelection(input: {
456
456
  declare function isCollapsed(sel: ReadonlySelectionState): boolean;
457
457
  declare function isMultiBlock(sel: ReadonlySelectionState): boolean;
458
458
  /**
459
- * Document-order block ids covered by `sel`. Pass a plain `blockOrder`
460
- * snapshot (`editor.documentState.blockOrder`) from a renderer effect —
461
- * walking a live `Y.Array` through a deep-proxied document writes back.
459
+ * Document-order block ids covered by `sel`. A live `PenDocument` walks
460
+ * nested `children` as well as top-level `blockOrder`. Pass a plain id
461
+ * snapshot from a renderer effect — walking a live `Y.Array` through a
462
+ * deep-proxied document writes back.
462
463
  */
463
464
  declare function getSelectionBlockRange(doc: PenDocument | readonly string[], sel: ReadonlySelectionState): string[];
464
465
  declare function isBlockSelected(blockOrder: readonly string[], sel: ReadonlySelectionState, blockId: string): boolean;
@@ -722,6 +723,71 @@ declare function resolveBlockDirection(editor: Editor, block: BlockHandle): Bloc
722
723
  */
723
724
  declare function blockLogicalText(editor: Editor, blockId: string): string;
724
725
 
726
+ /** Whether a trigger may sit after a non-whitespace character. */
727
+ type SuggestionMenuBoundary = "any" | "whitespace";
728
+ /**
729
+ * Match constraints for {@link resolveSuggestionMenuTarget}.
730
+ *
731
+ * Offsets are logical (N6): each inline atom is one unit.
732
+ */
733
+ interface SuggestionMenuTrigger {
734
+ /** Trigger string to find; empty `char` never matches. */
735
+ char: string;
736
+ /** Minimum query length after the trigger. @default 0 */
737
+ minQueryLength?: number;
738
+ /** Maximum query length after the trigger. @default unlimited */
739
+ maxQueryLength?: number;
740
+ /** How many logical offsets before the caret to search. @default 80 */
741
+ lookbehind?: number;
742
+ /** When false, a query containing whitespace is refused. @default false */
743
+ allowSpaces?: boolean;
744
+ /**
745
+ * `"whitespace"` requires start-of-prefix or a whitespace character before
746
+ * the trigger (an atom is not whitespace). `"any"` does not.
747
+ * @default "any"
748
+ */
749
+ boundary?: SuggestionMenuBoundary;
750
+ /** When set, a query containing this character is refused. @default none */
751
+ closingChar?: string;
752
+ /** When set, the query must match; `lastIndex` is reset first. @default none */
753
+ queryPattern?: RegExp;
754
+ }
755
+ /** Resolved trigger range in the logical offset domain (N6). */
756
+ interface SuggestionMenuTarget {
757
+ blockId: string;
758
+ startOffset: number;
759
+ endOffset: number;
760
+ query: string;
761
+ trigger: string;
762
+ }
763
+ /**
764
+ * Logical inline text of a block: stored string text plus one U+FFFC per
765
+ * inline atom, in the same offset domain as caret offsets and
766
+ * `block.length()` (N6).
767
+ *
768
+ * @param block - Live block handle to read.
769
+ * @returns The logical string. Empty blocks return `""`.
770
+ * @throws Never.
771
+ */
772
+ declare function inlineLogicalText(block: BlockHandle): string;
773
+ /**
774
+ * Resolves a collapsed caret to a suggestion-menu trigger range.
775
+ *
776
+ * Matching uses the logical offset domain (N6), not `block.textContent()`.
777
+ * Each inline atom occupies one offset (U+FFFC in {@link inlineLogicalText}).
778
+ * A query range that contains an atom is refused. A trigger immediately after
779
+ * an atom starts at the offset after that atom; `boundary: "whitespace"` still
780
+ * rejects when the preceding unit is the atom.
781
+ *
782
+ * @param editor - Editor whose collapsed text caret is read.
783
+ * @param trigger - Trigger character and match constraints. See
784
+ * {@link SuggestionMenuTrigger} for field defaults.
785
+ * @returns The trigger range in logical offsets, or `null` when the caret is
786
+ * not a collapsed in-block text selection or the prefix does not match.
787
+ * @throws Never. Non-matches return `null`.
788
+ */
789
+ declare function resolveSuggestionMenuTarget(editor: Editor, trigger: SuggestionMenuTrigger): SuggestionMenuTarget | null;
790
+
725
791
  type DefaultKeymapContext = "text" | "cell" | "block" | "any";
726
792
  type KeymapPlatform = "macos" | "windows" | "linux";
727
793
  interface DefaultKeymapBinding {
@@ -1036,6 +1102,7 @@ declare const assetProviderFacet: _input_pen_types.Facet<unknown, unknown>;
1036
1102
  declare const toolRuntimeFacet: _input_pen_types.Facet<unknown, unknown>;
1037
1103
  declare const announcerFacet: _input_pen_types.Facet<unknown, unknown>;
1038
1104
  declare const streamingTargetFacet: _input_pen_types.Facet<unknown, unknown>;
1105
+ declare const smoothStreamControllerFacet: _input_pen_types.Facet<unknown, unknown>;
1039
1106
 
1040
1107
  declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "blockText" | "structural">, documentOrder?: readonly string[]): string[];
1041
1108
 
@@ -1048,4 +1115,4 @@ declare function affectedBlockIdsFromSummary(summary: Pick<ChangeSummary, "block
1048
1115
  */
1049
1116
  declare function mapOffsetThroughSplices(splices: readonly TextSplice[], offset: number, assoc: Assoc): number;
1050
1117
 
1051
- export { A11Y_MISSING_LABEL_CODE, APPLY_STORM_CODE, APPLY_STORM_QUEUE_LIMIT, type BeforeApplyHook, type BlockDirection, type BlockDirectionResolver, type BlockDirectionSetting, type CaretMotionParam, type CellCaretFocus, type CellCaretWrite, type ClipboardHandler, type CommandDispatchContext, type CommandHandlerTable, type CommandRegistry, type ContentMove, type ConvertBlockParam, type CreateCommandRegistryOptions, type CreateFacetRegistryOptions, type CreateHeadlessEditorOptions, type DecorationSource, type DefaultKeymapBinding, type DefaultKeymapContext, type DefinedBlockSchema, type DeleteGranularity, type DeleteParam, type DocumentMigration, DocumentRangeImpl, type EditorA11yLabelAttrs, EventEmitter, ExtensionManagerImpl, type FacetRegistry, type FacetSettleInput, HOOK_PRIORITIES, type InsertTextParam, type Keymap, type KeymapPlatform, type MigrationReport, type NormalPositionSnapshot, PIPELINE_PHASES, PSEUDO_LOCALE_CLOSE, PSEUDO_LOCALE_OPEN, type PendingBlock, type PendingBlockImportPolicyViolation, type PendingBlockProfilePolicyViolation, type ProfilePolicyViolation, type SchemaA11yAttrs, type SchemaA11yKind, SchemaEngineImpl, type SchemaRegistryConfig, SchemaRegistryImpl, type SelectBlockParam, SelectionAuthorityImpl as SelectionAuthority, type StructureBlockParam, type ToggleMarkParam, type UrlContext, type UrlPolicy, type VerticalCaretDirection, type VerticalCaretMeasure, type VerticalCaretMeasureResult, type VerticalCaretPoint, type WordRange, a11yLabelFacet, affectedBlockIdsFromSummary, aiAutocompleteControllerFacet, aiControllerFacet, aiEgressExtension, aiEgressFacet, aiInlineCompletionFacet, aiInlineHistoryFacet, aiReviewControllerFacet, aiSuggestionsControllerFacet, announceEditorA11y, announcerFacet, applyDirectedBinding, applyMergeBlocks, applySplitBlock, ariaReadOnlyFacet, assetProviderFacet, beforeApplyFacet, blockDirectionFacet, blockLogicalText, blocksToOps, buildMergeBlocksRecipe, buildNormalPositionSnapshot, buildSplitBlockRecipe, buildTableChildren, builtinCommandHandlers, caretBlockEnd, caretBlockStart, caretDocEnd, caretDocStart, caretDown, caretLeft, caretLineEnd, caretLineStart, caretRight, caretUp, caretWordLeft, caretWordRight, clipboardFacet, collectEditorKeyBindings, collectToolExecutionOutput, commandHandler, commandsFacet, convertBlock, convertBlockOps, createAppHandle, createBlockHandle, createCommandRegistry, createDecorationSet, createDocumentSession, createEditor, createEmptySchema, createFacetRegistry, createHeadlessEditor, createImportResult, createMutationGroupMetadata, createPseudoLocaleCatalog, createTextSelection, decorationsFacet, deepEqual, defaultDirectionFacet, defaultKeymapBindings, defineBlock, defineCommand, defineExtension, defineFacet, delegatesToGridEditing, deleteAdjacentInlineAtom, deleteBackward, deleteBlock, deleteForward, deriveContentMoves, duplicateBlock, emptyDecorationSet, ensureInlineCompletionController, fieldEditorHostFacet, filterAIRequest, filterOpsForDocumentProfile, filterPendingBlocksForDocumentProfile, foldAndNormalize, getApplyOptionsGroupId, getBlockSelectionRoleFromSchema, getBlockSelectionRoleFromType, getCellCaretFocus, getCommandRegistry, getEditorSelectionRecord, getFlowCapabilityFromSchema, getFlowCapabilityFromType, getInlineCompletionController, getNumberedListItemValue, getOpOriginGroupId, getOpOriginType, getSelectionBlockRange, getVerticalCaretGoalX, getVerticalCaretMeasure, hasFieldEditorSurface, hasIndexedCellSelectionMetadata, historyRedo, historyUndo, hookPriorityToPrecedence, indent, inputRulesEngineFacet, inputRulesFacet, insertLineBreak, insertText, interpolateMessage, isBlockSelected, isCollapsed, isContainerBlock, isContainerBlockType, isContinuousTextFlowCapability, isMultiBlock, isPseudoLocaleText, keyBindingPriorityToPrecedence, keymapFacet, localeFacet, mapOffsetThroughSplices, mergeDecorationSets, mergeSchemas, messagesFacet, moveBlockDown, moveBlockUp, multiplayerControllerFacet, nextGraphemeBoundary, nextWordBoundary, normalizePendingBlocksForImport, orderSlashMenuItemsByGroup, outdent, previousGraphemeBoundary, previousWordBoundary, priorityToPrecedence, prop, renderSelectionTargetBlockText, renderSelectionTargetText, repairAnchor, reportPendingBlockImportViolations, reportPendingBlockProfileViolations, resolveA11yBlockTypeLabel, resolveA11ySpec, resolveBlockDirection, resolveBlockFlowCapability, resolveCellSelectionCoord, resolveCellSelectionMatrix, resolveDefaultKeymap, resolveDirectedBinding, resolveDirectedCommand, resolveEditorA11yLabel, resolveEditorMessage, resolveEditorSchema, resolveFieldEditorBehavior, resolveFieldEditorInputMode, resolveFocusBlockDirection, resolveMessage, resolveSchema, resolveSchemaA11y, resolveSelectionTargetBlockIds, runMigrations, searchControllerFacet, selectAdjacentInlineAtom, selectAll, selectBlock, selectionToRange, setCellCaretFocus, setVerticalCaretGoalX, setVerticalCaretMeasure, shouldAllowDirectBlockPaste, shouldAllowFlowInsertionInSlashMenu, shouldExposeBlockInTooling, shouldForceBlockScopedSelectAll, shouldRenderContainerChildren, shouldShowBlockInDefaultMenus, singleController, slashMenuGroupOf, snapToNormalPosition, snapshotsControllerFacet, sortDeltaAttributes, spliceDeleteOp, spliceInsertOp, splitBlock, streamThroughEgress, streamingTargetFacet, suggestion, supportsInlineInputRules, supportsInlineMarks, tableCellDown, tableCellNext, tableCellPrev, tableEscapeGrid, toPseudoLocaleText, toggleMark, toolRuntimeFacet, undoManagerFacet, undoMetadataControllerFacet, undoRestoreControllerFacet, urlPolicy, urlPolicyFacet, usesInlineTextSelection, wordRangeAt };
1118
+ export { A11Y_MISSING_LABEL_CODE, APPLY_STORM_CODE, APPLY_STORM_QUEUE_LIMIT, type BeforeApplyHook, type BlockDirection, type BlockDirectionResolver, type BlockDirectionSetting, type CaretMotionParam, type CellCaretFocus, type CellCaretWrite, type ClipboardHandler, type CommandDispatchContext, type CommandHandlerTable, type CommandRegistry, type ContentMove, type ConvertBlockParam, type CreateCommandRegistryOptions, type CreateFacetRegistryOptions, type CreateHeadlessEditorOptions, type DecorationSource, type DefaultKeymapBinding, type DefaultKeymapContext, type DefinedBlockSchema, type DeleteGranularity, type DeleteParam, type DocumentMigration, DocumentRangeImpl, type EditorA11yLabelAttrs, EventEmitter, ExtensionManagerImpl, type FacetRegistry, type FacetSettleInput, HOOK_PRIORITIES, type InsertTextParam, type Keymap, type KeymapPlatform, type MigrationReport, type NormalPositionSnapshot, PIPELINE_PHASES, PSEUDO_LOCALE_CLOSE, PSEUDO_LOCALE_OPEN, type PendingBlock, type PendingBlockImportPolicyViolation, type PendingBlockProfilePolicyViolation, type ProfilePolicyViolation, type SchemaA11yAttrs, type SchemaA11yKind, SchemaEngineImpl, type SchemaRegistryConfig, SchemaRegistryImpl, type SelectBlockParam, SelectionAuthorityImpl as SelectionAuthority, type StructureBlockParam, type SuggestionMenuBoundary, type SuggestionMenuTarget, type SuggestionMenuTrigger, type ToggleMarkParam, type UrlContext, type UrlPolicy, type VerticalCaretDirection, type VerticalCaretMeasure, type VerticalCaretMeasureResult, type VerticalCaretPoint, type WordRange, a11yLabelFacet, affectedBlockIdsFromSummary, aiAutocompleteControllerFacet, aiControllerFacet, aiEgressExtension, aiEgressFacet, aiInlineCompletionFacet, aiInlineHistoryFacet, aiReviewControllerFacet, aiSuggestionsControllerFacet, announceEditorA11y, announcerFacet, applyDirectedBinding, applyMergeBlocks, applySplitBlock, ariaReadOnlyFacet, assetProviderFacet, beforeApplyFacet, blockDirectionFacet, blockLogicalText, blocksToOps, buildMergeBlocksRecipe, buildNormalPositionSnapshot, buildSplitBlockRecipe, buildTableChildren, builtinCommandHandlers, caretBlockEnd, caretBlockStart, caretDocEnd, caretDocStart, caretDown, caretLeft, caretLineEnd, caretLineStart, caretRight, caretUp, caretWordLeft, caretWordRight, clipboardFacet, collectEditorKeyBindings, collectToolExecutionOutput, commandHandler, commandsFacet, convertBlock, convertBlockOps, createAppHandle, createBlockHandle, createCommandRegistry, createDecorationSet, createDocumentSession, createEditor, createEmptySchema, createFacetRegistry, createHeadlessEditor, createImportResult, createMutationGroupMetadata, createPseudoLocaleCatalog, createTextSelection, decorationsFacet, deepEqual, defaultDirectionFacet, defaultKeymapBindings, defineBlock, defineCommand, defineExtension, defineFacet, delegatesToGridEditing, deleteAdjacentInlineAtom, deleteBackward, deleteBlock, deleteForward, deriveContentMoves, duplicateBlock, emptyDecorationSet, ensureInlineCompletionController, fieldEditorHostFacet, filterAIRequest, filterOpsForDocumentProfile, filterPendingBlocksForDocumentProfile, foldAndNormalize, getApplyOptionsGroupId, getBlockSelectionRoleFromSchema, getBlockSelectionRoleFromType, getCellCaretFocus, getCommandRegistry, getEditorSelectionRecord, getFlowCapabilityFromSchema, getFlowCapabilityFromType, getInlineCompletionController, getNumberedListItemValue, getOpOriginGroupId, getOpOriginType, getSelectionBlockRange, getVerticalCaretGoalX, getVerticalCaretMeasure, hasFieldEditorSurface, hasIndexedCellSelectionMetadata, historyRedo, historyUndo, hookPriorityToPrecedence, indent, inlineLogicalText, inputRulesEngineFacet, inputRulesFacet, insertLineBreak, insertText, interpolateMessage, isBlockSelected, isCollapsed, isContainerBlock, isContainerBlockType, isContinuousTextFlowCapability, isMultiBlock, isPseudoLocaleText, keyBindingPriorityToPrecedence, keymapFacet, localeFacet, mapOffsetThroughSplices, mergeDecorationSets, mergeSchemas, messagesFacet, moveBlockDown, moveBlockUp, multiplayerControllerFacet, nextGraphemeBoundary, nextWordBoundary, normalizePendingBlocksForImport, orderSlashMenuItemsByGroup, outdent, previousGraphemeBoundary, previousWordBoundary, priorityToPrecedence, prop, renderSelectionTargetBlockText, renderSelectionTargetText, repairAnchor, reportPendingBlockImportViolations, reportPendingBlockProfileViolations, resolveA11yBlockTypeLabel, resolveA11ySpec, resolveBlockDirection, resolveBlockFlowCapability, resolveCellSelectionCoord, resolveCellSelectionMatrix, resolveDefaultKeymap, resolveDirectedBinding, resolveDirectedCommand, resolveEditorA11yLabel, resolveEditorMessage, resolveEditorSchema, resolveFieldEditorBehavior, resolveFieldEditorInputMode, resolveFocusBlockDirection, resolveMessage, resolveSchema, resolveSchemaA11y, resolveSelectionTargetBlockIds, resolveSuggestionMenuTarget, runMigrations, searchControllerFacet, selectAdjacentInlineAtom, selectAll, selectBlock, selectionToRange, setCellCaretFocus, setVerticalCaretGoalX, setVerticalCaretMeasure, shouldAllowDirectBlockPaste, shouldAllowFlowInsertionInSlashMenu, shouldExposeBlockInTooling, shouldForceBlockScopedSelectAll, shouldRenderContainerChildren, shouldShowBlockInDefaultMenus, singleController, slashMenuGroupOf, smoothStreamControllerFacet, snapToNormalPosition, snapshotsControllerFacet, sortDeltaAttributes, spliceDeleteOp, spliceInsertOp, splitBlock, streamThroughEgress, streamingTargetFacet, suggestion, supportsInlineInputRules, supportsInlineMarks, tableCellDown, tableCellNext, tableCellPrev, tableEscapeGrid, toPseudoLocaleText, toggleMark, toolRuntimeFacet, undoManagerFacet, undoMetadataControllerFacet, undoRestoreControllerFacet, urlPolicy, urlPolicyFacet, usesInlineTextSelection, wordRangeAt };
package/dist/index.mjs CHANGED
@@ -353,6 +353,47 @@ function filterOpsForDocumentProfile(ops, documentProfile, registry) {
353
353
  };
354
354
  }
355
355
 
356
+ // src/schema/generateValidator.ts
357
+ function generateValidator(propSchemas) {
358
+ return (raw) => {
359
+ const result = {};
360
+ for (const [key, schema] of Object.entries(propSchemas)) {
361
+ let value = raw[key];
362
+ if (value === void 0 || value === null) {
363
+ result[key] = schema.default;
364
+ continue;
365
+ }
366
+ const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type;
367
+ if (schemaType === "number" && typeof value === "string") {
368
+ const parsed = Number(value);
369
+ if (!Number.isNaN(parsed)) value = parsed;
370
+ }
371
+ if (schemaType === "boolean" && typeof value === "string") {
372
+ value = value === "true";
373
+ }
374
+ const matchesSchemaType = schemaType === "array" ? Array.isArray(value) : typeof value === schemaType;
375
+ if (schema.type && schemaType !== void 0 && !matchesSchemaType) {
376
+ result[key] = schema.default;
377
+ continue;
378
+ }
379
+ if (typeof value === "number") {
380
+ if (schema.minimum !== void 0 && value < schema.minimum) {
381
+ value = schema.minimum;
382
+ }
383
+ if (typeof value === "number" && schema.maximum !== void 0 && value > schema.maximum) {
384
+ value = schema.maximum;
385
+ }
386
+ }
387
+ if (schema.enum && !schema.enum.includes(value)) {
388
+ result[key] = schema.default;
389
+ continue;
390
+ }
391
+ result[key] = value;
392
+ }
393
+ return result;
394
+ };
395
+ }
396
+
356
397
  // src/schema/prop.ts
357
398
  var PropChainImpl = class {
358
399
  _schema;
@@ -659,6 +700,10 @@ var SchemaRegistryImpl = class _SchemaRegistryImpl {
659
700
  if (patch.serialize) {
660
701
  merged.serialize = { ...existing.serialize, ...patch.serialize };
661
702
  }
703
+ if ("propSchema" in patch && !("validateProps" in patch)) {
704
+ const propSchema = merged.propSchema ?? {};
705
+ merged.validateProps = Object.keys(propSchema).length > 0 ? generateValidator(propSchema) : void 0;
706
+ }
662
707
  const blocks = new Map(this._blocks);
663
708
  blocks.set(type, merged);
664
709
  return new _SchemaRegistryImpl({
@@ -753,45 +798,6 @@ function generateAIDescription(type, props) {
753
798
  }).join(", ");
754
799
  return `${type}: ${propDescriptions}`;
755
800
  }
756
- function generateValidator(propSchemas) {
757
- return (raw) => {
758
- const result = {};
759
- for (const [key, schema] of Object.entries(propSchemas)) {
760
- let value = raw[key];
761
- if (value === void 0 || value === null) {
762
- result[key] = schema.default;
763
- continue;
764
- }
765
- const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type;
766
- if (schemaType === "number" && typeof value === "string") {
767
- const parsed = Number(value);
768
- if (!Number.isNaN(parsed)) value = parsed;
769
- }
770
- if (schemaType === "boolean" && typeof value === "string") {
771
- value = value === "true";
772
- }
773
- const matchesSchemaType = schemaType === "array" ? Array.isArray(value) : typeof value === schemaType;
774
- if (schema.type && schemaType !== void 0 && !matchesSchemaType) {
775
- result[key] = schema.default;
776
- continue;
777
- }
778
- if (typeof value === "number") {
779
- if (schema.minimum !== void 0 && value < schema.minimum) {
780
- value = schema.minimum;
781
- }
782
- if (typeof value === "number" && schema.maximum !== void 0 && value > schema.maximum) {
783
- value = schema.maximum;
784
- }
785
- }
786
- if (schema.enum && !schema.enum.includes(value)) {
787
- result[key] = schema.default;
788
- continue;
789
- }
790
- result[key] = value;
791
- }
792
- return result;
793
- };
794
- }
795
801
  function defineBlock(typeOrConfig, maybeConfig) {
796
802
  const type = typeof typeOrConfig === "string" ? typeOrConfig : typeOrConfig.type;
797
803
  const config = typeof typeOrConfig === "string" ? maybeConfig : typeOrConfig;
@@ -3422,6 +3428,18 @@ function snapshotPlain(value) {
3422
3428
  writable: true
3423
3429
  });
3424
3430
  }
3431
+ for (const key of Object.getOwnPropertySymbols(value)) {
3432
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3433
+ if (!descriptor?.enumerable) {
3434
+ continue;
3435
+ }
3436
+ Object.defineProperty(next, key, {
3437
+ value: snapshotPlain(descriptor.value),
3438
+ enumerable: true,
3439
+ configurable: true,
3440
+ writable: true
3441
+ });
3442
+ }
3425
3443
  return next;
3426
3444
  }
3427
3445
  function snapshotOps(ops) {
@@ -4855,17 +4873,54 @@ function normalizeFieldEditorType(schema) {
4855
4873
  return "none";
4856
4874
  }
4857
4875
 
4876
+ // src/editor/documentPreorder.ts
4877
+ function documentPreorderBlockIds(editor) {
4878
+ return documentPreorderBlockIdsFromState(editor.documentState);
4879
+ }
4880
+ function documentPreorderBlockIdsFromState(state) {
4881
+ const ids = [];
4882
+ for (const block of state.blocks) {
4883
+ ids.push(block.id);
4884
+ }
4885
+ return ids;
4886
+ }
4887
+ function documentPreorderBlockIdsFromDoc(doc) {
4888
+ const ids = [];
4889
+ const seen = /* @__PURE__ */ new Set();
4890
+ const blocks = doc.blocks;
4891
+ const order = doc.blockOrder;
4892
+ const walk = (id) => {
4893
+ if (seen.has(id)) {
4894
+ return;
4895
+ }
4896
+ seen.add(id);
4897
+ ids.push(id);
4898
+ const blockMap = blocks.get(id);
4899
+ const children = blockMap?.get("children");
4900
+ if (!children) {
4901
+ return;
4902
+ }
4903
+ for (let i = 0; i < children.length; i++) {
4904
+ walk(children.get(i));
4905
+ }
4906
+ };
4907
+ for (let i = 0; i < order.length; i++) {
4908
+ walk(order.get(i));
4909
+ }
4910
+ return ids;
4911
+ }
4912
+
4858
4913
  // src/editor/range.ts
4859
4914
  var DocumentRangeImpl = class {
4860
4915
  start;
4861
4916
  end;
4862
4917
  _anchor;
4863
4918
  _focus;
4864
- _doc;
4919
+ _order;
4865
4920
  constructor(anchor, focus, doc) {
4866
4921
  this._anchor = anchor;
4867
4922
  this._focus = focus;
4868
- this._doc = doc;
4923
+ this._order = documentPreorderBlockIdsFromDoc(doc);
4869
4924
  const anchorIdx = this._indexOfBlock(anchor.blockId);
4870
4925
  const focusIdx = this._indexOfBlock(focus.blockId);
4871
4926
  if (anchorIdx < focusIdx || anchorIdx === focusIdx && (anchor.offset ?? 0) <= (focus.offset ?? 0)) {
@@ -4894,13 +4949,10 @@ var DocumentRangeImpl = class {
4894
4949
  get blockRange() {
4895
4950
  const startIdx = this._indexOfBlock(this.start.blockId);
4896
4951
  const endIdx = this._indexOfBlock(this.end.blockId);
4897
- const result = [];
4898
- for (let i = startIdx; i <= endIdx; i++) {
4899
- result.push(
4900
- this._doc.blockOrder.get(i)
4901
- );
4952
+ if (startIdx < 0 || endIdx < 0) {
4953
+ return [];
4902
4954
  }
4903
- return result;
4955
+ return this._order.slice(startIdx, endIdx + 1);
4904
4956
  }
4905
4957
  contains(point) {
4906
4958
  const idx = this._indexOfBlock(point.blockId);
@@ -4936,10 +4988,7 @@ var DocumentRangeImpl = class {
4936
4988
  };
4937
4989
  }
4938
4990
  _indexOfBlock(blockId) {
4939
- for (let i = 0; i < this._doc.blockOrder.length; i++) {
4940
- if (this._doc.blockOrder.get(i) === blockId) return i;
4941
- }
4942
- return -1;
4991
+ return this._order.indexOf(blockId);
4943
4992
  }
4944
4993
  };
4945
4994
 
@@ -5003,13 +5052,10 @@ function blockIdsFromOrder(order, anchorId, focusId) {
5003
5052
  );
5004
5053
  }
5005
5054
  function blockIdsBetween(doc, anchorId, focusId) {
5006
- const order = doc.blockOrder;
5007
- return sliceBlockIds(
5008
- indexOfBlock(order, anchorId),
5009
- indexOfBlock(order, focusId),
5055
+ return blockIdsFromOrder(
5056
+ documentPreorderBlockIdsFromDoc(doc),
5010
5057
  anchorId,
5011
- focusId,
5012
- (index) => order.get(index)
5058
+ focusId
5013
5059
  );
5014
5060
  }
5015
5061
  function sliceBlockIds(anchorIndex, focusIndex, anchorId, focusId, idAt) {
@@ -5030,14 +5076,6 @@ function sliceBlockIds(anchorIndex, focusIndex, anchorId, focusId, idAt) {
5030
5076
  }
5031
5077
  return ids;
5032
5078
  }
5033
- function indexOfBlock(order, blockId) {
5034
- for (let i = 0; i < order.length; i++) {
5035
- if (order.get(i) === blockId) {
5036
- return i;
5037
- }
5038
- }
5039
- return -1;
5040
- }
5041
5079
 
5042
5080
  // src/editor/anchorRepair.ts
5043
5081
  function sitsInMovedRange(offset, assoc, range) {
@@ -7738,7 +7776,7 @@ function readTextAnchor(editor) {
7738
7776
  return selection.anchor;
7739
7777
  }
7740
7778
  function documentOrderedTextPoints(editor, selection) {
7741
- const order = editor.documentState.blockOrder;
7779
+ const order = documentPreorderBlockIds(editor);
7742
7780
  const anchorIndex = order.indexOf(selection.anchor.blockId);
7743
7781
  const focusIndex = order.indexOf(selection.focus.blockId);
7744
7782
  if (anchorIndex < 0 || focusIndex < 0) {
@@ -7752,7 +7790,7 @@ function documentOrderedTextPoints(editor, selection) {
7752
7790
 
7753
7791
  // src/commands/commandSnapshots.ts
7754
7792
  function buildNormalPositionSnapshot(editor) {
7755
- const blockOrder = [...editor.documentState.blockOrder];
7793
+ const blockOrder = [...getVisibleBlockIds(editor)];
7756
7794
  const blocks = {};
7757
7795
  for (const blockId of blockOrder) {
7758
7796
  const block = editor.getBlock(blockId);
@@ -7773,7 +7811,7 @@ function buildNormalPositionSnapshot(editor) {
7773
7811
  return { blockOrder, blocks };
7774
7812
  }
7775
7813
  function buildTransitionSnapshot(editor) {
7776
- const blockOrder = [...editor.documentState.blockOrder];
7814
+ const blockOrder = [...getVisibleBlockIds(editor)];
7777
7815
  const blocks = {};
7778
7816
  for (const blockId of blockOrder) {
7779
7817
  const block = editor.getBlock(blockId);
@@ -8070,7 +8108,7 @@ function replaceSingleBlockRange(blockId, start, end, text, marks) {
8070
8108
  };
8071
8109
  }
8072
8110
  function replaceMultiBlockRange(editor, start, end, text, marks) {
8073
- const order = editor.documentState.blockOrder;
8111
+ const order = documentPreorderBlockIds(editor);
8074
8112
  const startIndex = order.indexOf(start.blockId);
8075
8113
  const endIndex = order.indexOf(end.blockId);
8076
8114
  if (startIndex < 0 || endIndex < 0 || startIndex >= endIndex) {
@@ -8109,7 +8147,7 @@ function replaceMultiBlockRange(editor, start, end, text, marks) {
8109
8147
  );
8110
8148
  }
8111
8149
  function replaceTextToTextRange(editor, start, end, text, marks, startIndex, endIndex, startLength) {
8112
- const order = editor.documentState.blockOrder;
8150
+ const order = documentPreorderBlockIds(editor);
8113
8151
  const ops = [];
8114
8152
  if (start.offset < startLength) {
8115
8153
  ops.push(
@@ -8171,7 +8209,7 @@ function replaceTextToTextRange(editor, start, end, text, marks, startIndex, end
8171
8209
  };
8172
8210
  }
8173
8211
  function replaceMixedBoundaryRange(editor, start, end, text, marks, startIndex, endIndex, startEditable, endEditable, startLength) {
8174
- const order = editor.documentState.blockOrder;
8212
+ const order = documentPreorderBlockIds(editor);
8175
8213
  const ops = [];
8176
8214
  if (startEditable) {
8177
8215
  if (start.offset < startLength) {
@@ -9061,11 +9099,10 @@ var multiplayerControllerFacet = singleController(
9061
9099
  );
9062
9100
  var snapshotsControllerFacet = singleController("history.controller");
9063
9101
  var assetProviderFacet = singleController("pen.assetProvider");
9064
- var toolRuntimeFacet = singleController(
9065
- "tools.toolRuntime"
9066
- );
9102
+ var toolRuntimeFacet = singleController("tools.toolRuntime");
9067
9103
  var announcerFacet = singleController("pen.announcer");
9068
9104
  var streamingTargetFacet = singleController("deltaStream.target");
9105
+ var smoothStreamControllerFacet = singleController("ai.smoothStream");
9069
9106
 
9070
9107
  // src/commands/history.ts
9071
9108
  var historyUndo = defineCommand("history.undo");
@@ -9846,7 +9883,7 @@ function toggleMarkAcrossBlocks(editor, selection, param) {
9846
9883
  if (!range) {
9847
9884
  return false;
9848
9885
  }
9849
- const order = editor.documentState.blockOrder;
9886
+ const order = documentPreorderBlockIds(editor);
9850
9887
  const startIndex = order.indexOf(range.start.blockId);
9851
9888
  const endIndex = order.indexOf(range.end.blockId);
9852
9889
  if (startIndex < 0 || endIndex < 0) {
@@ -10873,6 +10910,7 @@ var FACET_BY_SLOT_KEY = {
10873
10910
  "pen.messages": messagesFacet,
10874
10911
  "pen.a11yLabel": a11yLabelFacet,
10875
10912
  "delta-stream:target": streamingTargetFacet,
10913
+ "smooth-stream:controller": smoothStreamControllerFacet,
10876
10914
  [ANNOUNCER_SLOT_KEY]: announcerFacet
10877
10915
  };
10878
10916
  function writeAssignedSlot(self, key, value) {
@@ -14288,6 +14326,77 @@ function emitMissingBlock(editor, blockId) {
14288
14326
  emit("diagnostic", event);
14289
14327
  }
14290
14328
 
14329
+ // src/suggestion/resolveSuggestionMenuTarget.ts
14330
+ var DEFAULT_LOOKBEHIND = 80;
14331
+ function inlineLogicalText(block) {
14332
+ return logicalInline(block).text;
14333
+ }
14334
+ function resolveSuggestionMenuTarget(editor, trigger) {
14335
+ if (trigger.char.length === 0) {
14336
+ return null;
14337
+ }
14338
+ const selection = editor.selection;
14339
+ if (selection?.type !== "text" || !isCollapsed(selection)) {
14340
+ return null;
14341
+ }
14342
+ if (selection.anchor.blockId !== selection.focus.blockId) {
14343
+ return null;
14344
+ }
14345
+ const block = editor.getBlock(selection.focus.blockId);
14346
+ if (!block) {
14347
+ return null;
14348
+ }
14349
+ const offset = selection.focus.offset;
14350
+ const lookbehind = trigger.lookbehind ?? DEFAULT_LOOKBEHIND;
14351
+ const prefixStartOffset = Math.max(0, offset - lookbehind);
14352
+ const { text, atoms } = logicalInline(block);
14353
+ const textBefore = text.slice(prefixStartOffset, offset);
14354
+ const triggerIndex = textBefore.lastIndexOf(trigger.char);
14355
+ if (triggerIndex < 0) {
14356
+ return null;
14357
+ }
14358
+ if (trigger.boundary === "whitespace") {
14359
+ const previousChar = textBefore[triggerIndex - 1];
14360
+ if (previousChar && !/\s/.test(previousChar)) {
14361
+ return null;
14362
+ }
14363
+ }
14364
+ const query = textBefore.slice(triggerIndex + trigger.char.length);
14365
+ const startOffset = prefixStartOffset + triggerIndex;
14366
+ const queryStartOffset = startOffset + trigger.char.length;
14367
+ if (queryRangeContainsAtom(atoms, queryStartOffset, offset)) {
14368
+ return null;
14369
+ }
14370
+ if (!trigger.allowSpaces && /\s/.test(query)) {
14371
+ return null;
14372
+ }
14373
+ if (trigger.closingChar && query.includes(trigger.closingChar)) {
14374
+ return null;
14375
+ }
14376
+ if (query.length < (trigger.minQueryLength ?? 0)) {
14377
+ return null;
14378
+ }
14379
+ if (trigger.maxQueryLength !== void 0 && query.length > trigger.maxQueryLength) {
14380
+ return null;
14381
+ }
14382
+ if (trigger.queryPattern) {
14383
+ trigger.queryPattern.lastIndex = 0;
14384
+ if (!trigger.queryPattern.test(query)) {
14385
+ return null;
14386
+ }
14387
+ }
14388
+ return {
14389
+ blockId: selection.focus.blockId,
14390
+ startOffset,
14391
+ endOffset: offset,
14392
+ query,
14393
+ trigger: trigger.char
14394
+ };
14395
+ }
14396
+ function queryRangeContainsAtom(atoms, queryStart, queryEnd) {
14397
+ return atoms.some((atom) => atom.start < queryEnd && atom.end > queryStart);
14398
+ }
14399
+
14291
14400
  // src/commands/resolveDirectedBinding.ts
14292
14401
  function resolveDirectedBinding(editor, binding) {
14293
14402
  const direction = resolveFocusBlockDirection(editor);
@@ -14913,6 +15022,7 @@ export {
14913
15022
  historyUndo,
14914
15023
  hookPriorityToPrecedence,
14915
15024
  indent,
15025
+ inlineLogicalText,
14916
15026
  inputRulesEngineFacet,
14917
15027
  inputRulesFacet,
14918
15028
  insertLineBreak,
@@ -14968,6 +15078,7 @@ export {
14968
15078
  resolveSchema,
14969
15079
  resolveSchemaA11y,
14970
15080
  resolveSelectionTargetBlockIds,
15081
+ resolveSuggestionMenuTarget,
14971
15082
  runMigrations,
14972
15083
  searchControllerFacet,
14973
15084
  selectAdjacentInlineAtom,
@@ -14985,6 +15096,7 @@ export {
14985
15096
  shouldShowBlockInDefaultMenus,
14986
15097
  singleController,
14987
15098
  slashMenuGroupOf,
15099
+ smoothStreamControllerFacet,
14988
15100
  snapToNormalPosition,
14989
15101
  snapshotsControllerFacet,
14990
15102
  sortDeltaAttributes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@input/pen-core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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.7",
47
- "@input/pen-types": "^0.1.7"
46
+ "@input/pen-yjs": "^0.1.9",
47
+ "@input/pen-types": "^0.1.9"
48
48
  },
49
49
  "devDependencies": {
50
50
  "tsup": "^8.4.0",