@inshapardaz/likhari-react 0.1.18-dev.154 → 0.1.18-dev.158

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,24 +1,28 @@
1
1
  import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalNode, type NodeKey, type SerializedElementNode, type Spread, ElementNode } from 'lexical';
2
- export type PoetryLayout = 'single' | 'two-column' | 'alternating';
2
+ export type PoetryLayout = 'single' | 'two-column';
3
3
  export type PoetryAlign = 'justify' | 'left' | 'right' | 'start';
4
+ /** Vertical space between couplets — an ordered scale the menu steps through. */
5
+ export declare const POETRY_SPACINGS: readonly ["compact", "normal", "relaxed", "loose"];
6
+ export type PoetrySpacing = (typeof POETRY_SPACINGS)[number];
7
+ export declare const DEFAULT_POETRY_SPACING: PoetrySpacing;
4
8
  export type SerializedPoetryBlockNode = Spread<{
5
9
  layout: PoetryLayout;
6
10
  align: PoetryAlign;
7
11
  width?: number;
12
+ spacing?: PoetrySpacing;
8
13
  }, SerializedElementNode>;
9
14
  /**
10
- * A poetry *section*: one or more couplets (two-line verse units), each
11
- * wrapped as either a PoetryCoupletNode (single-column/alternating — see
12
- * that file) or a LayoutContainerNode row (two-column, built on the
13
- * columns primitive, §4.9) — see blocks/poetryActions.ts's $getCouplets
14
- * for how a block's children (a free mix of either wrapper, per couplet)
15
- * are read, and $setPoetryLayout/$exitPoetryOnEnter for how they're
16
- * restructured/grown. `layout` is this block's *default* for couplets
17
- * appended to it (a PoetryCoupletNode can still individually override to
18
- * "centered" regardless); `align` applies to the whole section. Both
19
- * mutate in place via poetryActions so converting a section between
20
- * layouts doesn't lose selection/undo coherence. One extensible primitive
21
- * in place of four fixed templates, per requirements doc §4.11.
15
+ * A poetry *section*: one or more couplets (two-line verse units) sharing
16
+ * one layout — single-column stacks each couplet's two misras directly as
17
+ * paragraph children (two per couplet, in order), two-column gives each
18
+ * couplet its own LayoutContainerNode row (built on the columns primitive,
19
+ * §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
20
+ * is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
21
+ * restructured/grown between couplets and layouts. `layout` and `align`
22
+ * apply to the whole section, not per couplet; both mutate in place via
23
+ * poetryActions so converting a section between layouts doesn't lose
24
+ * selection/undo coherence. One extensible primitive in place of four
25
+ * fixed templates, per requirements doc §4.11.
22
26
  */
23
27
  export declare class PoetryBlockNode extends ElementNode {
24
28
  __layout: PoetryLayout;
@@ -26,7 +30,8 @@ export declare class PoetryBlockNode extends ElementNode {
26
30
  /** User-chosen width in px (drag-resized), overriding editor.css's default
27
31
  * max-width: 70%. Undefined until the user resizes it. */
28
32
  __width?: number;
29
- constructor(layout?: PoetryLayout, align?: PoetryAlign, width?: number, key?: NodeKey);
33
+ __spacing: PoetrySpacing;
34
+ constructor(layout?: PoetryLayout, align?: PoetryAlign, width?: number, spacing?: PoetrySpacing, key?: NodeKey);
30
35
  static getType(): string;
31
36
  static clone(node: PoetryBlockNode): PoetryBlockNode;
32
37
  getLayout(): PoetryLayout;
@@ -37,6 +42,8 @@ export declare class PoetryBlockNode extends ElementNode {
37
42
  * poetryActions.ts's $setPoetryLayout, not a node method, since it needs
38
43
  * $-context tree mutations beyond this node's own state. */
39
44
  setLayoutAttribute(layout: PoetryLayout): this;
45
+ getSpacing(): PoetrySpacing;
46
+ setSpacing(spacing: PoetrySpacing): this;
40
47
  setAlign(align: PoetryAlign): this;
41
48
  static importJSON(serializedNode: SerializedPoetryBlockNode): PoetryBlockNode;
42
49
  exportJSON(): SerializedPoetryBlockNode;
@@ -47,6 +54,6 @@ export declare class PoetryBlockNode extends ElementNode {
47
54
  canBeEmpty(): false;
48
55
  isShadowRoot(): boolean;
49
56
  }
50
- export declare function $createPoetryBlockNode(layout?: PoetryLayout, align?: PoetryAlign, width?: number): PoetryBlockNode;
57
+ export declare function $createPoetryBlockNode(layout?: PoetryLayout, align?: PoetryAlign, width?: number, spacing?: PoetrySpacing): PoetryBlockNode;
51
58
  export declare function $isPoetryBlockNode(node: LexicalNode | null | undefined): node is PoetryBlockNode;
52
59
  //# sourceMappingURL=PoetryNode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PoetryNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryNode.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAwBjE,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,qBAAqB,CAAC,CAAC;AAEpI;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;gBAEL,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO;IAO1G,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe;IAIpD,SAAS,IAAI,YAAY;IAIzB,QAAQ,IAAI,WAAW;IAIvB,QAAQ,IAAI,MAAM,GAAG,SAAS;IAI9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAMzC;;gEAE4D;IAC5D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAM9C,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAMlC,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,yBAAyB,GAAG,eAAe;IAI7E,UAAU,IAAI,yBAAyB;IAYvC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAe3C,SAAS,IAAI,eAAe;IAW5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAa5C,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAerF,UAAU,IAAI,KAAK;IAInB,YAAY,IAAI,OAAO;CAGxB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAEvI;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,eAAe,CAEhG"}
1
+ {"version":3,"file":"PoetryNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryNode.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AACjE,iFAAiF;AACjF,eAAO,MAAM,eAAe,oDAAqD,CAAC;AAClF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,eAAO,MAAM,sBAAsB,EAAE,aAAwB,CAAC;AAyB9D,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAC5C;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,EACrF,qBAAqB,CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;gBAGvB,MAAM,GAAE,YAAuB,EAC/B,KAAK,GAAE,WAAuB,EAC9B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE,aAAsC,EAC/C,GAAG,CAAC,EAAE,OAAO;IASf,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe;IAIpD,SAAS,IAAI,YAAY;IAIzB,QAAQ,IAAI,WAAW;IAIvB,QAAQ,IAAI,MAAM,GAAG,SAAS;IAI9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAMzC;;gEAE4D;IAC5D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAM9C,UAAU,IAAI,aAAa;IAI3B,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAMxC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAMlC,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,yBAAyB,GAAG,eAAe;IAI7E,UAAU,IAAI,yBAAyB;IAavC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAiB3C,SAAS,IAAI,eAAe;IAY5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAa5C,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAoBrF,UAAU,IAAI,KAAK;IAInB,YAAY,IAAI,OAAO;CAGxB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,GAAE,YAAuB,EAC/B,KAAK,GAAE,WAAuB,EAC9B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE,aAAsC,GAC9C,eAAe,CAEjB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,eAAe,CAEhG"}
@@ -1,7 +1,11 @@
1
1
  import { addClassNamesToElement } from '@lexical/utils';
2
2
  import { $applyNodeReplacement, ElementNode, } from 'lexical';
3
- const VALID_LAYOUTS = new Set(['single', 'two-column', 'alternating']);
3
+ /** Vertical space between couplets — an ordered scale the menu steps through. */
4
+ export const POETRY_SPACINGS = ['compact', 'normal', 'relaxed', 'loose'];
5
+ export const DEFAULT_POETRY_SPACING = 'normal';
6
+ const VALID_LAYOUTS = new Set(['single', 'two-column']);
4
7
  const VALID_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
8
+ const VALID_SPACINGS = new Set(POETRY_SPACINGS);
5
9
  /** `text-align: justify` only stretches a line that actually wraps — a
6
10
  * misra short enough to fit on one line would otherwise just sit at its own
7
11
  * start edge instead of spreading its words across the full column width.
@@ -22,31 +26,31 @@ function applyWidthStyle(element, width) {
22
26
  element.style.maxWidth = `${width}px`;
23
27
  }
24
28
  /**
25
- * A poetry *section*: one or more couplets (two-line verse units), each
26
- * wrapped as either a PoetryCoupletNode (single-column/alternating — see
27
- * that file) or a LayoutContainerNode row (two-column, built on the
28
- * columns primitive, §4.9) — see blocks/poetryActions.ts's $getCouplets
29
- * for how a block's children (a free mix of either wrapper, per couplet)
30
- * are read, and $setPoetryLayout/$exitPoetryOnEnter for how they're
31
- * restructured/grown. `layout` is this block's *default* for couplets
32
- * appended to it (a PoetryCoupletNode can still individually override to
33
- * "centered" regardless); `align` applies to the whole section. Both
34
- * mutate in place via poetryActions so converting a section between
35
- * layouts doesn't lose selection/undo coherence. One extensible primitive
36
- * in place of four fixed templates, per requirements doc §4.11.
29
+ * A poetry *section*: one or more couplets (two-line verse units) sharing
30
+ * one layout — single-column stacks each couplet's two misras directly as
31
+ * paragraph children (two per couplet, in order), two-column gives each
32
+ * couplet its own LayoutContainerNode row (built on the columns primitive,
33
+ * §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
34
+ * is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
35
+ * restructured/grown between couplets and layouts. `layout` and `align`
36
+ * apply to the whole section, not per couplet; both mutate in place via
37
+ * poetryActions so converting a section between layouts doesn't lose
38
+ * selection/undo coherence. One extensible primitive in place of four
39
+ * fixed templates, per requirements doc §4.11.
37
40
  */
38
41
  export class PoetryBlockNode extends ElementNode {
39
- constructor(layout = 'single', align = 'justify', width, key) {
42
+ constructor(layout = 'single', align = 'justify', width, spacing = DEFAULT_POETRY_SPACING, key) {
40
43
  super(key);
41
44
  this.__layout = layout;
42
45
  this.__align = align;
43
46
  this.__width = width;
47
+ this.__spacing = spacing;
44
48
  }
45
49
  static getType() {
46
- return 'poetry-block';
50
+ return 'poetry-couplet';
47
51
  }
48
52
  static clone(node) {
49
- return new PoetryBlockNode(node.__layout, node.__align, node.__width, node.__key);
53
+ return new PoetryBlockNode(node.__layout, node.__align, node.__width, node.__spacing, node.__key);
50
54
  }
51
55
  getLayout() {
52
56
  return this.getLatest().__layout;
@@ -70,22 +74,31 @@ export class PoetryBlockNode extends ElementNode {
70
74
  writable.__layout = layout;
71
75
  return writable;
72
76
  }
77
+ getSpacing() {
78
+ return this.getLatest().__spacing;
79
+ }
80
+ setSpacing(spacing) {
81
+ const writable = this.getWritable();
82
+ writable.__spacing = spacing;
83
+ return writable;
84
+ }
73
85
  setAlign(align) {
74
86
  const writable = this.getWritable();
75
87
  writable.__align = align;
76
88
  return writable;
77
89
  }
78
90
  static importJSON(serializedNode) {
79
- return $createPoetryBlockNode(serializedNode.layout, serializedNode.align, serializedNode.width);
91
+ return $createPoetryBlockNode(serializedNode.layout, serializedNode.align, serializedNode.width, serializedNode.spacing);
80
92
  }
81
93
  exportJSON() {
82
94
  const width = this.getWidth();
83
95
  return {
84
96
  ...super.exportJSON(),
85
- type: 'poetry-block',
97
+ type: 'poetry-couplet',
86
98
  version: 1,
87
99
  layout: this.getLayout(),
88
100
  align: this.getAlign(),
101
+ spacing: this.getSpacing(),
89
102
  ...(width !== undefined ? { width } : {}),
90
103
  };
91
104
  }
@@ -100,7 +113,9 @@ export class PoetryBlockNode extends ElementNode {
100
113
  const align = VALID_ALIGNS.has(rawAlign) ? rawAlign : 'justify';
101
114
  const rawWidth = Number(domNode.getAttribute('data-likhari-poetry-width'));
102
115
  const width = Number.isFinite(rawWidth) && rawWidth > 0 ? rawWidth : undefined;
103
- return { conversion: () => ({ node: $createPoetryBlockNode(layout, align, width) }), priority: 2 };
116
+ const rawSpacing = domNode.getAttribute('data-likhari-poetry-spacing');
117
+ const spacing = VALID_SPACINGS.has(rawSpacing) ? rawSpacing : DEFAULT_POETRY_SPACING;
118
+ return { conversion: () => ({ node: $createPoetryBlockNode(layout, align, width, spacing) }), priority: 2 };
104
119
  },
105
120
  };
106
121
  }
@@ -108,6 +123,7 @@ export class PoetryBlockNode extends ElementNode {
108
123
  const element = document.createElement('div');
109
124
  element.setAttribute('data-likhari-poetry-layout', this.getLayout());
110
125
  element.setAttribute('data-likhari-poetry-align', this.getAlign());
126
+ element.setAttribute('data-likhari-poetry-spacing', this.getSpacing());
111
127
  const width = this.getWidth();
112
128
  if (width !== undefined)
113
129
  element.setAttribute('data-likhari-poetry-width', String(width));
@@ -118,7 +134,7 @@ export class PoetryBlockNode extends ElementNode {
118
134
  createDOM(config) {
119
135
  const element = document.createElement('div');
120
136
  const base = config.theme.poetry ?? 'likhari-poetry';
121
- addClassNamesToElement(element, base, `${base}--${this.__layout}`);
137
+ addClassNamesToElement(element, base, `${base}--${this.__layout}`, `${base}--spacing-${this.__spacing}`);
122
138
  applyAlignStyle(element, this.__align);
123
139
  applyWidthStyle(element, this.__width);
124
140
  return element;
@@ -133,6 +149,11 @@ export class PoetryBlockNode extends ElementNode {
133
149
  dom.classList.remove(`${base}--${prevNode.__layout}`);
134
150
  dom.classList.add(`${base}--${this.__layout}`);
135
151
  }
152
+ if (prevNode.__spacing !== this.__spacing) {
153
+ const base = config.theme.poetry ?? 'likhari-poetry';
154
+ dom.classList.remove(`${base}--spacing-${prevNode.__spacing}`);
155
+ dom.classList.add(`${base}--spacing-${this.__spacing}`);
156
+ }
136
157
  if (prevNode.__align !== this.__align) {
137
158
  applyAlignStyle(dom, this.__align);
138
159
  }
@@ -148,8 +169,8 @@ export class PoetryBlockNode extends ElementNode {
148
169
  return true;
149
170
  }
150
171
  }
151
- export function $createPoetryBlockNode(layout = 'single', align = 'justify', width) {
152
- return $applyNodeReplacement(new PoetryBlockNode(layout, align, width));
172
+ export function $createPoetryBlockNode(layout = 'single', align = 'justify', width, spacing = DEFAULT_POETRY_SPACING) {
173
+ return $applyNodeReplacement(new PoetryBlockNode(layout, align, width, spacing));
153
174
  }
154
175
  export function $isPoetryBlockNode(node) {
155
176
  return node instanceof PoetryBlockNode;
@@ -1,15 +1,14 @@
1
1
  import { type ParagraphNode } from 'lexical';
2
- import { type PoetryAlign, type PoetryLayout, type PoetryBlockNode } from './PoetryNode.js';
2
+ import { type PoetryAlign, type PoetryBlockNode, type PoetryLayout } from './PoetryNode.js';
3
3
  /** One couplet's two misra paragraphs, in order. */
4
4
  export type Couplet = [ParagraphNode, ParagraphNode];
5
5
  /**
6
- * A poetry block's children are a free mix, per couplet, of either a
7
- * PoetryCoupletNode (single-column/alternating, optionally `centered`) or a
8
- * LayoutContainerNode row (two-column) — "two columns with a couplet that
9
- * is single, aligned centered" means exactly this: some couplets two-column,
10
- * others centered singles, in the same block. This walks that mixed
11
- * structure and returns each couplet as a misra pair, in document order —
12
- * the one place that knows how to read either wrapper, so nothing else does.
6
+ * A poetry block is a *section* of one or more couplets sharing one layout
7
+ * (single-column stacks each couplet's two misras directly as paragraph
8
+ * children, two deep per couplet; two-column gives each couplet its own
9
+ * LayoutContainerNode row). This walks that structure and returns each
10
+ * couplet as a pair, in document order — the one place that knows how to
11
+ * read either shape, so nothing else needs to.
13
12
  */
14
13
  export declare function $getCouplets(node: PoetryBlockNode): Couplet[];
15
14
  /** Every misra in the block, flattened across all its couplets — for
@@ -19,33 +18,22 @@ export declare function $getMisraParagraphs(node: PoetryBlockNode): ParagraphNod
19
18
  /** The PoetryBlockNode the current selection is inside, or null. */
20
19
  export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
21
20
  /**
22
- * Converts every couplet in the block between two-column (built on the
23
- * columns primitive, §4.9) and single-column/alternating (a
24
- * PoetryCoupletNode per couplet) — pulls each couplet's two misra
25
- * paragraphs out of whichever shape currently holds them and rebuilds the
26
- * other shape around the same paragraph nodes, preserving couplet order
27
- * (and dropping any individual 'centered' override, which only applies to
28
- * the non-two-column shape), so content, selection and undo history all
29
- * survive the conversion. Switching between 'single' and 'alternating' is
30
- * purely an attribute change (both use the same PoetryCoupletNode
31
- * structure) — no restructuring needed.
21
+ * The explicit "Insert couplet above/below" menu actions — mirrors the
22
+ * table menu's "Insert row before/after". Targets the couplet the caret is
23
+ * currently in, falling back to the block's last couplet (so the toolbar's
24
+ * always-available insert button has somewhere sensible to act on even
25
+ * without a precise caret position).
32
26
  */
33
- export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
27
+ export declare function $insertCoupletRelativeToSelection(position: 'before' | 'after'): boolean;
34
28
  /**
35
- * Sets whether the couplet the selection is inside renders as a narrower,
36
- * centered box ("single, aligned centered") instead of the block's default
37
- * width — independent of the block's own layout. A two-column couplet is
38
- * converted into a PoetryCoupletNode first (its two columns stacked into
39
- * two lines), since "centered" only applies to that shape; a couplet
40
- * that's already a PoetryCoupletNode just has its flag flipped. Returns
41
- * false when the selection isn't inside a couplet.
29
+ * Converts every couplet in the block between single-column (two stacked
30
+ * misras) and two-column (built on the columns primitive, §4.9) in place —
31
+ * pulls each couplet's two misra paragraphs out of whichever shape
32
+ * currently holds them and rebuilds the other shape around the same
33
+ * paragraph nodes, preserving couplet order, so content, selection and
34
+ * undo history all survive the conversion.
42
35
  */
43
- export declare function $setCoupletCentered(centered: boolean): boolean;
44
- /** Whether the couplet the selection is inside is currently centered —
45
- * always false for a two-column couplet, since that override only applies
46
- * to the PoetryCoupletNode shape. Null when the selection isn't inside a
47
- * couplet at all (vs. inside one that just isn't centered). */
48
- export declare function $getCoupletCenteredFromSelection(): boolean | null;
36
+ export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
49
37
  /** A block at the very end of the document would otherwise leave no
50
38
  * editable block to click or arrow down into — appends one empty paragraph
51
39
  * if the block doesn't already have a next sibling. Idempotent: once a
@@ -56,10 +44,13 @@ export declare function $getCoupletCenteredFromSelection(): boolean | null;
56
44
  export declare function $ensureTrailingParagraph(node: PoetryBlockNode): void;
57
45
  /**
58
46
  * Inserts a poetry couplet at the caret. If the caret is already inside a
59
- * poetry block, appends a new couplet to *that* block (in its existing
60
- * default layout) and focuses its first misra. Otherwise creates a new
61
- * block (in `layout`/`align`) with one couplet, after the selection's
62
- * top-level element.
47
+ * poetry block, inserts a new couplet right after the one the caret is in
48
+ * (same as the "Insert couplet below" menu action — table-row-insert
49
+ * semantics, in its existing layout: "one poetry block can contain one or
50
+ * more couplets, in one or two column layout", so a block's layout is
51
+ * fixed once it has couplets, not chosen per couplet) and focuses its first
52
+ * misra. Otherwise creates a new block (in `layout`/`align`) with one
53
+ * couplet, after the selection's top-level element.
63
54
  */
64
55
  export declare function $insertPoetryCouplet(layout: PoetryLayout, align: PoetryAlign): boolean;
65
56
  /** Removes the couplet the selection is inside. If it's the block's only
@@ -97,4 +88,7 @@ export declare function $deletePoetryOnBackspace(): boolean;
97
88
  * next couplet's first misra.
98
89
  */
99
90
  export declare function $exitPoetryOnEnter(): boolean;
91
+ /** Steps the caret's poetry block one notch looser (+1) or tighter (-1) on
92
+ * the couplet-spacing scale; clamps at either end. */
93
+ export declare function $adjustPoetrySpacing(step: 1 | -1): boolean;
100
94
  //# sourceMappingURL=poetryActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4F,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAGvI,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErI,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,EAAE,CAc7D;AAED;;6CAE6C;AAC7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAE1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AAyCD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA8BlF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CA4B9D;AAED;;;+DAG+D;AAC/D,wBAAgB,gCAAgC,IAAI,OAAO,GAAG,IAAI,CAYjE;AAED;;;;;;qDAMqD;AACrD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAmBtF;AAED;;;;wCAIwC;AACxC,wBAAgB,oBAAoB,IAAI,OAAO,CAkB9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CA8BlD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAyC5C"}
1
+ {"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4F,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvI,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,EAAE,CAgB7D;AAED;;6CAE6C;AAC7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAE1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AA0DD;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAevF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAmBlF;AAED;;;;;;qDAMqD;AACrD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAiBtF;AAED;;;;wCAIwC;AACxC,wBAAgB,oBAAoB,IAAI,OAAO,CAwB9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAqClD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAgD5C;AAED;sDACsD;AACtD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAO1D"}
@@ -1,32 +1,31 @@
1
1
  import { $findMatchingParent } from '@lexical/utils';
2
2
  import { $createParagraphNode, $getSelection, $isElementNode, $isParagraphNode, $isRangeSelection } from 'lexical';
3
3
  import { $createLayoutContainerNode, $createLayoutItemNode, $isLayoutContainerNode, $isLayoutItemNode } from './LayoutNode.js';
4
- import { $createPoetryCoupletNode, $isPoetryCoupletNode } from './PoetryCoupletNode.js';
5
- import { $createPoetryBlockNode, $isPoetryBlockNode } from './PoetryNode.js';
4
+ import { $createPoetryBlockNode, $isPoetryBlockNode, POETRY_SPACINGS, } from './PoetryNode.js';
6
5
  /**
7
- * A poetry block's children are a free mix, per couplet, of either a
8
- * PoetryCoupletNode (single-column/alternating, optionally `centered`) or a
9
- * LayoutContainerNode row (two-column) — "two columns with a couplet that
10
- * is single, aligned centered" means exactly this: some couplets two-column,
11
- * others centered singles, in the same block. This walks that mixed
12
- * structure and returns each couplet as a misra pair, in document order —
13
- * the one place that knows how to read either wrapper, so nothing else does.
6
+ * A poetry block is a *section* of one or more couplets sharing one layout
7
+ * (single-column stacks each couplet's two misras directly as paragraph
8
+ * children, two deep per couplet; two-column gives each couplet its own
9
+ * LayoutContainerNode row). This walks that structure and returns each
10
+ * couplet as a pair, in document order — the one place that knows how to
11
+ * read either shape, so nothing else needs to.
14
12
  */
15
13
  export function $getCouplets(node) {
14
+ if (node.getLayout() === 'single') {
15
+ const paragraphs = node.getChildren().filter($isParagraphNode);
16
+ const couplets = [];
17
+ for (let i = 0; i + 1 < paragraphs.length; i += 2)
18
+ couplets.push([paragraphs[i], paragraphs[i + 1]]);
19
+ return couplets;
20
+ }
21
+ const containers = node.getChildren().filter($isLayoutContainerNode);
16
22
  const couplets = [];
17
- for (const child of node.getChildren()) {
18
- if ($isPoetryCoupletNode(child)) {
19
- const [a, b] = child.getChildren().filter($isParagraphNode);
20
- if (a && b)
21
- couplets.push([a, b]);
22
- }
23
- else if ($isLayoutContainerNode(child)) {
24
- const items = child.getChildren().filter($isLayoutItemNode);
25
- const a = items[0]?.getFirstChild();
26
- const b = items[1]?.getFirstChild();
27
- if ($isParagraphNode(a) && $isParagraphNode(b))
28
- couplets.push([a, b]);
29
- }
23
+ for (const container of containers) {
24
+ const items = container.getChildren().filter($isLayoutItemNode);
25
+ const a = items[0]?.getFirstChild();
26
+ const b = items[1]?.getFirstChild();
27
+ if ($isParagraphNode(a) && $isParagraphNode(b))
28
+ couplets.push([a, b]);
30
29
  }
31
30
  return couplets;
32
31
  }
@@ -56,53 +55,87 @@ function $findCoupletPosition(couplets, target) {
56
55
  }
57
56
  return null;
58
57
  }
59
- /** A couplet's own wrapper node (PoetryCoupletNode or LayoutContainerNode) —
60
- * every couplet has exactly one, so removing/replacing a couplet means
61
- * operating on this, not on its misra paragraphs individually. */
62
- function $findCoupletWrapper(misra) {
63
- return $findMatchingParent(misra, (n) => $isPoetryCoupletNode(n) || $isLayoutContainerNode(n));
64
- }
65
- /** Appends a new couplet to the block, in its default layout (two-column
66
- * for a 'two-column' block, a plain PoetryCoupletNode otherwise) unless
67
- * `twoColumn` is given explicitly. */
68
- function $appendCoupletTo(node, twoColumn) {
58
+ function $appendCoupletTo(node) {
69
59
  const misraA = $createParagraphNode();
70
60
  const misraB = $createParagraphNode();
71
- const useTwoColumn = twoColumn ?? node.getLayout() === 'two-column';
72
- if (useTwoColumn) {
61
+ if (node.getLayout() === 'single') {
62
+ node.append(misraA, misraB);
63
+ }
64
+ else {
73
65
  const container = $createLayoutContainerNode('repeat(2, 1fr)');
74
66
  container.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
75
67
  node.append(container);
76
68
  }
69
+ return [misraA, misraB];
70
+ }
71
+ /**
72
+ * Inserts a new, empty couplet immediately before or after an existing one —
73
+ * the table-row-insert equivalent for a poetry block ("add/remove couplets
74
+ * just like adding/removing table rows"). `existingFirstMisra` is that
75
+ * couplet's first misra, used to locate its wrapper (nothing, in single
76
+ * layout — just its two sibling paragraphs; a LayoutContainerNode row in
77
+ * two-column) so the new couplet can be spliced in at the right spot
78
+ * regardless of layout.
79
+ */
80
+ function $insertCoupletRelativeTo(existingFirstMisra, layout, position) {
81
+ const misraA = $createParagraphNode();
82
+ const misraB = $createParagraphNode();
83
+ if (layout === 'single') {
84
+ if (position === 'before') {
85
+ existingFirstMisra.insertBefore(misraA);
86
+ misraA.insertAfter(misraB);
87
+ }
88
+ else {
89
+ const existingSecondMisra = existingFirstMisra.getNextSibling() ?? existingFirstMisra;
90
+ existingSecondMisra.insertAfter(misraA);
91
+ misraA.insertAfter(misraB);
92
+ }
93
+ }
77
94
  else {
78
- node.append($createPoetryCoupletNode().append(misraA, misraB));
95
+ const container = $findMatchingParent(existingFirstMisra, $isLayoutContainerNode);
96
+ const newContainer = $createLayoutContainerNode('repeat(2, 1fr)');
97
+ newContainer.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
98
+ if (position === 'before')
99
+ container.insertBefore(newContainer);
100
+ else
101
+ container.insertAfter(newContainer);
79
102
  }
80
103
  return [misraA, misraB];
81
104
  }
82
105
  /**
83
- * Converts every couplet in the block between two-column (built on the
84
- * columns primitive, §4.9) and single-column/alternating (a
85
- * PoetryCoupletNode per couplet) — pulls each couplet's two misra
86
- * paragraphs out of whichever shape currently holds them and rebuilds the
87
- * other shape around the same paragraph nodes, preserving couplet order
88
- * (and dropping any individual 'centered' override, which only applies to
89
- * the non-two-column shape), so content, selection and undo history all
90
- * survive the conversion. Switching between 'single' and 'alternating' is
91
- * purely an attribute change (both use the same PoetryCoupletNode
92
- * structure) — no restructuring needed.
106
+ * The explicit "Insert couplet above/below" menu actions — mirrors the
107
+ * table menu's "Insert row before/after". Targets the couplet the caret is
108
+ * currently in, falling back to the block's last couplet (so the toolbar's
109
+ * always-available insert button has somewhere sensible to act on even
110
+ * without a precise caret position).
111
+ */
112
+ export function $insertCoupletRelativeToSelection(position) {
113
+ const block = $getPoetryBlockFromSelection();
114
+ if (!block)
115
+ return false;
116
+ const selection = $getSelection();
117
+ const couplets = $getCouplets(block);
118
+ const anchorNode = $isRangeSelection(selection) ? selection.anchor.getNode() : null;
119
+ const found = anchorNode ? $findCoupletPosition(couplets, anchorNode) : null;
120
+ const index = found ? found.coupletIndex : couplets.length - 1;
121
+ const target = couplets[index];
122
+ if (!target)
123
+ return false;
124
+ const [newA] = $insertCoupletRelativeTo(target[0], block.getLayout(), position);
125
+ newA.selectStart();
126
+ return true;
127
+ }
128
+ /**
129
+ * Converts every couplet in the block between single-column (two stacked
130
+ * misras) and two-column (built on the columns primitive, §4.9) in place —
131
+ * pulls each couplet's two misra paragraphs out of whichever shape
132
+ * currently holds them and rebuilds the other shape around the same
133
+ * paragraph nodes, preserving couplet order, so content, selection and
134
+ * undo history all survive the conversion.
93
135
  */
94
136
  export function $setPoetryLayout(node, layout) {
95
- const from = node.getLayout();
96
- if (from === layout)
137
+ if (node.getLayout() === layout)
97
138
  return;
98
- const fromTwoColumn = from === 'two-column';
99
- const toTwoColumn = layout === 'two-column';
100
- if (fromTwoColumn === toTwoColumn) {
101
- // Both 'single' and 'alternating' share the same PoetryCoupletNode
102
- // structure — just relabel the block.
103
- node.setLayoutAttribute(layout);
104
- return;
105
- }
106
139
  const couplets = $getCouplets(node);
107
140
  for (const [a, b] of couplets) {
108
141
  a.remove();
@@ -110,74 +143,19 @@ export function $setPoetryLayout(node, layout) {
110
143
  }
111
144
  for (const leftover of node.getChildren())
112
145
  leftover.remove();
113
- for (const [a, b] of couplets) {
114
- if (toTwoColumn) {
146
+ if (layout === 'single') {
147
+ for (const [a, b] of couplets)
148
+ node.append(a, b);
149
+ }
150
+ else {
151
+ for (const [a, b] of couplets) {
115
152
  const container = $createLayoutContainerNode('repeat(2, 1fr)');
116
153
  container.append($createLayoutItemNode().append(a), $createLayoutItemNode().append(b));
117
154
  node.append(container);
118
155
  }
119
- else {
120
- node.append($createPoetryCoupletNode().append(a, b));
121
- }
122
156
  }
123
157
  node.setLayoutAttribute(layout);
124
158
  }
125
- /**
126
- * Sets whether the couplet the selection is inside renders as a narrower,
127
- * centered box ("single, aligned centered") instead of the block's default
128
- * width — independent of the block's own layout. A two-column couplet is
129
- * converted into a PoetryCoupletNode first (its two columns stacked into
130
- * two lines), since "centered" only applies to that shape; a couplet
131
- * that's already a PoetryCoupletNode just has its flag flipped. Returns
132
- * false when the selection isn't inside a couplet.
133
- */
134
- export function $setCoupletCentered(centered) {
135
- const block = $getPoetryBlockFromSelection();
136
- if (!block)
137
- return false;
138
- const selection = $getSelection();
139
- if (!$isRangeSelection(selection))
140
- return false;
141
- const couplets = $getCouplets(block);
142
- const position = $findCoupletPosition(couplets, selection.anchor.getNode());
143
- if (!position)
144
- return false;
145
- const [a, b] = couplets[position.coupletIndex];
146
- const wrapper = $findCoupletWrapper(a);
147
- if (!wrapper)
148
- return false;
149
- if ($isPoetryCoupletNode(wrapper)) {
150
- wrapper.setCentered(centered);
151
- return true;
152
- }
153
- // A two-column couplet: stack its two columns into a centered pair,
154
- // replacing the LayoutContainerNode row with a PoetryCoupletNode in the
155
- // same position.
156
- a.remove();
157
- b.remove();
158
- const coupletNode = $createPoetryCoupletNode(centered).append(a, b);
159
- wrapper.insertAfter(coupletNode);
160
- wrapper.remove();
161
- return true;
162
- }
163
- /** Whether the couplet the selection is inside is currently centered —
164
- * always false for a two-column couplet, since that override only applies
165
- * to the PoetryCoupletNode shape. Null when the selection isn't inside a
166
- * couplet at all (vs. inside one that just isn't centered). */
167
- export function $getCoupletCenteredFromSelection() {
168
- const block = $getPoetryBlockFromSelection();
169
- if (!block)
170
- return null;
171
- const selection = $getSelection();
172
- if (!$isRangeSelection(selection))
173
- return null;
174
- const couplets = $getCouplets(block);
175
- const position = $findCoupletPosition(couplets, selection.anchor.getNode());
176
- if (!position)
177
- return null;
178
- const wrapper = $findCoupletWrapper(couplets[position.coupletIndex][0]);
179
- return $isPoetryCoupletNode(wrapper) ? wrapper.getCentered() : false;
180
- }
181
159
  /** A block at the very end of the document would otherwise leave no
182
160
  * editable block to click or arrow down into — appends one empty paragraph
183
161
  * if the block doesn't already have a next sibling. Idempotent: once a
@@ -191,10 +169,13 @@ export function $ensureTrailingParagraph(node) {
191
169
  }
192
170
  /**
193
171
  * Inserts a poetry couplet at the caret. If the caret is already inside a
194
- * poetry block, appends a new couplet to *that* block (in its existing
195
- * default layout) and focuses its first misra. Otherwise creates a new
196
- * block (in `layout`/`align`) with one couplet, after the selection's
197
- * top-level element.
172
+ * poetry block, inserts a new couplet right after the one the caret is in
173
+ * (same as the "Insert couplet below" menu action — table-row-insert
174
+ * semantics, in its existing layout: "one poetry block can contain one or
175
+ * more couplets, in one or two column layout", so a block's layout is
176
+ * fixed once it has couplets, not chosen per couplet) and focuses its first
177
+ * misra. Otherwise creates a new block (in `layout`/`align`) with one
178
+ * couplet, after the selection's top-level element.
198
179
  */
199
180
  export function $insertPoetryCouplet(layout, align) {
200
181
  const selection = $getSelection();
@@ -202,13 +183,11 @@ export function $insertPoetryCouplet(layout, align) {
202
183
  return false;
203
184
  const existing = $getPoetryBlockFromSelection();
204
185
  if (existing) {
205
- const [misraA] = $appendCoupletTo(existing);
206
- misraA.selectStart();
207
- return true;
186
+ return $insertCoupletRelativeToSelection('after');
208
187
  }
209
188
  const anchorTopLevel = selection.anchor.getNode().getTopLevelElementOrThrow();
210
189
  const node = $createPoetryBlockNode(layout, align);
211
- const [misraA] = $appendCoupletTo(node, layout === 'two-column');
190
+ const [misraA] = $appendCoupletTo(node);
212
191
  anchorTopLevel.insertAfter(node);
213
192
  $ensureTrailingParagraph(node);
214
193
  misraA.selectStart();
@@ -234,7 +213,14 @@ export function $deletePoetryCouplet() {
234
213
  block.remove();
235
214
  return true;
236
215
  }
237
- $findCoupletWrapper(target[0])?.remove();
216
+ const [a, b] = target;
217
+ const container = $findMatchingParent(a, $isLayoutContainerNode);
218
+ if (container)
219
+ container.remove();
220
+ else {
221
+ a.remove();
222
+ b.remove();
223
+ }
238
224
  return true;
239
225
  }
240
226
  /**
@@ -260,11 +246,12 @@ export function $deletePoetryOnBackspace() {
260
246
  const position = $findCoupletPosition(couplets, anchorNode);
261
247
  if (!position || position.misraIndex !== 0)
262
248
  return false;
263
- const [a, b] = couplets[position.coupletIndex];
249
+ const [firstMisra] = couplets[position.coupletIndex];
264
250
  const anchor = selection.anchor;
265
- const isAtStart = anchor.key === a.getKey() && anchor.offset === 0;
251
+ const isAtStart = anchor.key === firstMisra.getKey() && anchor.offset === 0;
266
252
  if (!isAtStart)
267
253
  return false;
254
+ const [a, b] = couplets[position.coupletIndex];
268
255
  if (!a.isEmpty() || !b.isEmpty())
269
256
  return false;
270
257
  if (couplets.length <= 1) {
@@ -274,7 +261,13 @@ export function $deletePoetryOnBackspace() {
274
261
  previous.selectEnd();
275
262
  return true;
276
263
  }
277
- $findCoupletWrapper(a)?.remove();
264
+ const container = $findMatchingParent(a, $isLayoutContainerNode);
265
+ if (container)
266
+ container.remove();
267
+ else {
268
+ a.remove();
269
+ b.remove();
270
+ }
278
271
  const remaining = $getCouplets(block);
279
272
  const previousCouplet = remaining[position.coupletIndex - 1] ?? remaining[0];
280
273
  previousCouplet?.[1].selectEnd();
@@ -315,8 +308,15 @@ export function $exitPoetryOnEnter() {
315
308
  // Drop the couplet we ourselves auto-created on the previous Enter —
316
309
  // unless it's the block's only couplet, in which case leave the
317
310
  // section as the user's (empty) content rather than deleting it.
318
- if (couplets.length > 1)
319
- $findCoupletWrapper(a)?.remove();
311
+ if (couplets.length > 1) {
312
+ const container = $findMatchingParent(a, $isLayoutContainerNode);
313
+ if (container)
314
+ container.remove();
315
+ else {
316
+ a.remove();
317
+ b.remove();
318
+ }
319
+ }
320
320
  const existingNext = block.getNextSibling();
321
321
  const paragraph = $isParagraphNode(existingNext) ? existingNext : $createParagraphNode();
322
322
  if (paragraph !== existingNext)
@@ -336,3 +336,14 @@ export function $exitPoetryOnEnter() {
336
336
  newA.selectStart();
337
337
  return true;
338
338
  }
339
+ /** Steps the caret's poetry block one notch looser (+1) or tighter (-1) on
340
+ * the couplet-spacing scale; clamps at either end. */
341
+ export function $adjustPoetrySpacing(step) {
342
+ const block = $getPoetryBlockFromSelection();
343
+ if (!block)
344
+ return false;
345
+ const index = POETRY_SPACINGS.indexOf(block.getSpacing());
346
+ const next = POETRY_SPACINGS[Math.min(POETRY_SPACINGS.length - 1, Math.max(0, index + step))];
347
+ block.setSpacing(next);
348
+ return true;
349
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAuEA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AA0fvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BA0kCxJ"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAuEA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AAsfvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BA+jCxJ"}
@@ -15,7 +15,7 @@ import { INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
15
15
  import { INSERT_LAYOUT_COMMAND } from '../blocks/LayoutNode.js';
16
16
  import { INSERT_FOOTNOTE_COMMAND } from '../blocks/FootnoteNode.js';
17
17
  import { $isPoetryBlockNode } from '../blocks/PoetryNode.js';
18
- import { $deletePoetryCouplet, $getCoupletCenteredFromSelection, $getPoetryBlockFromSelection, $setCoupletCentered, $setPoetryLayout, } from '../blocks/poetryActions.js';
18
+ import { $adjustPoetrySpacing, $deletePoetryCouplet, $getPoetryBlockFromSelection, $insertCoupletRelativeToSelection, $setPoetryLayout, } from '../blocks/poetryActions.js';
19
19
  import { INSERT_POETRY_COUPLET_COMMAND } from '../plugins/PoetryPlugin.js';
20
20
  import { TableDialog } from './TableDialog.js';
21
21
  import { LayoutDialog } from './LayoutDialog.js';
@@ -29,7 +29,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
29
29
  import { setStyleProperty } from '../utils/style.js';
30
30
  import { useStrings } from '../i18n/useStrings.js';
31
31
  import { usePortalTarget } from '../PortalTargetContext.js';
32
- import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconArrowsShuffle, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
32
+ import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconArrowsMaximize, IconArrowsMinimize, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
33
33
  /** Sentinel for "no inline style": Lexical returns the default it is given when
34
34
  * the selection has none, so this tells "unstyled" (show the canvas default)
35
35
  * apart from "styled with a value the dropdown doesn't list" (show nothing). */
@@ -51,7 +51,6 @@ const INITIAL_STATE = {
51
51
  inPoetry: false,
52
52
  poetryLayout: 'single',
53
53
  poetryAlign: 'justify',
54
- coupletCentered: false,
55
54
  canUndo: false,
56
55
  canRedo: false,
57
56
  };
@@ -163,13 +162,14 @@ function TableMenuItems({ strings, direction, rows, columns, canMergeCells, canU
163
162
  };
164
163
  return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconRowInsertTop, manyRows ? t.insertRowsBefore : t.insertRowBefore, () => $insertTableRows(false)), item(IconRowInsertBottom, manyRows ? t.insertRowsAfter : t.insertRowAfter, () => $insertTableRows(true)), item(rtl ? IconColumnInsertRight : IconColumnInsertLeft, manyColumns ? t.insertColumnsBefore : t.insertColumnBefore, () => $insertTableColumns(false)), item(rtl ? IconColumnInsertLeft : IconColumnInsertRight, manyColumns ? t.insertColumnsAfter : t.insertColumnAfter, () => $insertTableColumns(true)), (canMergeCells || canUnmergeCell) && _jsx(Menu.Divider, {}), canMergeCells && item(IconArrowMergeBoth, t.mergeCells, $mergeTableCells), canUnmergeCell && item(IconArrowsSplit2, t.unmergeCell, $unmergeTableCell), _jsx(Menu.Divider, {}), item(IconRowRemove, manyRows ? t.deleteRows : t.deleteRow, $deleteTableRows), item(IconColumnRemove, manyColumns ? t.deleteColumns : t.deleteColumn, $deleteTableColumns), item(IconTableMinus, t.deleteTable, $deleteTable, 'red')] }));
165
164
  }
166
- /** The poetry couplet menu's contents — block layout (single/two-column/
167
- * alternating), literal alignment (both per-block overrides, requirements
168
- * doc §4.11: "set per CoupletNode, not inherited from a document-wide
169
- * setting"), and a per-couplet "centered" toggle independent of either —
170
- * "two columns with a couplet that is single, aligned centered" mixes a
171
- * centered couplet into an otherwise two-column block. */
172
- function PoetryMenuItems({ strings, alignIcons, layout, align, centered, onAction, }) {
165
+ /** The poetry couplet menu's contents — layout (single/two-column) and
166
+ * literal alignment, both per-instance overrides (requirements doc §4.11:
167
+ * "set per CoupletNode, not inherited from a document-wide setting"), plus
168
+ * table-row-style structural actions (insert a couplet above/below the one
169
+ * the caret is in, delete it) — a poetry block is "one object with multiple
170
+ * couplets, add/remove like table rows", so these mirror TableMenuItems'
171
+ * own insert-before/insert-after/delete-row actions. */
172
+ function PoetryMenuItems({ strings, alignIcons, layout, align, onAction, }) {
173
173
  const t = strings.poetryMenu;
174
174
  const a = strings.toolbar.alignOptions;
175
175
  const item = (icon, label, action, active = false, color) => {
@@ -184,7 +184,7 @@ function PoetryMenuItems({ strings, alignIcons, layout, align, centered, onActio
184
184
  const setAlign = (next) => () => {
185
185
  $getPoetryBlockFromSelection()?.setAlign(next);
186
186
  };
187
- return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconColumns1, t.singleColumn, setLayout('single'), layout === 'single'), item(IconColumns2, t.twoColumn, setLayout('two-column'), layout === 'two-column'), item(IconArrowsShuffle, t.alternating, setLayout('alternating'), layout === 'alternating'), _jsx(Menu.Divider, {}), item(alignIcons.justify ?? IconAlignJustified, a.justify, setAlign('justify'), align === 'justify'), item(alignIcons.start ?? IconAlignLeft, a.start, setAlign('start'), align === 'start'), item(IconAlignLeft, a.left, setAlign('left'), align === 'left'), item(IconAlignRight, a.right, setAlign('right'), align === 'right'), _jsx(Menu.Divider, {}), item(IconAlignCenter, centered ? t.uncenterCouplet : t.centerCouplet, () => $setCoupletCentered(!centered)), _jsx(Menu.Divider, {}), item(IconTrash, t.deleteCouplet, $deletePoetryCouplet, false, 'red')] }));
187
+ return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconColumns1, t.singleColumn, setLayout('single'), layout === 'single'), item(IconColumns2, t.twoColumn, setLayout('two-column'), layout === 'two-column'), _jsx(Menu.Divider, {}), item(alignIcons.justify ?? IconAlignJustified, a.justify, setAlign('justify'), align === 'justify'), item(alignIcons.start ?? IconAlignLeft, a.start, setAlign('start'), align === 'start'), item(IconAlignLeft, a.left, setAlign('left'), align === 'left'), item(IconAlignRight, a.right, setAlign('right'), align === 'right'), _jsx(Menu.Divider, {}), item(IconRowInsertTop, t.insertCoupletBefore, () => $insertCoupletRelativeToSelection('before')), item(IconRowInsertBottom, t.insertCoupletAfter, () => $insertCoupletRelativeToSelection('after')), item(IconArrowsMinimize, t.tighterSpacing, () => $adjustPoetrySpacing(-1)), item(IconArrowsMaximize, t.looserSpacing, () => $adjustPoetrySpacing(1)), item(IconTrash, t.deleteCouplet, $deletePoetryCouplet, false, 'red')] }));
188
188
  }
189
189
  export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
190
190
  const [editor] = useLexicalComposerContext();
@@ -210,16 +210,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
210
210
  if ($isTableSelection(selection)) {
211
211
  const { rows, columns } = $getTableSelectionSize();
212
212
  const canMergeCells = $canMergeSelectedCells();
213
- setState((s) => ({
214
- ...s,
215
- inTable: true,
216
- tableRows: rows,
217
- tableColumns: columns,
218
- canMergeCells,
219
- canUnmergeCell: false,
220
- inPoetry: false,
221
- coupletCentered: false,
222
- }));
213
+ setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns, canMergeCells, canUnmergeCell: false, inPoetry: false }));
223
214
  return;
224
215
  }
225
216
  if (!$isRangeSelection(selection))
@@ -256,7 +247,6 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
256
247
  const elementFormat = ($isElementNode(element) ? element.getFormatType() : 'start') || 'start';
257
248
  const poetryLayout = poetryBlock?.getLayout();
258
249
  const poetryAlign = poetryBlock?.getAlign();
259
- const coupletCentered = $getCoupletCenteredFromSelection();
260
250
  setState((s) => ({
261
251
  ...s,
262
252
  blockType,
@@ -275,7 +265,6 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
275
265
  inPoetry: poetryBlock !== null,
276
266
  poetryLayout: poetryLayout ?? s.poetryLayout,
277
267
  poetryAlign: poetryAlign ?? s.poetryAlign,
278
- coupletCentered: coupletCentered ?? false,
279
268
  }));
280
269
  });
281
270
  }, [editor]);
@@ -821,7 +810,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
821
810
  }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
822
811
  showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout, align: 'justify' }) }))] }, "stubInsert")),
823
812
  config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] }) }, "tableActions")),
824
- config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, centered: state.coupletCentered, onAction: runMenuAction }) })] }) }, "poetryActions")),
813
+ config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, onAction: runMenuAction }) })] }) }, "poetryActions")),
825
814
  showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && _jsx(StubButton, { icon: IconWand, title: strings.toolbar.autocorrect, comingSoon: strings.toolbar.comingSoon }), config.language.textCleanup && _jsx(StubButton, { icon: IconSparkles, title: strings.toolbar.textCleanup, comingSoon: strings.toolbar.comingSoon }), config.language.spellCheck && _jsx(StubButton, { icon: IconAbc, title: strings.toolbar.spellChecker, comingSoon: strings.toolbar.comingSoon })] }, "language")),
826
815
  ];
827
816
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
@@ -840,7 +829,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
840
829
  }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: tableContext?.x ?? -9999, top: tableContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: tableContext?.rows ?? 1, columns: tableContext?.columns ?? 1, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.poetry.enabled && (_jsxs(Menu, { opened: poetryContext !== null, onChange: (opened) => {
841
830
  if (!opened)
842
831
  setPoetryContext(null);
843
- }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: poetryContext?.x ?? -9999, top: poetryContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, centered: state.coupletCentered, onAction: runMenuAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
832
+ }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: poetryContext?.x ?? -9999, top: poetryContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, onAction: runMenuAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
844
833
  position: 'absolute',
845
834
  visibility: 'hidden',
846
835
  pointerEvents: 'none',
@@ -142,9 +142,10 @@ export interface Strings {
142
142
  menuLabel: string;
143
143
  singleColumn: string;
144
144
  twoColumn: string;
145
- alternating: string;
146
- centerCouplet: string;
147
- uncenterCouplet: string;
145
+ insertCoupletBefore: string;
146
+ insertCoupletAfter: string;
147
+ tighterSpacing: string;
148
+ looserSpacing: string;
148
149
  deleteCouplet: string;
149
150
  };
150
151
  link: {
@@ -1 +1 @@
1
- {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
@@ -125,9 +125,10 @@ const en = {
125
125
  menuLabel: 'Poetry',
126
126
  singleColumn: 'Single column',
127
127
  twoColumn: 'Two column',
128
- alternating: 'Alternating',
129
- centerCouplet: 'Center this couplet',
130
- uncenterCouplet: 'Un-center this couplet',
128
+ insertCoupletBefore: 'Insert couplet above',
129
+ insertCoupletAfter: 'Insert couplet below',
130
+ tighterSpacing: 'Tighter couplet spacing',
131
+ looserSpacing: 'Looser couplet spacing',
131
132
  deleteCouplet: 'Delete couplet',
132
133
  },
133
134
  link: {
@@ -336,9 +337,10 @@ const ur = {
336
337
  menuLabel: 'شاعری',
337
338
  singleColumn: 'ایک کالم',
338
339
  twoColumn: 'دو کالم',
339
- alternating: 'باری باری',
340
- centerCouplet: 'یہ شعر وسط میں رکھیں',
341
- uncenterCouplet: 'وسط میں رکھنا ختم کریں',
340
+ insertCoupletBefore: 'اوپر شعر شامل کریں',
341
+ insertCoupletAfter: 'نیچے شعر شامل کریں',
342
+ tighterSpacing: 'شعروں کے درمیان کم جگہ',
343
+ looserSpacing: 'شعروں کے درمیان زیادہ جگہ',
342
344
  deleteCouplet: 'شعر حذف کریں',
343
345
  },
344
346
  link: {
@@ -547,9 +549,10 @@ const paShahmukhi = {
547
549
  menuLabel: 'شاعری',
548
550
  singleColumn: 'اک کالم',
549
551
  twoColumn: 'دو کالم',
550
- alternating: 'واری واری',
551
- centerCouplet: 'ایہ شعر وچکار رکھو',
552
- uncenterCouplet: 'وچکار رکھنا بند کرو',
552
+ insertCoupletBefore: 'اُتے شعر شامل کرو',
553
+ insertCoupletAfter: 'ھیٹھ شعر شامل کرو',
554
+ tighterSpacing: 'شعراں وچکار گھٹ جگہ',
555
+ looserSpacing: 'شعراں وچکار ودھ جگہ',
553
556
  deleteCouplet: 'شعر مٹاؤ',
554
557
  },
555
558
  link: {
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAclD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAmB5C,CAAC"}
1
+ {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAalD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAkB5C,CAAC"}
package/dist/nodes.js CHANGED
@@ -9,7 +9,6 @@ import { LayoutContainerNode, LayoutItemNode } from './blocks/LayoutNode.js';
9
9
  import { FootnoteReferenceNode } from './blocks/FootnoteNode.js';
10
10
  import { FootnoteItemNode, FootnoteListNode } from './blocks/FootnoteListNode.js';
11
11
  import { PoetryBlockNode } from './blocks/PoetryNode.js';
12
- import { PoetryCoupletNode } from './blocks/PoetryCoupletNode.js';
13
12
  /**
14
13
  * Node types registered on the editor at construction time. Per
15
14
  * editor-architecture-design.md §2, node registration cannot be conditional
@@ -37,5 +36,4 @@ export const EDITOR_NODES = [
37
36
  FootnoteListNode,
38
37
  FootnoteItemNode,
39
38
  PoetryBlockNode,
40
- PoetryCoupletNode,
41
39
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAIjB,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;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,SA4C3B"}
1
+ {"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAGjB,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;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,SA4C3B"}
@@ -3,7 +3,6 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
3
3
  import { COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, KEY_BACKSPACE_COMMAND, KEY_ENTER_COMMAND, createCommand, } from 'lexical';
4
4
  import { mergeRegister } from '@lexical/utils';
5
5
  import { LayoutContainerNode, LayoutItemNode } from '../blocks/LayoutNode.js';
6
- import { PoetryCoupletNode } from '../blocks/PoetryCoupletNode.js';
7
6
  import { PoetryBlockNode } from '../blocks/PoetryNode.js';
8
7
  import { $deletePoetryOnBackspace, $ensureTrailingParagraph, $exitPoetryOnEnter, $insertPoetryCouplet } from '../blocks/poetryActions.js';
9
8
  export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLET_COMMAND');
@@ -22,8 +21,8 @@ export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLE
22
21
  export function PoetryPlugin() {
23
22
  const [editor] = useLexicalComposerContext();
24
23
  useEffect(() => {
25
- if (!editor.hasNodes([PoetryBlockNode, PoetryCoupletNode, LayoutContainerNode, LayoutItemNode])) {
26
- throw new Error('PoetryPlugin: PoetryBlockNode/PoetryCoupletNode (and the columns nodes two-column composes) not registered on the editor');
24
+ if (!editor.hasNodes([PoetryBlockNode, LayoutContainerNode, LayoutItemNode])) {
25
+ throw new Error('PoetryPlugin: PoetryBlockNode (and the columns nodes it composes) not registered on the editor');
27
26
  }
28
27
  return mergeRegister(editor.registerCommand(INSERT_POETRY_COUPLET_COMMAND, ({ layout, align }) => $insertPoetryCouplet(layout, align), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(PoetryBlockNode, (node) => {
29
28
  if (node.getChildrenSize() === 0) {
@@ -407,75 +407,56 @@
407
407
  /* Misra lines sit tight against each other within a couplet — the generic
408
408
  .likhari-paragraph margin would otherwise add the same gap between a
409
409
  couplet's own two lines as between separate couplets within the same
410
- block, losing the pairing entirely. A block's children are a free mix of
411
- .likhari-poetry-couplet (single-column/alternating/centered) and
412
- .likhari-layout-container (two-column) — one wrapper box per couplet,
413
- regardless of type — so "extra space after every couplet" is just
414
- margin-bottom on every non-last couplet of either kind. */
410
+ block, losing the pairing entirely. */
415
411
  .likhari-poetry .likhari-paragraph {
416
412
  margin: 0;
417
413
  }
418
414
 
419
- .likhari-poetry > .likhari-poetry-couplet:not(:last-child),
420
- .likhari-poetry > .likhari-layout-container:not(:last-child) {
421
- margin-bottom: 1em;
415
+ /* A poetry block is "one object with multiple couplets, add/remove like
416
+ table rows" — so couplet boundaries get the same visible divider as a
417
+ table's internal row borders, not just extra space. Every couplet but
418
+ the first (nth-child(odd), since each couplet is two paragraph
419
+ children — its first misra always lands on an odd position) gets a
420
+ border-top in place of the previous couplet's margin-bottom. */
421
+ .likhari-poetry--single .likhari-paragraph:nth-child(odd):not(:first-child) {
422
+ border-top: 1px dashed var(--editor-line-strong);
423
+ margin-top: var(--poetry-couplet-gap);
424
+ padding-top: calc(var(--poetry-couplet-gap) * 0.6);
422
425
  }
423
426
 
424
- .likhari-poetry-couplet {
425
- box-sizing: border-box;
426
- }
427
-
428
- /* "Two columns with a couplet that is single, aligned centered": a couplet
429
- can individually override to a narrower, page-centered box regardless of
430
- the block's own layout — the same capped-width + auto-margin centering
431
- .likhari-poetry itself uses, scoped to just this one couplet. */
432
- .likhari-poetry-couplet--centered {
433
- width: fit-content;
434
- max-width: 70%;
435
- margin-inline: auto;
436
- }
437
-
438
- /* "Aligned alternative": every couplet is the same fixed width, but
439
- consecutive couplets alternate which edge they're pinned to — since both
440
- widths overlap past the midpoint, the couplets visually stagger with
441
- some overlap toward the center, rather than all stacking flush to one
442
- side or centered identically. */
443
- .likhari-poetry--alternating .likhari-poetry-couplet {
444
- width: 65%;
445
- max-width: none;
446
- }
447
-
448
- .likhari-poetry--alternating .likhari-poetry-couplet:nth-of-type(odd) {
449
- margin-inline-end: auto;
450
- }
427
+ .likhari-poetry--spacing-compact { --poetry-couplet-gap: 0.4em; }
428
+ .likhari-poetry--spacing-normal { --poetry-couplet-gap: 1em; }
429
+ .likhari-poetry--spacing-relaxed { --poetry-couplet-gap: 1.8em; }
430
+ .likhari-poetry--spacing-loose { --poetry-couplet-gap: 2.8em; }
451
431
 
452
- .likhari-poetry--alternating .likhari-poetry-couplet:nth-of-type(even) {
453
- margin-inline-start: auto;
454
- }
455
-
456
- /* Only inside a poetry block's own two-column couplets: no block margin
457
- (the generic .likhari-layout-container rule's margin is for a
458
- stand-alone Columns insert, not one couplet of several in a poetry
459
- block — spacing between couplets is the margin-bottom rule above
460
- instead) and no grid gap — the divider below sits exactly on the track
461
- boundary, so spacing instead comes from matching padding on each side
462
- of it, which keeps the gutter visually even on both sides of the line. */
463
- .likhari-poetry > .likhari-layout-container {
432
+ .likhari-poetry--two-column .likhari-layout-container {
464
433
  margin: 0;
434
+ /* No grid gap — the column divider sits exactly on the track boundary,
435
+ so spacing instead comes from matching padding on each side of it,
436
+ which keeps the gutter visually even on both sides of the line. */
465
437
  gap: 0;
466
438
  }
467
439
 
468
- .likhari-poetry .likhari-layout-item {
440
+ /* One row per couplet in two-column layout — same table-row-border
441
+ treatment as the single-column rule above, between couplets instead of
442
+ within one. */
443
+ .likhari-poetry--two-column .likhari-layout-container:not(:first-child) {
444
+ border-top: 1px dashed var(--editor-line-strong);
445
+ margin-top: var(--poetry-couplet-gap);
446
+ padding-top: calc(var(--poetry-couplet-gap) * 0.6);
447
+ }
448
+
449
+ .likhari-poetry--two-column .likhari-layout-item {
469
450
  text-align: inherit;
470
451
  }
471
452
 
472
- .likhari-poetry .likhari-layout-item:first-child {
453
+ .likhari-poetry--two-column .likhari-layout-item:first-child {
473
454
  padding-inline-end: 20px;
474
455
  }
475
456
 
476
457
  /* A visible divider between misra columns, same dashed language as the
477
458
  couplet's own border — matches a table's internal cell borders. */
478
- .likhari-poetry .likhari-layout-item:not(:first-child) {
459
+ .likhari-poetry--two-column .likhari-layout-item:not(:first-child) {
479
460
  border-inline-start: 1px dashed var(--editor-line-strong);
480
461
  padding-inline-start: 20px;
481
462
  }
@@ -1 +1 @@
1
- {"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,kBAiDzB,CAAC"}
1
+ {"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,kBAgDzB,CAAC"}
@@ -46,7 +46,6 @@ export const editorTheme = {
46
46
  footnoteList: 'likhari-footnote-list',
47
47
  footnoteItem: 'likhari-footnote-item',
48
48
  poetry: 'likhari-poetry',
49
- poetryCouplet: 'likhari-poetry-couplet',
50
49
  table: 'likhari-table',
51
50
  tableRow: 'likhari-table-row',
52
51
  tableCell: 'likhari-table-cell',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.18-dev.154",
3
+ "version": "0.1.18-dev.158",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,36 +0,0 @@
1
- import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalNode, type NodeKey, type SerializedElementNode, type Spread, ElementNode } from 'lexical';
2
- export type SerializedPoetryCoupletNode = Spread<{
3
- centered: boolean;
4
- }, SerializedElementNode>;
5
- /**
6
- * One couplet's two misra lines, wrapped so each couplet can carry its own
7
- * rendering override independent of its PoetryBlockNode's overall layout —
8
- * currently just `centered` (a narrower box, centered on the page, instead
9
- * of the block's default width): "two columns with a couplet that is
10
- * single, aligned centered" mixes LayoutContainerNode rows (two-column
11
- * couplets) with centered PoetryCoupletNodes in the same block, each
12
- * couplet choosing independently.
13
- *
14
- * Used for single-column and alternating block layouts; a two-column
15
- * couplet uses LayoutContainerNode instead (its own two-item row already
16
- * gives it a natural per-couplet box, so it doesn't need this wrapper).
17
- */
18
- export declare class PoetryCoupletNode extends ElementNode {
19
- __centered: boolean;
20
- constructor(centered?: boolean, key?: NodeKey);
21
- static getType(): string;
22
- static clone(node: PoetryCoupletNode): PoetryCoupletNode;
23
- getCentered(): boolean;
24
- setCentered(centered: boolean): this;
25
- static importJSON(serializedNode: SerializedPoetryCoupletNode): PoetryCoupletNode;
26
- exportJSON(): SerializedPoetryCoupletNode;
27
- static importDOM(): DOMConversionMap | null;
28
- exportDOM(): DOMExportOutput;
29
- createDOM(config: EditorConfig): HTMLElement;
30
- updateDOM(prevNode: PoetryCoupletNode, dom: HTMLElement, config: EditorConfig): boolean;
31
- canBeEmpty(): false;
32
- canIndent(): false;
33
- }
34
- export declare function $createPoetryCoupletNode(centered?: boolean): PoetryCoupletNode;
35
- export declare function $isPoetryCoupletNode(node: LexicalNode | null | undefined): node is PoetryCoupletNode;
36
- //# sourceMappingURL=PoetryCoupletNode.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PoetryCoupletNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryCoupletNode.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,EAAE,qBAAqB,CAAC,CAAC;AAE/F;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,UAAU,EAAE,OAAO,CAAC;gBAER,QAAQ,UAAQ,EAAE,GAAG,CAAC,EAAE,OAAO;IAK3C,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB;IAIxD,WAAW,IAAI,OAAO;IAItB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAMpC,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,2BAA2B,GAAG,iBAAiB;IAIjF,UAAU,IAAI,2BAA2B;IAIzC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAU3C,SAAS,IAAI,eAAe;IAO5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAQ5C,SAAS,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAQvF,UAAU,IAAI,KAAK;IAInB,SAAS,IAAI,KAAK;CAGnB;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,UAAQ,GAAG,iBAAiB,CAE5E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,iBAAiB,CAEpG"}
@@ -1,85 +0,0 @@
1
- import { addClassNamesToElement } from '@lexical/utils';
2
- import { $applyNodeReplacement, ElementNode, } from 'lexical';
3
- /**
4
- * One couplet's two misra lines, wrapped so each couplet can carry its own
5
- * rendering override independent of its PoetryBlockNode's overall layout —
6
- * currently just `centered` (a narrower box, centered on the page, instead
7
- * of the block's default width): "two columns with a couplet that is
8
- * single, aligned centered" mixes LayoutContainerNode rows (two-column
9
- * couplets) with centered PoetryCoupletNodes in the same block, each
10
- * couplet choosing independently.
11
- *
12
- * Used for single-column and alternating block layouts; a two-column
13
- * couplet uses LayoutContainerNode instead (its own two-item row already
14
- * gives it a natural per-couplet box, so it doesn't need this wrapper).
15
- */
16
- export class PoetryCoupletNode extends ElementNode {
17
- constructor(centered = false, key) {
18
- super(key);
19
- this.__centered = centered;
20
- }
21
- static getType() {
22
- return 'poetry-couplet';
23
- }
24
- static clone(node) {
25
- return new PoetryCoupletNode(node.__centered, node.__key);
26
- }
27
- getCentered() {
28
- return this.getLatest().__centered;
29
- }
30
- setCentered(centered) {
31
- const writable = this.getWritable();
32
- writable.__centered = centered;
33
- return writable;
34
- }
35
- static importJSON(serializedNode) {
36
- return $createPoetryCoupletNode(serializedNode.centered);
37
- }
38
- exportJSON() {
39
- return { ...super.exportJSON(), type: 'poetry-couplet', version: 1, centered: this.getCentered() };
40
- }
41
- static importDOM() {
42
- return {
43
- div: (domNode) => {
44
- if (!domNode.hasAttribute('data-likhari-poetry-couplet'))
45
- return null;
46
- const centered = domNode.getAttribute('data-likhari-poetry-couplet-centered') === 'true';
47
- return { conversion: () => ({ node: $createPoetryCoupletNode(centered) }), priority: 2 };
48
- },
49
- };
50
- }
51
- exportDOM() {
52
- const element = document.createElement('div');
53
- element.setAttribute('data-likhari-poetry-couplet', '');
54
- if (this.getCentered())
55
- element.setAttribute('data-likhari-poetry-couplet-centered', 'true');
56
- return { element };
57
- }
58
- createDOM(config) {
59
- const element = document.createElement('div');
60
- const base = config.theme.poetryCouplet ?? 'likhari-poetry-couplet';
61
- addClassNamesToElement(element, base);
62
- if (this.__centered)
63
- element.classList.add(`${base}--centered`);
64
- return element;
65
- }
66
- updateDOM(prevNode, dom, config) {
67
- if (prevNode.__centered !== this.__centered) {
68
- const base = config.theme.poetryCouplet ?? 'likhari-poetry-couplet';
69
- dom.classList.toggle(`${base}--centered`, this.__centered);
70
- }
71
- return false;
72
- }
73
- canBeEmpty() {
74
- return false;
75
- }
76
- canIndent() {
77
- return false;
78
- }
79
- }
80
- export function $createPoetryCoupletNode(centered = false) {
81
- return $applyNodeReplacement(new PoetryCoupletNode(centered));
82
- }
83
- export function $isPoetryCoupletNode(node) {
84
- return node instanceof PoetryCoupletNode;
85
- }