@inshapardaz/likhari-react 0.1.18-dev.132 → 0.1.18-dev.136

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.
@@ -13,6 +13,14 @@ export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
13
13
  * selection and undo history all survive the conversion.
14
14
  */
15
15
  export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
16
+ /** A couplet at the very end of the document would otherwise leave no
17
+ * editable block to click or arrow down into — appends one empty paragraph
18
+ * if the couplet doesn't already have a next sibling. Idempotent: once a
19
+ * sibling exists (that paragraph, or anything else), this is a no-op, so
20
+ * it's safe to call from both the insert command and a node transform that
21
+ * keeps the invariant even after later edits (e.g. deleting the trailing
22
+ * paragraph, or JSON content loaded without one). */
23
+ export declare function $ensureTrailingParagraph(node: PoetryBlockNode): void;
16
24
  /** Inserts a new two-misra couplet after the selection's top-level block,
17
25
  * in `layout`/`align`, and focuses its first misra. */
18
26
  export declare function $insertPoetryCouplet(layout: PoetryLayout, align: PoetryAlign): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4E,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvH,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErI;wCACwC;AACxC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAW1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAclF;AAED;uDACuD;AACvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAmBtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAoB5C"}
1
+ {"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4E,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvH,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErI;wCACwC;AACxC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAW1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAclF;AAED;;;;;;qDAMqD;AACrD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;uDACuD;AACvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAyBtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAuB5C"}
@@ -52,13 +52,29 @@ export function $setPoetryLayout(node, layout) {
52
52
  }
53
53
  node.setLayoutAttribute(layout);
54
54
  }
55
+ /** A couplet at the very end of the document would otherwise leave no
56
+ * editable block to click or arrow down into — appends one empty paragraph
57
+ * if the couplet doesn't already have a next sibling. Idempotent: once a
58
+ * sibling exists (that paragraph, or anything else), this is a no-op, so
59
+ * it's safe to call from both the insert command and a node transform that
60
+ * keeps the invariant even after later edits (e.g. deleting the trailing
61
+ * paragraph, or JSON content loaded without one). */
62
+ export function $ensureTrailingParagraph(node) {
63
+ if (node.getNextSibling() === null)
64
+ node.insertAfter($createParagraphNode());
65
+ }
55
66
  /** Inserts a new two-misra couplet after the selection's top-level block,
56
67
  * in `layout`/`align`, and focuses its first misra. */
57
68
  export function $insertPoetryCouplet(layout, align) {
58
69
  const selection = $getSelection();
59
70
  if (!$isRangeSelection(selection))
60
71
  return false;
61
- const anchorTopLevel = selection.anchor.getNode().getTopLevelElementOrThrow();
72
+ // getTopLevelElement() stops at the nearest shadow root, and a
73
+ // PoetryBlockNode is one — so from inside an existing couplet it resolves
74
+ // to the misra paragraph itself, not the couplet. Without this check, a
75
+ // second insert lands as a sibling of that misra (nested inside the first
76
+ // couplet) instead of after the couplet as a whole.
77
+ const anchorTopLevel = $getPoetryBlockFromSelection() ?? selection.anchor.getNode().getTopLevelElementOrThrow();
62
78
  const node = $createPoetryBlockNode(layout, align);
63
79
  const misraA = $createParagraphNode();
64
80
  const misraB = $createParagraphNode();
@@ -71,6 +87,7 @@ export function $insertPoetryCouplet(layout, align) {
71
87
  node.append(container);
72
88
  }
73
89
  anchorTopLevel.insertAfter(node);
90
+ $ensureTrailingParagraph(node);
74
91
  misraA.selectStart();
75
92
  return true;
76
93
  }
@@ -99,8 +116,12 @@ export function $exitPoetryOnEnter() {
99
116
  const isAtEnd = lastDesc ? anchor.key === lastDesc.getKey() && anchor.offset === lastDesc.getTextContentSize() : anchor.key === lastMisra.getKey();
100
117
  if (!isAtEnd)
101
118
  return false;
102
- const paragraph = $createParagraphNode();
103
- couplet.insertAfter(paragraph);
119
+ // $ensureTrailingParagraph already guarantees one after every couplet —
120
+ // reuse it rather than stacking a new empty paragraph on repeated Enters.
121
+ const existing = couplet.getNextSibling();
122
+ const paragraph = $isParagraphNode(existing) ? existing : $createParagraphNode();
123
+ if (paragraph !== existing)
124
+ couplet.insertAfter(paragraph);
104
125
  paragraph.select();
105
126
  return true;
106
127
  }
