@jackuait/blok 0.4.1-beta.17 → 0.4.1-beta.19

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.
@@ -1,4 +1,4 @@
1
- import { e as i } from "./blok-BmQiBq7w.mjs";
1
+ import { e as i } from "./blok-zaWxnlMM.mjs";
2
2
  const l = async (e, r) => {
3
3
  const n = (await import("./i18next-CugVlwWp.mjs")).default.createInstance(), s = {
4
4
  lng: e,
@@ -1,4 +1,4 @@
1
- import { t as f, q as i } from "./inline-tool-convert-f0-Y0Vcm.mjs";
1
+ import { t as f, q as i } from "./inline-tool-convert-CoQJYHI_.mjs";
2
2
  const a = {
3
3
  wrapper: i(
4
4
  "fixed z-[2] bottom-5 left-5",
@@ -18,7 +18,7 @@ let nt = (o = 21) => {
18
18
  return t;
19
19
  };
20
20
  var ot = /* @__PURE__ */ ((o) => (o.VERBOSE = "VERBOSE", o.INFO = "INFO", o.WARN = "WARN", o.ERROR = "ERROR", o))(ot || {});
21
- const rt = () => "0.4.1-beta.17", Ct = {
21
+ const rt = () => "0.4.1-beta.19", Ct = {
22
22
  BACKSPACE: 8,
23
23
  TAB: 9,
24
24
  ENTER: 13,
package/dist/full.mjs CHANGED
@@ -10,10 +10,10 @@ var e = (a, l, o) => l in a ? n(a, l, { enumerable: !0, configurable: !0, writab
10
10
  d.call(l, o) && e(a, o, l[o]);
11
11
  return a;
12
12
  }, r = (a, l) => t(a, c(l));
13
- import { B as v, v as A } from "./chunks/blok-BmQiBq7w.mjs";
13
+ import { B as v, v as A } from "./chunks/blok-zaWxnlMM.mjs";
14
14
  import { List as p, Header as f, Paragraph as I, Link as k, Italic as u, Bold as B } from "./tools.mjs";
15
15
  import { defaultBlockTools as H, defaultInlineTools as P } from "./tools.mjs";
16
- import { D as _ } from "./chunks/inline-tool-convert-f0-Y0Vcm.mjs";
16
+ import { D as _ } from "./chunks/inline-tool-convert-CoQJYHI_.mjs";
17
17
  const m = {
18
18
  paragraph: {
19
19
  class: I,
package/dist/tools.mjs CHANGED
@@ -10,8 +10,8 @@ var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writa
10
10
  it.call(t, e) && U(f, e, t[e]);
11
11
  return f;
12
12
  }, P = (f, t) => rt(f, st(t));
13
- import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-f0-Y0Vcm.mjs";
14
- import { a0 as Dt } from "./chunks/inline-tool-convert-f0-Y0Vcm.mjs";
13
+ import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-CoQJYHI_.mjs";
14
+ import { a0 as Dt } from "./chunks/inline-tool-convert-CoQJYHI_.mjs";
15
15
  const W = [
16
16
  "empty:before:pointer-events-none",
17
17
  "empty:before:text-gray-text",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackuait/blok",
3
- "version": "0.4.1-beta.17",
3
+ "version": "0.4.1-beta.19",
4
4
  "description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
5
5
  "module": "dist/blok.mjs",
6
6
  "types": "./types/index.d.ts",
@@ -79,6 +79,12 @@ interface BlockConstructorOptions {
79
79
  * References blocks that are children of this block.
80
80
  */
81
81
  contentIds?: string[];
82
+
83
+ /**
84
+ * Slot index within parent container (e.g., which column in a columns block).
85
+ * Used to organize children into specific slots for container blocks.
86
+ */
87
+ slot?: number;
82
88
  }
83
89
 
84
90
  /**
@@ -142,6 +148,12 @@ export class Block extends EventsDispatcher<BlockEvents> {
142
148
  */
143
149
  public contentIds: string[];
144
150
 
151
+ /**
152
+ * Slot index within parent container (e.g., which column in a columns block).
153
+ * Null if this block is not assigned to a specific slot.
154
+ */
155
+ public slot: number | null;
156
+
145
157
  /**
146
158
  * Block Tool`s name
147
159
  */
@@ -259,6 +271,7 @@ export class Block extends EventsDispatcher<BlockEvents> {
259
271
  * @param options.readOnly - Read-Only flag
260
272
  * @param [options.parentId] - parent block id for hierarchical structure
261
273
  * @param [options.contentIds] - array of child block ids
274
+ * @param [options.slot] - slot index within parent container
262
275
  * @param [eventBus] - Blok common event bus. Allows to subscribe on some Blok events. Could be omitted when "virtual" Block is created. See BlocksAPI@composeBlockData.
263
276
  */
264
277
  constructor({
@@ -269,6 +282,7 @@ export class Block extends EventsDispatcher<BlockEvents> {
269
282
  tunesData,
270
283
  parentId,
271
284
  contentIds,
285
+ slot,
272
286
  }: BlockConstructorOptions, eventBus?: EventsDispatcher<BlokEventMap>) {
273
287
  super();
274
288
  this.ready = new Promise((resolve) => {
@@ -278,6 +292,7 @@ export class Block extends EventsDispatcher<BlockEvents> {
278
292
  this.id = id;
279
293
  this.parentId = parentId ?? null;
280
294
  this.contentIds = contentIds ?? [];
295
+ this.slot = slot ?? null;
281
296
  this.settings = tool.settings;
282
297
  this.config = this.settings;
283
298
  this.blokEventBus = eventBus || null;
@@ -29,6 +29,8 @@ export class BlocksAPI extends Module {
29
29
  getBlockIndex: (id: string): number | undefined => this.getBlockIndex(id),
30
30
  getBlocksCount: (): number => this.getBlocksCount(),
31
31
  getBlockByElement: (element: HTMLElement) => this.getBlockByElement(element),
32
+ getChildren: (parentId: string): BlockAPIInterface[] => this.getChildren(parentId),
33
+ getChildrenInSlot: (parentId: string, slot: number): BlockAPIInterface[] => this.getChildrenInSlot(parentId, slot),
32
34
  insert: this.insert,
33
35
  insertMany: this.insertMany,
34
36
  update: this.update,
@@ -118,6 +120,31 @@ export class BlocksAPI extends Module {
118
120
  return new BlockAPI(block);
119
121
  }
120
122
 
123
+ /**
124
+ * Returns all child blocks of a parent container block
125
+ * @param parentId - id of the parent block
126
+ */
127
+ public getChildren(parentId: string): BlockAPIInterface[] {
128
+ const children = this.Blok.BlockManager.blocks.filter(
129
+ (block) => block.parentId === parentId
130
+ );
131
+
132
+ return children.map((block) => new BlockAPI(block));
133
+ }
134
+
135
+ /**
136
+ * Returns child blocks of a parent container block in a specific slot
137
+ * @param parentId - id of the parent block
138
+ * @param slot - slot index (e.g., column index for columns block)
139
+ */
140
+ public getChildrenInSlot(parentId: string, slot: number): BlockAPIInterface[] {
141
+ const children = this.Blok.BlockManager.blocks.filter(
142
+ (block) => block.parentId === parentId && block.slot === slot
143
+ );
144
+
145
+ return children.map((block) => new BlockAPI(block));
146
+ }
147
+
121
148
  /**
122
149
  * Move block from one index to another
123
150
  * @param {number} toIndex - index to move to
@@ -235,6 +235,7 @@ export class BlockManager extends Module {
235
235
  * @param {BlockToolData} [options.data] - constructor params
236
236
  * @param {string} [options.parentId] - parent block id for hierarchical structure
237
237
  * @param {string[]} [options.contentIds] - array of child block ids
238
+ * @param {number} [options.slot] - slot index within parent container
238
239
  * @returns {Block}
239
240
  */
240
241
  public composeBlock({
@@ -244,6 +245,7 @@ export class BlockManager extends Module {
244
245
  tunes: tunesData = {},
245
246
  parentId,
246
247
  contentIds,
248
+ slot,
247
249
  }: {
248
250
  tool: string;
249
251
  id?: string;
@@ -251,6 +253,7 @@ export class BlockManager extends Module {
251
253
  tunes?: {[name: string]: BlockTuneData};
252
254
  parentId?: string;
253
255
  contentIds?: string[];
256
+ slot?: number;
254
257
  }): Block {
255
258
  const readOnly = this.Blok.ReadOnly.isEnabled;
256
259
  const tool = this.Blok.Tools.blockTools.get(name);
@@ -268,6 +271,7 @@ export class BlockManager extends Module {
268
271
  tunesData,
269
272
  parentId,
270
273
  contentIds,
274
+ slot,
271
275
  }, this.eventsDispatcher);
272
276
 
273
277
  if (!readOnly) {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * History module utilities
3
+ * @module History
4
+ */
5
+
6
+ export { SmartGrouping } from './smart-grouping';
7
+ export type { ActionType, MutationMetadata, ActionContext } from './types';
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Smart grouping logic for history checkpoints
3
+ * @module History/SmartGrouping
4
+ */
5
+
6
+ import type { ActionContext, ActionType, MutationMetadata } from './types';
7
+
8
+ /**
9
+ * Actions that should create an immediate checkpoint (before the action)
10
+ */
11
+ const IMMEDIATE_CHECKPOINT_ACTIONS: ActionType[] = [
12
+ 'format',
13
+ 'structural',
14
+ 'paste',
15
+ 'cut',
16
+ ];
17
+
18
+ /**
19
+ * Determines when to create history checkpoints based on action patterns
20
+ *
21
+ * Creates checkpoints when:
22
+ * - Action type changes (e.g., typing → deleting)
23
+ * - Block changes
24
+ * - Immediate actions (format, structural, paste, cut)
25
+ */
26
+ export class SmartGrouping {
27
+ /**
28
+ * Current action context
29
+ */
30
+ private currentContext: ActionContext | undefined;
31
+
32
+ /**
33
+ * Determines if a checkpoint should be created before recording this mutation
34
+ * @param metadata - mutation metadata with action type info
35
+ * @param blockId - ID of the block being mutated
36
+ * @returns true if a checkpoint should be created
37
+ */
38
+ public shouldCreateCheckpoint(
39
+ metadata: MutationMetadata,
40
+ blockId: string
41
+ ): boolean {
42
+ const actionType = metadata.actionType ?? 'insert';
43
+
44
+ // No current context means this is the first action - don't checkpoint
45
+ if (!this.currentContext) {
46
+ return false;
47
+ }
48
+
49
+ // Block changed - create checkpoint
50
+ if (this.currentContext.blockId !== blockId) {
51
+ return true;
52
+ }
53
+
54
+ // Action type changed (e.g., typing → deleting) - create checkpoint
55
+ if (this.currentContext.type !== actionType) {
56
+ return true;
57
+ }
58
+
59
+ return false;
60
+ }
61
+
62
+ /**
63
+ * Checks if this action type should trigger an immediate checkpoint
64
+ * @param actionType - the action type to check
65
+ * @returns true if this action should create an immediate checkpoint
66
+ */
67
+ public isImmediateCheckpoint(actionType: ActionType): boolean {
68
+ return IMMEDIATE_CHECKPOINT_ACTIONS.includes(actionType);
69
+ }
70
+
71
+ /**
72
+ * Updates the current action context
73
+ * @param actionType - the new action type
74
+ * @param blockId - the block being edited
75
+ */
76
+ public updateContext(actionType: ActionType, blockId: string): void {
77
+ this.currentContext = {
78
+ type: actionType,
79
+ blockId,
80
+ timestamp: Date.now(),
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Gets the current action context
86
+ * @returns the current context or undefined
87
+ */
88
+ public getCurrentContext(): ActionContext | undefined {
89
+ return this.currentContext;
90
+ }
91
+
92
+ /**
93
+ * Clears the current action context
94
+ */
95
+ public clearContext(): void {
96
+ this.currentContext = undefined;
97
+ }
98
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Types for smart history grouping
3
+ * @module History/Types
4
+ */
5
+
6
+ /**
7
+ * Action types that can be detected from mutations
8
+ */
9
+ export type ActionType =
10
+ | 'insert' // Typing characters
11
+ | 'delete-back' // Backspace deletion
12
+ | 'delete-fwd' // Forward delete
13
+ | 'format' // Inline formatting (bold, italic, etc.)
14
+ | 'structural' // Block-level changes (split, merge)
15
+ | 'paste' // Paste operation
16
+ | 'cut'; // Cut operation
17
+
18
+ /**
19
+ * Metadata about a mutation for smart grouping decisions
20
+ */
21
+ export interface MutationMetadata {
22
+ /**
23
+ * Type of action that caused this mutation
24
+ */
25
+ actionType?: ActionType;
26
+
27
+ /**
28
+ * Text that was inserted (captured by MutationDetector)
29
+ */
30
+ insertedText?: string;
31
+
32
+ /**
33
+ * Text that was deleted (captured by MutationDetector)
34
+ */
35
+ deletedText?: string;
36
+ }
37
+
38
+ /**
39
+ * Context about the current action sequence
40
+ */
41
+ export interface ActionContext {
42
+ /**
43
+ * The type of action being performed
44
+ */
45
+ type: ActionType;
46
+
47
+ /**
48
+ * ID of the block being edited
49
+ */
50
+ blockId: string;
51
+
52
+ /**
53
+ * Timestamp when this context started
54
+ */
55
+ timestamp: number;
56
+ }
@@ -9,6 +9,8 @@ import { BlockChanged, HistoryStateChanged } from '../events';
9
9
  import type { BlockMutationEvent } from '../../../types/events/block';
10
10
  import { Shortcuts } from '../utils/shortcuts';
11
11
  import type { Block } from '../block';
12
+ import { SmartGrouping } from './history/smart-grouping';
13
+ import type { ActionType } from './history/types';
12
14
 
13
15
  /**
14
16
  * Default maximum history stack size
@@ -117,6 +119,21 @@ export class History extends Module {
117
119
  */
118
120
  private initialStateCaptured = false;
119
121
 
122
+ /**
123
+ * Smart grouping logic for determining when to create checkpoints
124
+ */
125
+ private smartGrouping = new SmartGrouping();
126
+
127
+ /**
128
+ * Current action type being tracked (for detecting action type changes)
129
+ */
130
+ private currentActionType: ActionType = 'insert';
131
+
132
+ /**
133
+ * Keydown handler reference for cleanup
134
+ */
135
+ private keydownHandler: ((e: KeyboardEvent) => void) | null = null;
136
+
120
137
  /**
121
138
  * Maximum number of entries in history stack
122
139
  */
@@ -145,6 +162,7 @@ export class History extends Module {
145
162
  public async prepare(): Promise<void> {
146
163
  this.setupEventListeners();
147
164
  this.setupKeyboardShortcuts();
165
+ this.setupActionTypeTracking();
148
166
  }
149
167
 
150
168
  /**
@@ -310,6 +328,7 @@ export class History extends Module {
310
328
  this.undoStack = [];
311
329
  this.redoStack = [];
312
330
  this.initialStateCaptured = false;
331
+ this.smartGrouping.clearContext();
313
332
  this.emitStateChanged();
314
333
  }
315
334
 
@@ -433,11 +452,47 @@ export class History extends Module {
433
452
  return false;
434
453
  }
435
454
 
455
+ /**
456
+ * Sets up keydown tracking for action type detection
457
+ */
458
+ private setupActionTypeTracking(): void {
459
+ // Wait for UI to be ready
460
+ setTimeout(() => {
461
+ const redactor = this.Blok.UI?.nodes?.redactor;
462
+
463
+ if (!redactor) {
464
+ return;
465
+ }
466
+
467
+ this.keydownHandler = (e: KeyboardEvent): void => {
468
+ // Detect action type from key press
469
+ if (e.key === 'Backspace') {
470
+ this.currentActionType = 'delete-back';
471
+
472
+ return;
473
+ }
474
+
475
+ if (e.key === 'Delete') {
476
+ this.currentActionType = 'delete-fwd';
477
+
478
+ return;
479
+ }
480
+
481
+ // Single character key (typing)
482
+ if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
483
+ this.currentActionType = 'insert';
484
+ }
485
+ };
486
+
487
+ redactor.addEventListener('keydown', this.keydownHandler);
488
+ }, 0);
489
+ }
490
+
436
491
  /**
437
492
  * Handles block mutation events
438
- * Debounces rapid changes and records state snapshots
493
+ * Uses smart grouping to create checkpoints when action type changes
439
494
  */
440
- private handleBlockMutation(_event: BlockMutationEvent): void {
495
+ private handleBlockMutation(event: BlockMutationEvent): void {
441
496
  // Mark this instance as active for global shortcuts
442
497
  History.activeInstance = this;
443
498
 
@@ -453,10 +508,39 @@ export class History extends Module {
453
508
  return;
454
509
  }
455
510
 
456
- // Clear existing debounce timeout
457
- this.clearDebounce();
511
+ // Get block ID from event
512
+ const blockId = event.detail.target.id;
513
+
514
+ // Check if we should create a checkpoint before this action
515
+ const shouldCheckpoint = this.smartGrouping.shouldCreateCheckpoint(
516
+ { actionType: this.currentActionType },
517
+ blockId
518
+ );
519
+
520
+ // Check if this is an immediate checkpoint action
521
+ const isImmediate = this.smartGrouping.isImmediateCheckpoint(this.currentActionType);
522
+
523
+ if (shouldCheckpoint || isImmediate) {
524
+ // Create checkpoint immediately (flush pending debounce)
525
+ this.clearDebounce();
526
+ void this.recordState().then(() => {
527
+ // Update context after recording
528
+ this.smartGrouping.updateContext(this.currentActionType, blockId);
529
+ // Start new debounce for continued editing
530
+ this.startDebounce();
531
+ });
532
+ } else {
533
+ // Update context and debounce normally
534
+ this.smartGrouping.updateContext(this.currentActionType, blockId);
535
+ this.clearDebounce();
536
+ this.startDebounce();
537
+ }
538
+ }
458
539
 
459
- // Debounce to batch rapid changes
540
+ /**
541
+ * Starts the debounce timer for recording state
542
+ */
543
+ private startDebounce(): void {
460
544
  this.debounceTimeout = setTimeout(() => {
461
545
  void this.recordState();
462
546
  }, this.debounceTime);
@@ -491,6 +575,14 @@ export class History extends Module {
491
575
  // Capture caret position along with state
492
576
  const caretPosition = this.getCaretPosition();
493
577
 
578
+ // Check if this state is identical to the last entry (avoid duplicates)
579
+ const lastEntry = this.undoStack[this.undoStack.length - 1];
580
+
581
+ if (lastEntry && this.areStatesEqual(lastEntry.state, state)) {
582
+ // State hasn't changed, no need to record
583
+ return;
584
+ }
585
+
494
586
  // Clear redo stack when new changes are made
495
587
  this.redoStack = [];
496
588
 
@@ -561,6 +653,44 @@ export class History extends Module {
561
653
  }
562
654
  }
563
655
 
656
+ /**
657
+ * Compares two states for equality (ignoring timestamps)
658
+ * @param a - first state
659
+ * @param b - second state
660
+ * @returns true if the block content is identical
661
+ */
662
+ private areStatesEqual(a: OutputData, b: OutputData): boolean {
663
+ // Quick check: different number of blocks
664
+ if (a.blocks.length !== b.blocks.length) {
665
+ return false;
666
+ }
667
+
668
+ // Compare each block using every() for functional approach
669
+ return a.blocks.every((blockA, i) => {
670
+ const blockB = b.blocks[i];
671
+
672
+ // Check ID and type
673
+ if (blockA.id !== blockB.id || blockA.type !== blockB.type) {
674
+ return false;
675
+ }
676
+
677
+ // Compare data (deep comparison via JSON)
678
+ if (JSON.stringify(blockA.data) !== JSON.stringify(blockB.data)) {
679
+ return false;
680
+ }
681
+
682
+ // Compare tunes if present
683
+ const tunesA = JSON.stringify(blockA.tunes ?? {});
684
+ const tunesB = JSON.stringify(blockB.tunes ?? {});
685
+
686
+ if (tunesA !== tunesB) {
687
+ return false;
688
+ }
689
+
690
+ return true;
691
+ });
692
+ }
693
+
564
694
  /**
565
695
  * Restores document to a given state using smart diffing
566
696
  * Only updates blocks that have changed to preserve DOM state
@@ -1085,14 +1215,23 @@ export class History extends Module {
1085
1215
  }
1086
1216
  this.registeredShortcuts = [];
1087
1217
 
1218
+ // Remove keydown handler
1219
+ const redactor = this.Blok.UI?.nodes?.redactor;
1220
+
1221
+ if (this.keydownHandler && redactor) {
1222
+ redactor.removeEventListener('keydown', this.keydownHandler);
1223
+ }
1224
+ this.keydownHandler = null;
1225
+
1088
1226
  // Clear active instance if it's this one
1089
1227
  if (History.activeInstance === this) {
1090
1228
  History.activeInstance = null;
1091
1229
  }
1092
1230
 
1093
- // Clear stacks
1231
+ // Clear stacks and smart grouping
1094
1232
  this.undoStack = [];
1095
1233
  this.redoStack = [];
1096
1234
  this.initialStateCaptured = false;
1235
+ this.smartGrouping.clearContext();
1097
1236
  }
1098
1237
  }
@@ -270,14 +270,10 @@ export class Paste extends Module {
270
270
  const normalizedHtmlData = rawHtmlData;
271
271
 
272
272
  /**
273
- * If Blok json is passed, insert it
273
+ * If Blok json is passed, check if we should try pattern matching first
274
274
  */
275
- if (blokData) {
276
- try {
277
- this.insertBlokData(JSON.parse(blokData));
278
-
279
- return;
280
- } catch (_e) { } // Do nothing and continue execution as usual if error appears
275
+ if (blokData && await this.handleBlokDataPaste(blokData, plainData)) {
276
+ return;
281
277
  }
282
278
 
283
279
  /** Add all tags that can be substituted to sanitizer configuration */
@@ -307,6 +303,50 @@ export class Paste extends Module {
307
303
  }
308
304
  }
309
305
 
306
+ /**
307
+ * Handles pasting of Blok JSON data, with pattern matching priority.
308
+ * For plain text that might match a pattern (like URLs), tries pattern matching first.
309
+ * This handles the case where text is cut and pasted within the same editor -
310
+ * we want pattern matching to still work for things like embed URLs.
311
+ * @param blokData - serialized Blok JSON data
312
+ * @param plainData - plain text content from clipboard
313
+ * @returns true if paste was handled, false otherwise
314
+ */
315
+ private async handleBlokDataPaste(blokData: string, plainData: string): Promise<boolean> {
316
+ try {
317
+ const parsedBlokData = JSON.parse(blokData) as Pick<SavedData, 'id' | 'data' | 'tool'>[];
318
+ const shouldTryPatternMatch = plainData && this.toolsPatterns.length > 0;
319
+ const patternResult = shouldTryPatternMatch ? await this.processPattern(plainData) : undefined;
320
+
321
+ if (patternResult) {
322
+ await this.insertPatternMatch(patternResult);
323
+
324
+ return true;
325
+ }
326
+
327
+ this.insertBlokData(parsedBlokData);
328
+
329
+ return true;
330
+ } catch (_e) {
331
+ return false;
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Inserts a matched pattern as a new block
337
+ * @param patternResult - the matched pattern with tool and event
338
+ */
339
+ private async insertPatternMatch(patternResult: { event: PasteEvent; tool: string }): Promise<void> {
340
+ const { BlockManager, Caret } = this.Blok;
341
+ const needToReplaceCurrentBlock = BlockManager.currentBlock &&
342
+ BlockManager.currentBlock.tool.isDefault &&
343
+ BlockManager.currentBlock.isEmpty;
344
+
345
+ const insertedBlock = await BlockManager.paste(patternResult.tool, patternResult.event, needToReplaceCurrentBlock);
346
+
347
+ Caret.setToBlock(insertedBlock, Caret.positions.END);
348
+ }
349
+
310
350
  /**
311
351
  * Process pasted text and divide them into Blocks
312
352
  * @param {string} data - text to process. Can be HTML or plain.
@@ -52,7 +52,7 @@ export class Renderer extends Module {
52
52
  * Create Blocks instances
53
53
  */
54
54
  const blocks = processedBlocks.map((blockData: OutputBlockData) => {
55
- const { tunes, id, parent, content } = blockData;
55
+ const { tunes, id, parent, content, slot } = blockData;
56
56
  const originalTool = blockData.type;
57
57
  const availabilityResult = (() => {
58
58
  if (Tools.available.has(originalTool)) {
@@ -79,6 +79,7 @@ export class Renderer extends Module {
79
79
  tunes,
80
80
  parentId: parent,
81
81
  contentIds: content,
82
+ slot,
82
83
  });
83
84
  } catch (error) {
84
85
  log(`Block «${tool}» skipped because of plugins error`, 'error', {
@@ -98,6 +99,7 @@ export class Renderer extends Module {
98
99
  tunes,
99
100
  parentId: parent,
100
101
  contentIds: content,
102
+ slot,
101
103
  });
102
104
  }
103
105
  };
@@ -71,6 +71,21 @@ export interface Blocks {
71
71
  */
72
72
  getBlockByElement(element: HTMLElement): BlockAPI | undefined;
73
73
 
74
+ /**
75
+ * Returns all child blocks of a parent container block
76
+ *
77
+ * @param parentId - id of the parent block
78
+ */
79
+ getChildren(parentId: string): BlockAPI[];
80
+
81
+ /**
82
+ * Returns child blocks of a parent container block in a specific slot
83
+ *
84
+ * @param parentId - id of the parent block
85
+ * @param slot - slot index (e.g., column index for columns block)
86
+ */
87
+ getChildrenInSlot(parentId: string, slot: number): BlockAPI[];
88
+
74
89
  /**
75
90
  * Returns Blocks count
76
91
  * @return {number}