@blankdotpage/cake 0.1.7 → 0.1.8

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.
@@ -46,6 +46,8 @@ export type InsertCommand = {
46
46
  /** Commands that can be applied directly by the engine */
47
47
  export type ApplyEditCommand = InsertCommand | {
48
48
  type: "insert-line-break";
49
+ } | {
50
+ type: "insert-hard-line-break";
49
51
  } | {
50
52
  type: "delete-backward";
51
53
  } | {
package/dist/index.cjs CHANGED
@@ -1078,10 +1078,10 @@ class CursorSourceBuilder {
1078
1078
  }
1079
1079
  }
1080
1080
  function isStructuralEdit(command) {
1081
- return command.type === "insert" || command.type === "delete-backward" || command.type === "delete-forward" || command.type === "insert-line-break" || command.type === "exit-block-wrapper";
1081
+ return command.type === "insert" || command.type === "delete-backward" || command.type === "delete-forward" || command.type === "insert-line-break" || command.type === "insert-hard-line-break" || command.type === "exit-block-wrapper";
1082
1082
  }
1083
1083
  function isApplyEditCommand(command) {
1084
- return command.type === "insert" || command.type === "insert-line-break" || command.type === "delete-backward" || command.type === "delete-forward";
1084
+ return command.type === "insert" || command.type === "insert-line-break" || command.type === "insert-hard-line-break" || command.type === "delete-backward" || command.type === "delete-forward";
1085
1085
  }
1086
1086
  function defineExtension(extension) {
1087
1087
  return extension;
@@ -1462,7 +1462,7 @@ function createRuntime(extensions) {
1462
1462
  nextAffinity: affinity === "backward" ? "forward" : "forward"
1463
1463
  };
1464
1464
  }
1465
- const replaceText = command.type === "insert" ? command.text : command.type === "insert-line-break" ? "\n" : command.type === "exit-block-wrapper" ? "\n" : "";
1465
+ const replaceText = command.type === "insert" ? command.text : command.type === "insert-line-break" || command.type === "insert-hard-line-break" ? "\n" : command.type === "exit-block-wrapper" ? "\n" : "";
1466
1466
  const range = command.type === "delete-backward" && cursorStart === cursorEnd ? cursorStart === 0 ? { start: 0, end: 0 } : { start: cursorStart - 1, end: cursorStart } : command.type === "delete-forward" && cursorStart === cursorEnd ? cursorStart === docCursorLength ? { start: cursorStart, end: cursorStart } : { start: cursorStart, end: cursorStart + 1 } : { start: cursorStart, end: cursorEnd };
1467
1467
  const shouldReplacePlaceholder = command.type === "insert" && replaceText.length > 0 && range.start === range.end && graphemeAtCursor(lines, range.start) === "​";
1468
1468
  const effectiveRange = shouldReplacePlaceholder ? { start: range.start, end: Math.min(docCursorLength, range.start + 1) } : range;
@@ -4310,6 +4310,41 @@ function findLineStartInSource$1(source, sourceOffset) {
4310
4310
  }
4311
4311
  return lineStart;
4312
4312
  }
4313
+ function isInsideBlockquote(source, sourcePos) {
4314
+ const lineStart = findLineStartInSource$1(source, sourcePos);
4315
+ const lineContent = source.slice(
4316
+ lineStart,
4317
+ source.indexOf("\n", lineStart) === -1 ? source.length : source.indexOf("\n", lineStart)
4318
+ );
4319
+ return BLOCKQUOTE_PATTERN.test(lineContent);
4320
+ }
4321
+ function handleExitBlockquote(state) {
4322
+ const { source, selection, map, runtime } = state;
4323
+ const cursorPos = Math.min(selection.start, selection.end);
4324
+ const sourcePos = map.cursorToSource(
4325
+ cursorPos,
4326
+ selection.affinity ?? "forward"
4327
+ );
4328
+ if (!isInsideBlockquote(source, sourcePos)) {
4329
+ return null;
4330
+ }
4331
+ let lineEnd = source.indexOf("\n", sourcePos);
4332
+ if (lineEnd === -1) {
4333
+ lineEnd = source.length;
4334
+ }
4335
+ const newSource = source.slice(0, lineEnd) + "\n" + source.slice(lineEnd);
4336
+ const next = runtime.createState(newSource);
4337
+ const newCursorOffset = lineEnd + 1;
4338
+ const caretCursor = next.map.sourceToCursor(newCursorOffset, "forward");
4339
+ return {
4340
+ source: newSource,
4341
+ selection: {
4342
+ start: caretCursor.cursorOffset,
4343
+ end: caretCursor.cursorOffset,
4344
+ affinity: "forward"
4345
+ }
4346
+ };
4347
+ }
4313
4348
  function handleToggleBlockquote(state) {
4314
4349
  const { source, selection, map, runtime } = state;
4315
4350
  const cursorPos = Math.min(selection.start, selection.end);
@@ -4355,6 +4390,9 @@ const blockquoteExtension = defineExtension({
4355
4390
  if (command.type === "toggle-blockquote") {
4356
4391
  return handleToggleBlockquote(state);
4357
4392
  }
4393
+ if (command.type === "insert-hard-line-break") {
4394
+ return handleExitBlockquote(state);
4395
+ }
4358
4396
  return null;
4359
4397
  },
4360
4398
  parseBlock(source, start, context) {
@@ -6808,6 +6846,21 @@ function hitTestFromLayout(params) {
6808
6846
  const caretX = (cursorOffsetInLine) => {
6809
6847
  return measureCaretXOnRow(cursorOffsetInLine) ?? approximateX(cursorOffsetInLine);
6810
6848
  };
6849
+ const rowLeftEdge = row.rect.left;
6850
+ const rowRightEdge = row.rect.left + row.rect.width;
6851
+ if (relativeX < rowLeftEdge) {
6852
+ return {
6853
+ cursorOffset: lineStartOffset + row.startOffset,
6854
+ pastRowEnd: false
6855
+ };
6856
+ }
6857
+ if (relativeX > rowRightEdge) {
6858
+ const isEndOfLine2 = row.endOffset === lineInfo.cursorLength;
6859
+ return {
6860
+ cursorOffset: lineStartOffset + row.endOffset,
6861
+ pastRowEnd: !isEndOfLine2 && row.endOffset < lineInfo.cursorLength
6862
+ };
6863
+ }
6811
6864
  let low = row.startOffset;
6812
6865
  let high = row.endOffset;
6813
6866
  while (low < high) {
@@ -6837,7 +6890,7 @@ function hitTestFromLayout(params) {
6837
6890
  return candidateA;
6838
6891
  }
6839
6892
  const mid = (xA + xB) / 2;
6840
- return relativeX >= mid ? candidateA : candidateB;
6893
+ return relativeX > mid ? candidateA : candidateB;
6841
6894
  })();
6842
6895
  const endX = caretX(row.endOffset);
6843
6896
  {
@@ -8380,6 +8433,7 @@ class CakeEngine {
8380
8433
  this.recordHistory(command.type);
8381
8434
  this.state = nextState;
8382
8435
  this.render();
8436
+ this.flushOverlayUpdate();
8383
8437
  (_a = this.onChange) == null ? void 0 : _a.call(this, this.state.source, this.state.selection);
8384
8438
  this.scheduleScrollCaretIntoView();
8385
8439
  if (shouldOpenLinkPopover) {
@@ -8389,6 +8443,7 @@ class CakeEngine {
8389
8443
  }
8390
8444
  if (options == null ? void 0 : options.restoreFocus) {
8391
8445
  this.focus();
8446
+ this.applySelection(this.state.selection);
8392
8447
  }
8393
8448
  return true;
8394
8449
  }
@@ -8488,9 +8543,7 @@ class CakeEngine {
8488
8543
  this.detachDragListeners();
8489
8544
  }
8490
8545
  handleFocusIn() {
8491
- queueMicrotask(() => {
8492
- this.scheduleOverlayUpdate();
8493
- });
8546
+ this.flushOverlayUpdate();
8494
8547
  }
8495
8548
  handleFocusOut() {
8496
8549
  queueMicrotask(() => {
@@ -8974,7 +9027,7 @@ class CakeEngine {
8974
9027
  if (cmdOrCtrl && event.key === "Enter") {
8975
9028
  event.preventDefault();
8976
9029
  this.keydownHandledBeforeInput = true;
8977
- this.applyEdit({ type: "insert-line-break" });
9030
+ this.applyEdit({ type: "insert-hard-line-break" });
8978
9031
  queueMicrotask(() => {
8979
9032
  this.keydownHandledBeforeInput = false;
8980
9033
  });
package/dist/index.js CHANGED
@@ -1076,10 +1076,10 @@ class CursorSourceBuilder {
1076
1076
  }
1077
1077
  }
1078
1078
  function isStructuralEdit(command) {
1079
- return command.type === "insert" || command.type === "delete-backward" || command.type === "delete-forward" || command.type === "insert-line-break" || command.type === "exit-block-wrapper";
1079
+ return command.type === "insert" || command.type === "delete-backward" || command.type === "delete-forward" || command.type === "insert-line-break" || command.type === "insert-hard-line-break" || command.type === "exit-block-wrapper";
1080
1080
  }
1081
1081
  function isApplyEditCommand(command) {
1082
- return command.type === "insert" || command.type === "insert-line-break" || command.type === "delete-backward" || command.type === "delete-forward";
1082
+ return command.type === "insert" || command.type === "insert-line-break" || command.type === "insert-hard-line-break" || command.type === "delete-backward" || command.type === "delete-forward";
1083
1083
  }
1084
1084
  function defineExtension(extension) {
1085
1085
  return extension;
@@ -1460,7 +1460,7 @@ function createRuntime(extensions) {
1460
1460
  nextAffinity: affinity === "backward" ? "forward" : "forward"
1461
1461
  };
1462
1462
  }
1463
- const replaceText = command.type === "insert" ? command.text : command.type === "insert-line-break" ? "\n" : command.type === "exit-block-wrapper" ? "\n" : "";
1463
+ const replaceText = command.type === "insert" ? command.text : command.type === "insert-line-break" || command.type === "insert-hard-line-break" ? "\n" : command.type === "exit-block-wrapper" ? "\n" : "";
1464
1464
  const range = command.type === "delete-backward" && cursorStart === cursorEnd ? cursorStart === 0 ? { start: 0, end: 0 } : { start: cursorStart - 1, end: cursorStart } : command.type === "delete-forward" && cursorStart === cursorEnd ? cursorStart === docCursorLength ? { start: cursorStart, end: cursorStart } : { start: cursorStart, end: cursorStart + 1 } : { start: cursorStart, end: cursorEnd };
1465
1465
  const shouldReplacePlaceholder = command.type === "insert" && replaceText.length > 0 && range.start === range.end && graphemeAtCursor(lines, range.start) === "​";
1466
1466
  const effectiveRange = shouldReplacePlaceholder ? { start: range.start, end: Math.min(docCursorLength, range.start + 1) } : range;
@@ -4308,6 +4308,41 @@ function findLineStartInSource$1(source, sourceOffset) {
4308
4308
  }
4309
4309
  return lineStart;
4310
4310
  }
4311
+ function isInsideBlockquote(source, sourcePos) {
4312
+ const lineStart = findLineStartInSource$1(source, sourcePos);
4313
+ const lineContent = source.slice(
4314
+ lineStart,
4315
+ source.indexOf("\n", lineStart) === -1 ? source.length : source.indexOf("\n", lineStart)
4316
+ );
4317
+ return BLOCKQUOTE_PATTERN.test(lineContent);
4318
+ }
4319
+ function handleExitBlockquote(state) {
4320
+ const { source, selection, map, runtime } = state;
4321
+ const cursorPos = Math.min(selection.start, selection.end);
4322
+ const sourcePos = map.cursorToSource(
4323
+ cursorPos,
4324
+ selection.affinity ?? "forward"
4325
+ );
4326
+ if (!isInsideBlockquote(source, sourcePos)) {
4327
+ return null;
4328
+ }
4329
+ let lineEnd = source.indexOf("\n", sourcePos);
4330
+ if (lineEnd === -1) {
4331
+ lineEnd = source.length;
4332
+ }
4333
+ const newSource = source.slice(0, lineEnd) + "\n" + source.slice(lineEnd);
4334
+ const next = runtime.createState(newSource);
4335
+ const newCursorOffset = lineEnd + 1;
4336
+ const caretCursor = next.map.sourceToCursor(newCursorOffset, "forward");
4337
+ return {
4338
+ source: newSource,
4339
+ selection: {
4340
+ start: caretCursor.cursorOffset,
4341
+ end: caretCursor.cursorOffset,
4342
+ affinity: "forward"
4343
+ }
4344
+ };
4345
+ }
4311
4346
  function handleToggleBlockquote(state) {
4312
4347
  const { source, selection, map, runtime } = state;
4313
4348
  const cursorPos = Math.min(selection.start, selection.end);
@@ -4353,6 +4388,9 @@ const blockquoteExtension = defineExtension({
4353
4388
  if (command.type === "toggle-blockquote") {
4354
4389
  return handleToggleBlockquote(state);
4355
4390
  }
4391
+ if (command.type === "insert-hard-line-break") {
4392
+ return handleExitBlockquote(state);
4393
+ }
4356
4394
  return null;
4357
4395
  },
4358
4396
  parseBlock(source, start, context) {
@@ -6806,6 +6844,21 @@ function hitTestFromLayout(params) {
6806
6844
  const caretX = (cursorOffsetInLine) => {
6807
6845
  return measureCaretXOnRow(cursorOffsetInLine) ?? approximateX(cursorOffsetInLine);
6808
6846
  };
6847
+ const rowLeftEdge = row.rect.left;
6848
+ const rowRightEdge = row.rect.left + row.rect.width;
6849
+ if (relativeX < rowLeftEdge) {
6850
+ return {
6851
+ cursorOffset: lineStartOffset + row.startOffset,
6852
+ pastRowEnd: false
6853
+ };
6854
+ }
6855
+ if (relativeX > rowRightEdge) {
6856
+ const isEndOfLine2 = row.endOffset === lineInfo.cursorLength;
6857
+ return {
6858
+ cursorOffset: lineStartOffset + row.endOffset,
6859
+ pastRowEnd: !isEndOfLine2 && row.endOffset < lineInfo.cursorLength
6860
+ };
6861
+ }
6809
6862
  let low = row.startOffset;
6810
6863
  let high = row.endOffset;
6811
6864
  while (low < high) {
@@ -6835,7 +6888,7 @@ function hitTestFromLayout(params) {
6835
6888
  return candidateA;
6836
6889
  }
6837
6890
  const mid = (xA + xB) / 2;
6838
- return relativeX >= mid ? candidateA : candidateB;
6891
+ return relativeX > mid ? candidateA : candidateB;
6839
6892
  })();
6840
6893
  const endX = caretX(row.endOffset);
6841
6894
  {
@@ -8378,6 +8431,7 @@ class CakeEngine {
8378
8431
  this.recordHistory(command.type);
8379
8432
  this.state = nextState;
8380
8433
  this.render();
8434
+ this.flushOverlayUpdate();
8381
8435
  (_a = this.onChange) == null ? void 0 : _a.call(this, this.state.source, this.state.selection);
8382
8436
  this.scheduleScrollCaretIntoView();
8383
8437
  if (shouldOpenLinkPopover) {
@@ -8387,6 +8441,7 @@ class CakeEngine {
8387
8441
  }
8388
8442
  if (options == null ? void 0 : options.restoreFocus) {
8389
8443
  this.focus();
8444
+ this.applySelection(this.state.selection);
8390
8445
  }
8391
8446
  return true;
8392
8447
  }
@@ -8486,9 +8541,7 @@ class CakeEngine {
8486
8541
  this.detachDragListeners();
8487
8542
  }
8488
8543
  handleFocusIn() {
8489
- queueMicrotask(() => {
8490
- this.scheduleOverlayUpdate();
8491
- });
8544
+ this.flushOverlayUpdate();
8492
8545
  }
8493
8546
  handleFocusOut() {
8494
8547
  queueMicrotask(() => {
@@ -8972,7 +9025,7 @@ class CakeEngine {
8972
9025
  if (cmdOrCtrl && event.key === "Enter") {
8973
9026
  event.preventDefault();
8974
9027
  this.keydownHandledBeforeInput = true;
8975
- this.applyEdit({ type: "insert-line-break" });
9028
+ this.applyEdit({ type: "insert-hard-line-break" });
8976
9029
  queueMicrotask(() => {
8977
9030
  this.keydownHandledBeforeInput = false;
8978
9031
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blankdotpage/cake",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",