@@ -6,9 +6,9 @@ export declare const INSERT_POETRY_COUPLET_COMMAND: LexicalCommand<{
6
6
  }>;
7
7
  /**
8
8
  * Registers INSERT_POETRY_COUPLET_COMMAND (mirrors LayoutPlugin/FootnotePlugin's
9
- * shape), a node transform that removes a couplet left with no misra
10
- * content — e.g. after undo/paste leaves it structurally empty — rather
11
- * than letting an unselectable husk linger in the document, and a
9
+ * shape); a node transform that removes a couplet left with no misra
10
+ * content (e.g. after undo/paste leaves it structurally empty) and keeps a
11
+ * trailing couplet always followed by an editable paragraph; and a
12
12
  * KEY_ENTER_COMMAND handler (above RichTextPlugin's own, which would
13
13
  * otherwise trap Enter inside the couplet — see poetryActions.ts) that lets
14
14
  * Enter at the end of the last misra add a paragraph after the block.
@@ -1 +1 @@
1
- {"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoF,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAGhI,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG5F,eAAO,MAAM,6BAA6B,EAAE,cAAc,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAEtG,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,YAAY,SA+B3B"}
1
+ {"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoF,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAGhI,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG5F,eAAO,MAAM,6BAA6B,EAAE,cAAc,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAEtG,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,YAAY,SAmC3B"}
@@ -4,13 +4,13 @@ import { COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, KEY_ENTER_COMMAND, crea
4
4
  import { mergeRegister } from '@lexical/utils';
5
5
  import { LayoutContainerNode, LayoutItemNode } from '../blocks/LayoutNode.js';
6
6
  import { PoetryBlockNode } from '../blocks/PoetryNode.js';
7
- import { $exitPoetryOnEnter, $insertPoetryCouplet } from '../blocks/poetryActions.js';
7
+ import { $ensureTrailingParagraph, $exitPoetryOnEnter, $insertPoetryCouplet } from '../blocks/poetryActions.js';
8
8
  export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLET_COMMAND');
9
9
  /**
10
10
  * Registers INSERT_POETRY_COUPLET_COMMAND (mirrors LayoutPlugin/FootnotePlugin's
11
- * shape), a node transform that removes a couplet left with no misra
12
- * content — e.g. after undo/paste leaves it structurally empty — rather
13
- * than letting an unselectable husk linger in the document, and a
11
+ * shape); a node transform that removes a couplet left with no misra
12
+ * content (e.g. after undo/paste leaves it structurally empty) and keeps a
13
+ * trailing couplet always followed by an editable paragraph; and a
14
14
  * KEY_ENTER_COMMAND handler (above RichTextPlugin's own, which would
15
15
  * otherwise trap Enter inside the couplet — see poetryActions.ts) that lets
16
16
  * Enter at the end of the last misra add a paragraph after the block.
@@ -22,8 +22,11 @@ export function PoetryPlugin() {
22
22
  throw new Error('PoetryPlugin: PoetryBlockNode (and the columns nodes it composes) not registered on the editor');
23
23
  }
24
24
  return mergeRegister(editor.registerCommand(INSERT_POETRY_COUPLET_COMMAND, ({ layout, align }) => $insertPoetryCouplet(layout, align), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(PoetryBlockNode, (node) => {
25
- if (node.getChildrenSize() === 0)
25
+ if (node.getChildrenSize() === 0) {
26
26
  node.remove();
27
+ return;
28
+ }
29
+ $ensureTrailingParagraph(node);
27
30
  }), editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
28
31
  if (event?.shiftKey)
29
32
  return false; // Shift+Enter stays a plain line break
@@ -413,6 +413,13 @@
413
413
  text-align: inherit;
414
414
  }
415
415
 
416
+ /* A visible divider between misra columns, same dashed language as the
417
+ couplet's own border — matches a table's internal cell borders. */
418
+ .likhari-poetry--two-column .likhari-layout-item:not(:first-child) {
419
+ border-inline-start: 1px dashed var(--editor-line-strong);
420
+ padding-inline-start: 16px;
421
+ }
422
+
416
423
  .likhari-table {
417
424
  border-collapse: collapse;
418
425
  margin: 1em 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.18-dev.132",
3
+ "version": "0.1.18-dev.136",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {