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

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.
@@ -0,0 +1,36 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,85 @@
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
+ }
@@ -1,5 +1,5 @@
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';
2
+ export type PoetryLayout = 'single' | 'two-column' | 'alternating';
3
3
  export type PoetryAlign = 'justify' | 'left' | 'right' | 'start';
4
4
  export type SerializedPoetryBlockNode = Spread<{
5
5
  layout: PoetryLayout;
@@ -7,17 +7,18 @@ export type SerializedPoetryBlockNode = Spread<{
7
7
  width?: number;
8
8
  }, SerializedElementNode>;
9
9
  /**
10
- * A poetry *section*: one or more couplets (two-line verse units) sharing
11
- * one layout — single-column stacks each couplet's two misras directly as
12
- * paragraph children (two per couplet, in order), two-column gives each
13
- * couplet its own LayoutContainerNode row (built on the columns primitive,
14
- * §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
15
- * is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
16
- * restructured/grown between couplets and layouts. `layout` and `align`
17
- * apply to the whole section, not per couplet; both mutate in place via
18
- * poetryActions so converting a section between layouts doesn't lose
19
- * selection/undo coherence. One extensible primitive in place of four
20
- * fixed templates, per requirements doc §4.11.
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.
21
22
  */
22
23
  export declare class PoetryBlockNode extends ElementNode {
23
24
  __layout: PoetryLayout;
@@ -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,CAAC;AACnD,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;;;;;;;;;;;;GAYG;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,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,6 +1,6 @@
1
1
  import { addClassNamesToElement } from '@lexical/utils';
2
2
  import { $applyNodeReplacement, ElementNode, } from 'lexical';
3
- const VALID_LAYOUTS = new Set(['single', 'two-column']);
3
+ const VALID_LAYOUTS = new Set(['single', 'two-column', 'alternating']);
4
4
  const VALID_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
5
5
  /** `text-align: justify` only stretches a line that actually wraps — a
6
6
  * misra short enough to fit on one line would otherwise just sit at its own
@@ -22,17 +22,18 @@ function applyWidthStyle(element, width) {
22
22
  element.style.maxWidth = `${width}px`;
23
23
  }
24
24
  /**
25
- * A poetry *section*: one or more couplets (two-line verse units) sharing
26
- * one layout — single-column stacks each couplet's two misras directly as
27
- * paragraph children (two per couplet, in order), two-column gives each
28
- * couplet its own LayoutContainerNode row (built on the columns primitive,
29
- * §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
30
- * is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
31
- * restructured/grown between couplets and layouts. `layout` and `align`
32
- * apply to the whole section, not per couplet; both mutate in place via
33
- * poetryActions so converting a section between layouts doesn't lose
34
- * selection/undo coherence. One extensible primitive in place of four
35
- * fixed templates, per requirements doc §4.11.
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.
36
37
  */
37
38
  export class PoetryBlockNode extends ElementNode {
38
39
  constructor(layout = 'single', align = 'justify', width, key) {
@@ -42,7 +43,7 @@ export class PoetryBlockNode extends ElementNode {
42
43
  this.__width = width;
43
44
  }
44
45
  static getType() {
45
- return 'poetry-couplet';
46
+ return 'poetry-block';
46
47
  }
47
48
  static clone(node) {
48
49
  return new PoetryBlockNode(node.__layout, node.__align, node.__width, node.__key);
@@ -81,7 +82,7 @@ export class PoetryBlockNode extends ElementNode {
81
82
  const width = this.getWidth();
82
83
  return {
83
84
  ...super.exportJSON(),
84
- type: 'poetry-couplet',
85
+ type: 'poetry-block',
85
86
  version: 1,
86
87
  layout: this.getLayout(),
87
88
  align: this.getAlign(),
@@ -3,12 +3,13 @@ import { type PoetryAlign, type PoetryLayout, type PoetryBlockNode } from './Poe
3
3
  /** One couplet's two misra paragraphs, in order. */
4
4
  export type Couplet = [ParagraphNode, ParagraphNode];
5
5
  /**
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.
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.
12
13
  */
13
14
  export declare function $getCouplets(node: PoetryBlockNode): Couplet[];
14
15
  /** Every misra in the block, flattened across all its couplets — for
@@ -18,14 +19,33 @@ export declare function $getMisraParagraphs(node: PoetryBlockNode): ParagraphNod
18
19
  /** The PoetryBlockNode the current selection is inside, or null. */
19
20
  export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
20
21
  /**
21
- * Converts every couplet in the block between single-column (two stacked
22
- * misras) and two-column (built on the columns primitive, §4.9) in place —
23
- * pulls each couplet's two misra paragraphs out of whichever shape
24
- * currently holds them and rebuilds the other shape around the same
25
- * paragraph nodes, preserving couplet order, so content, selection and
26
- * undo history all survive the conversion.
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.
27
32
  */
28
33
  export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
34
+ /**
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.
42
+ */
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;
29
49
  /** A block at the very end of the document would otherwise leave no
30
50
  * editable block to click or arrow down into — appends one empty paragraph
31
51
  * if the block doesn't already have a next sibling. Idempotent: once a
@@ -37,10 +57,8 @@ export declare function $ensureTrailingParagraph(node: PoetryBlockNode): void;
37
57
  /**
38
58
  * Inserts a poetry couplet at the caret. If the caret is already inside a
39
59
  * poetry block, appends a new couplet to *that* block (in its existing
40
- * layout — "one poetry block can contain one or more couplets, in one or
41
- * two column layout", so a block's layout is fixed once it has couplets,
42
- * not chosen per couplet) and focuses its first misra. Otherwise creates a
43
- * new block (in `layout`/`align`) with one couplet, after the selection's
60
+ * default layout) and focuses its first misra. Otherwise creates a new
61
+ * block (in `layout`/`align`) with one couplet, after the selection's
44
62
  * top-level element.
45
63
  */
46
64
  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,EAA4F,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvI,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;;;;;;;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;AA2BD;;;;;;;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;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAmBtF;AAED;;;;wCAIwC;AACxC,wBAAgB,oBAAoB,IAAI,OAAO,CAwB9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAqClD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAgD5C"}
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,31 +1,32 @@
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';
4
5
  import { $createPoetryBlockNode, $isPoetryBlockNode } from './PoetryNode.js';
5
6
  /**
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.
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.
12
14
  */
13
15
  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);
22
16
  const couplets = [];
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]);
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
+ }
29
30
  }
30
31
  return couplets;
31
32
  }
@@ -55,30 +56,53 @@ function $findCoupletPosition(couplets, target) {
55
56
  }
56
57
  return null;
57
58
  }
58
- function $appendCoupletTo(node) {
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) {
59
69
  const misraA = $createParagraphNode();
60
70
  const misraB = $createParagraphNode();
61
- if (node.getLayout() === 'single') {
62
- node.append(misraA, misraB);
63
- }
64
- else {
71
+ const useTwoColumn = twoColumn ?? node.getLayout() === 'two-column';
72
+ if (useTwoColumn) {
65
73
  const container = $createLayoutContainerNode('repeat(2, 1fr)');
66
74
  container.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
67
75
  node.append(container);
68
76
  }
77
+ else {
78
+ node.append($createPoetryCoupletNode().append(misraA, misraB));
79
+ }
69
80
  return [misraA, misraB];
70
81
  }
71
82
  /**
72
- * Converts every couplet in the block between single-column (two stacked
73
- * misras) and two-column (built on the columns primitive, §4.9) in place —
74
- * pulls each couplet's two misra paragraphs out of whichever shape
75
- * currently holds them and rebuilds the other shape around the same
76
- * paragraph nodes, preserving couplet order, so content, selection and
77
- * undo history all survive the conversion.
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.
78
93
  */
79
94
  export function $setPoetryLayout(node, layout) {
80
- if (node.getLayout() === layout)
95
+ const from = node.getLayout();
96
+ if (from === layout)
97
+ 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);
81
104
  return;
105
+ }
82
106
  const couplets = $getCouplets(node);
83
107
  for (const [a, b] of couplets) {
84
108
  a.remove();
@@ -86,19 +110,74 @@ export function $setPoetryLayout(node, layout) {
86
110
  }
87
111
  for (const leftover of node.getChildren())
88
112
  leftover.remove();
89
- if (layout === 'single') {
90
- for (const [a, b] of couplets)
91
- node.append(a, b);
92
- }
93
- else {
94
- for (const [a, b] of couplets) {
113
+ for (const [a, b] of couplets) {
114
+ if (toTwoColumn) {
95
115
  const container = $createLayoutContainerNode('repeat(2, 1fr)');
96
116
  container.append($createLayoutItemNode().append(a), $createLayoutItemNode().append(b));
97
117
  node.append(container);
98
118
  }
119
+ else {
120
+ node.append($createPoetryCoupletNode().append(a, b));
121
+ }
99
122
  }
100
123
  node.setLayoutAttribute(layout);
101
124
  }
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
+ }
102
181
  /** A block at the very end of the document would otherwise leave no
103
182
  * editable block to click or arrow down into — appends one empty paragraph
104
183
  * if the block doesn't already have a next sibling. Idempotent: once a
@@ -113,10 +192,8 @@ export function $ensureTrailingParagraph(node) {
113
192
  /**
114
193
  * Inserts a poetry couplet at the caret. If the caret is already inside a
115
194
  * poetry block, appends a new couplet to *that* block (in its existing
116
- * layout — "one poetry block can contain one or more couplets, in one or
117
- * two column layout", so a block's layout is fixed once it has couplets,
118
- * not chosen per couplet) and focuses its first misra. Otherwise creates a
119
- * new block (in `layout`/`align`) with one couplet, after the selection's
195
+ * default layout) and focuses its first misra. Otherwise creates a new
196
+ * block (in `layout`/`align`) with one couplet, after the selection's
120
197
  * top-level element.
121
198
  */
122
199
  export function $insertPoetryCouplet(layout, align) {
@@ -131,7 +208,7 @@ export function $insertPoetryCouplet(layout, align) {
131
208
  }
132
209
  const anchorTopLevel = selection.anchor.getNode().getTopLevelElementOrThrow();
133
210
  const node = $createPoetryBlockNode(layout, align);
134
- const [misraA] = $appendCoupletTo(node);
211
+ const [misraA] = $appendCoupletTo(node, layout === 'two-column');
135
212
  anchorTopLevel.insertAfter(node);
136
213
  $ensureTrailingParagraph(node);
137
214
  misraA.selectStart();
@@ -157,14 +234,7 @@ export function $deletePoetryCouplet() {
157
234
  block.remove();
158
235
  return true;
159
236
  }
160
- const [a, b] = target;
161
- const container = $findMatchingParent(a, $isLayoutContainerNode);
162
- if (container)
163
- container.remove();
164
- else {
165
- a.remove();
166
- b.remove();
167
- }
237
+ $findCoupletWrapper(target[0])?.remove();
168
238
  return true;
169
239
  }
170
240
  /**
@@ -190,12 +260,11 @@ export function $deletePoetryOnBackspace() {
190
260
  const position = $findCoupletPosition(couplets, anchorNode);
191
261
  if (!position || position.misraIndex !== 0)
192
262
  return false;
193
- const [firstMisra] = couplets[position.coupletIndex];
263
+ const [a, b] = couplets[position.coupletIndex];
194
264
  const anchor = selection.anchor;
195
- const isAtStart = anchor.key === firstMisra.getKey() && anchor.offset === 0;
265
+ const isAtStart = anchor.key === a.getKey() && anchor.offset === 0;
196
266
  if (!isAtStart)
197
267
  return false;
198
- const [a, b] = couplets[position.coupletIndex];
199
268
  if (!a.isEmpty() || !b.isEmpty())
200
269
  return false;
201
270
  if (couplets.length <= 1) {
@@ -205,13 +274,7 @@ export function $deletePoetryOnBackspace() {
205
274
  previous.selectEnd();
206
275
  return true;
207
276
  }
208
- const container = $findMatchingParent(a, $isLayoutContainerNode);
209
- if (container)
210
- container.remove();
211
- else {
212
- a.remove();
213
- b.remove();
214
- }
277
+ $findCoupletWrapper(a)?.remove();
215
278
  const remaining = $getCouplets(block);
216
279
  const previousCouplet = remaining[position.coupletIndex - 1] ?? remaining[0];
217
280
  previousCouplet?.[1].selectEnd();
@@ -252,15 +315,8 @@ export function $exitPoetryOnEnter() {
252
315
  // Drop the couplet we ourselves auto-created on the previous Enter —
253
316
  // unless it's the block's only couplet, in which case leave the
254
317
  // section as the user's (empty) content rather than deleting it.
255
- if (couplets.length > 1) {
256
- const container = $findMatchingParent(a, $isLayoutContainerNode);
257
- if (container)
258
- container.remove();
259
- else {
260
- a.remove();
261
- b.remove();
262
- }
263
- }
318
+ if (couplets.length > 1)
319
+ $findCoupletWrapper(a)?.remove();
264
320
  const existingNext = block.getNextSibling();
265
321
  const paragraph = $isParagraphNode(existingNext) ? existingNext : $createParagraphNode();
266
322
  if (paragraph !== existingNext)
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAiEA,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;AA4evD,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"}
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"}
@@ -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, $getPoetryBlockFromSelection, $setPoetryLayout } from '../blocks/poetryActions.js';
18
+ import { $deletePoetryCouplet, $getCoupletCenteredFromSelection, $getPoetryBlockFromSelection, $setCoupletCentered, $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, 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, IconArrowsShuffle, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, 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,6 +51,7 @@ const INITIAL_STATE = {
51
51
  inPoetry: false,
52
52
  poetryLayout: 'single',
53
53
  poetryAlign: 'justify',
54
+ coupletCentered: false,
54
55
  canUndo: false,
55
56
  canRedo: false,
56
57
  };
@@ -162,10 +163,13 @@ function TableMenuItems({ strings, direction, rows, columns, canMergeCells, canU
162
163
  };
163
164
  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')] }));
164
165
  }
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"). */
168
- function PoetryMenuItems({ strings, alignIcons, layout, align, onAction, }) {
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, }) {
169
173
  const t = strings.poetryMenu;
170
174
  const a = strings.toolbar.alignOptions;
171
175
  const item = (icon, label, action, active = false, color) => {
@@ -180,7 +184,7 @@ function PoetryMenuItems({ strings, alignIcons, layout, align, onAction, }) {
180
184
  const setAlign = (next) => () => {
181
185
  $getPoetryBlockFromSelection()?.setAlign(next);
182
186
  };
183
- 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(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'), 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')] }));
184
188
  }
185
189
  export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
186
190
  const [editor] = useLexicalComposerContext();
@@ -206,7 +210,16 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
206
210
  if ($isTableSelection(selection)) {
207
211
  const { rows, columns } = $getTableSelectionSize();
208
212
  const canMergeCells = $canMergeSelectedCells();
209
- setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns, canMergeCells, canUnmergeCell: false, inPoetry: false }));
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
+ }));
210
223
  return;
211
224
  }
212
225
  if (!$isRangeSelection(selection))
@@ -243,6 +256,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
243
256
  const elementFormat = ($isElementNode(element) ? element.getFormatType() : 'start') || 'start';
244
257
  const poetryLayout = poetryBlock?.getLayout();
245
258
  const poetryAlign = poetryBlock?.getAlign();
259
+ const coupletCentered = $getCoupletCenteredFromSelection();
246
260
  setState((s) => ({
247
261
  ...s,
248
262
  blockType,
@@ -261,6 +275,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
261
275
  inPoetry: poetryBlock !== null,
262
276
  poetryLayout: poetryLayout ?? s.poetryLayout,
263
277
  poetryAlign: poetryAlign ?? s.poetryAlign,
278
+ coupletCentered: coupletCentered ?? false,
264
279
  }));
265
280
  });
266
281
  }, [editor]);
@@ -806,7 +821,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
806
821
  }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
807
822
  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")),
808
823
  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")),
809
- 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")),
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")),
810
825
  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")),
811
826
  ];
812
827
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
@@ -825,7 +840,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
825
840
  }, 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) => {
826
841
  if (!opened)
827
842
  setPoetryContext(null);
828
- }, 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: {
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: {
829
844
  position: 'absolute',
830
845
  visibility: 'hidden',
831
846
  pointerEvents: 'none',
@@ -142,6 +142,9 @@ 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
148
  deleteCouplet: string;
146
149
  };
147
150
  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":"AAAA;;;;;GAKG;AAEH;+DAC+D;AAC/D,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;AAElD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE;QACN,mGAAmG;QACnG,WAAW,EAAE,MAAM,CAAC;QACpB,gDAAgD;QAChD,YAAY,EAAE,MAAM,CAAC;QACrB,sEAAsE;QACtE,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,OAAO,EAAE;QACP,gEAAgE;QAChE,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,0CAA0C;QAC1C,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,QAAQ,EAAE,MAAM,CAAC;QACjB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,8FAA8F;QAC9F,YAAY,EAAE,MAAM,CAAC;QACrB,0GAA0G;QAC1G,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QACtC,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE;YACjB,SAAS,EAAE,MAAM,CAAC;YAClB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;YACnC,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,MAAM,CAAC;YACnB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,YAAY,EAAE;YACZ,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;YACf,GAAG,EAAE,MAAM,CAAC;YACZ,OAAO,EAAE,MAAM,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;IACF,uIAAuI;IACvI,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,2FAA2F;QAC3F,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,oGAAoG;IACpG,MAAM,EAAE;QACN,0GAA0G;QAC1G,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QACxC,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,2DAA2D;QAC3D,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QACtC,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QAClC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QAC7B,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,yEAAyE;IACzE,SAAS,EAAE;QACT,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,2EAA2E;QAC3E,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,WAAW,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;QAC3B,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;QACpC,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;QAC1E,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,MAAM,EAAE;YACN,QAAQ,EAAE,MAAM,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,EAAE,MAAM,CAAC;YACvB,YAAY,EAAE,MAAM,CAAC;YACrB,eAAe,EAAE,MAAM,CAAC;YACxB,gBAAgB,EAAE,MAAM,CAAC;YACzB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;SAC3C,CAAC;KACH,CAAC;IACF,eAAe,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,CAAC,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,KAAK,MAAM,CAAC;QACxE,MAAM,EAAE;YACN,cAAc,EAAE,MAAM,CAAC;YACvB,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;SAC3C,CAAC;KACH,CAAC;IACF,SAAS,EAAE;QACT,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAioBD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAI3C,CAAC;AAEF;;sDAEsD;AACtD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElD"}
@@ -125,6 +125,9 @@ 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
131
  deleteCouplet: 'Delete couplet',
129
132
  },
130
133
  link: {
@@ -333,6 +336,9 @@ const ur = {
333
336
  menuLabel: 'شاعری',
334
337
  singleColumn: 'ایک کالم',
335
338
  twoColumn: 'دو کالم',
339
+ alternating: 'باری باری',
340
+ centerCouplet: 'یہ شعر وسط میں رکھیں',
341
+ uncenterCouplet: 'وسط میں رکھنا ختم کریں',
336
342
  deleteCouplet: 'شعر حذف کریں',
337
343
  },
338
344
  link: {
@@ -541,6 +547,9 @@ const paShahmukhi = {
541
547
  menuLabel: 'شاعری',
542
548
  singleColumn: 'اک کالم',
543
549
  twoColumn: 'دو کالم',
550
+ alternating: 'واری واری',
551
+ centerCouplet: 'ایہ شعر وچکار رکھو',
552
+ uncenterCouplet: 'وچکار رکھنا بند کرو',
544
553
  deleteCouplet: 'شعر مٹاؤ',
545
554
  },
546
555
  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;AAalD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAkB5C,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;AAclD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAmB5C,CAAC"}
package/dist/nodes.js CHANGED
@@ -9,6 +9,7 @@ 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';
12
13
  /**
13
14
  * Node types registered on the editor at construction time. Per
14
15
  * editor-architecture-design.md §2, node registration cannot be conditional
@@ -36,4 +37,5 @@ export const EDITOR_NODES = [
36
37
  FootnoteListNode,
37
38
  FootnoteItemNode,
38
39
  PoetryBlockNode,
40
+ PoetryCoupletNode,
39
41
  ];
@@ -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;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"}
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"}
@@ -3,6 +3,7 @@ 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';
6
7
  import { PoetryBlockNode } from '../blocks/PoetryNode.js';
7
8
  import { $deletePoetryOnBackspace, $ensureTrailingParagraph, $exitPoetryOnEnter, $insertPoetryCouplet } from '../blocks/poetryActions.js';
8
9
  export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLET_COMMAND');
@@ -21,8 +22,8 @@ export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLE
21
22
  export function PoetryPlugin() {
22
23
  const [editor] = useLexicalComposerContext();
23
24
  useEffect(() => {
24
- if (!editor.hasNodes([PoetryBlockNode, LayoutContainerNode, LayoutItemNode])) {
25
- throw new Error('PoetryPlugin: PoetryBlockNode (and the columns nodes it composes) not registered on the editor');
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');
26
27
  }
27
28
  return mergeRegister(editor.registerCommand(INSERT_POETRY_COUPLET_COMMAND, ({ layout, align }) => $insertPoetryCouplet(layout, align), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(PoetryBlockNode, (node) => {
28
29
  if (node.getChildrenSize() === 0) {
@@ -407,42 +407,75 @@
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. Re-added below on every *second*
411
- line (nth-child(2n)) — one couplet's worth of lines — so the extra space
412
- falls after every couplet, not after every single line. */
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. */
413
415
  .likhari-poetry .likhari-paragraph {
414
416
  margin: 0;
415
417
  }
416
418
 
417
- .likhari-poetry--single .likhari-paragraph:nth-child(2n):not(:last-child) {
419
+ .likhari-poetry > .likhari-poetry-couplet:not(:last-child),
420
+ .likhari-poetry > .likhari-layout-container:not(:last-child) {
418
421
  margin-bottom: 1em;
419
422
  }
420
423
 
421
- .likhari-poetry--two-column .likhari-layout-container {
422
- margin: 0;
423
- /* No grid gap — the divider below sits exactly on the track boundary, so
424
- spacing instead comes from matching padding on each side of it, which
425
- keeps the gutter visually even on both sides of the line. */
426
- gap: 0;
424
+ .likhari-poetry-couplet {
425
+ box-sizing: border-box;
427
426
  }
428
427
 
429
- /* One row per couplet in two-column layout — same "extra space after every
430
- couplet" rhythm as the single-column rule above. */
431
- .likhari-poetry--two-column .likhari-layout-container:not(:last-child) {
432
- margin-bottom: 1em;
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
+ }
451
+
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 {
464
+ margin: 0;
465
+ gap: 0;
433
466
  }
434
467
 
435
- .likhari-poetry--two-column .likhari-layout-item {
468
+ .likhari-poetry .likhari-layout-item {
436
469
  text-align: inherit;
437
470
  }
438
471
 
439
- .likhari-poetry--two-column .likhari-layout-item:first-child {
472
+ .likhari-poetry .likhari-layout-item:first-child {
440
473
  padding-inline-end: 20px;
441
474
  }
442
475
 
443
476
  /* A visible divider between misra columns, same dashed language as the
444
477
  couplet's own border — matches a table's internal cell borders. */
445
- .likhari-poetry--two-column .likhari-layout-item:not(:first-child) {
478
+ .likhari-poetry .likhari-layout-item:not(:first-child) {
446
479
  border-inline-start: 1px dashed var(--editor-line-strong);
447
480
  padding-inline-start: 20px;
448
481
  }
@@ -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,kBAgDzB,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,kBAiDzB,CAAC"}
@@ -46,6 +46,7 @@ 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',
49
50
  table: 'likhari-table',
50
51
  tableRow: 'likhari-table-row',
51
52
  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.152",
3
+ "version": "0.1.18-dev.154",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {