@inshapardaz/likhari-react 0.1.17 → 0.1.18-dev.124

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 +1 @@
1
- {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAKjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAYhF,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAmXrB,CAAC"}
1
+ {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAMjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAYhF,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGAoXrB,CAAC"}
@@ -23,6 +23,7 @@ import { LinkPastePlugin } from './plugins/LinkPastePlugin.js';
23
23
  import { PageBreakPlugin } from './plugins/PageBreakPlugin.js';
24
24
  import { LayoutPlugin } from './plugins/LayoutPlugin.js';
25
25
  import { FootnotePlugin } from './plugins/FootnotePlugin.js';
26
+ import { PoetryPlugin } from './plugins/PoetryPlugin.js';
26
27
  import { DraftRestore } from './components/DraftRestore.js';
27
28
  import { LeaveDialog } from './components/LeaveDialog.js';
28
29
  import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
@@ -279,7 +280,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
279
280
  const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
280
281
  return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
281
282
  ? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
282
- : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
283
+ : undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
283
284
  handleSave();
284
285
  settleLeave(true);
285
286
  }, onSaveDraft: () => {
@@ -0,0 +1,42 @@
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';
3
+ export type PoetryAlign = 'justify' | 'left' | 'right' | 'start';
4
+ export type SerializedPoetryBlockNode = Spread<{
5
+ layout: PoetryLayout;
6
+ align: PoetryAlign;
7
+ }, SerializedElementNode>;
8
+ /**
9
+ * A couplet (two-line verse unit), the one extensible primitive requirements
10
+ * doc §4.11 recommends in place of four fixed templates. `layout` picks
11
+ * single-column (two stacked misras) vs. two-column (built on the columns
12
+ * primitive, §4.9 — see blocks/poetryActions.ts for how children are
13
+ * restructured between the two); `align` is a literal per-instance override,
14
+ * independent of the document's own alignment setting (architecture doc
15
+ * §3.1). Both mutate in place via poetryActions so converting a couplet
16
+ * between layouts doesn't lose selection/undo coherence.
17
+ */
18
+ export declare class PoetryBlockNode extends ElementNode {
19
+ __layout: PoetryLayout;
20
+ __align: PoetryAlign;
21
+ constructor(layout?: PoetryLayout, align?: PoetryAlign, key?: NodeKey);
22
+ static getType(): string;
23
+ static clone(node: PoetryBlockNode): PoetryBlockNode;
24
+ getLayout(): PoetryLayout;
25
+ getAlign(): PoetryAlign;
26
+ /** Attribute-only — restructuring children for a layout change is
27
+ * poetryActions.ts's $setPoetryLayout, not a node method, since it needs
28
+ * $-context tree mutations beyond this node's own state. */
29
+ setLayoutAttribute(layout: PoetryLayout): this;
30
+ setAlign(align: PoetryAlign): this;
31
+ static importJSON(serializedNode: SerializedPoetryBlockNode): PoetryBlockNode;
32
+ exportJSON(): SerializedPoetryBlockNode;
33
+ static importDOM(): DOMConversionMap | null;
34
+ exportDOM(): DOMExportOutput;
35
+ createDOM(config: EditorConfig): HTMLElement;
36
+ updateDOM(prevNode: PoetryBlockNode, dom: HTMLElement, config: EditorConfig): boolean;
37
+ canBeEmpty(): false;
38
+ isShadowRoot(): boolean;
39
+ }
40
+ export declare function $createPoetryBlockNode(layout?: PoetryLayout, align?: PoetryAlign): PoetryBlockNode;
41
+ export declare function $isPoetryBlockNode(node: LexicalNode | null | undefined): node is PoetryBlockNode;
42
+ //# sourceMappingURL=PoetryNode.d.ts.map
@@ -0,0 +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;AAKjE,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,EAAE,qBAAqB,CAAC,CAAC;AAEpH;;;;;;;;;GASG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;gBAET,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,EAAE,GAAG,CAAC,EAAE,OAAO;IAM1F,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe;IAIpD,SAAS,IAAI,YAAY;IAIzB,QAAQ,IAAI,WAAW;IAIvB;;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;IAIvC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAa3C,SAAS,IAAI,eAAe;IAQ5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAY5C,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAYrF,UAAU,IAAI,KAAK;IAInB,YAAY,IAAI,OAAO;CAGxB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,GAAG,eAAe,CAEvH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,eAAe,CAEhG"}
@@ -0,0 +1,106 @@
1
+ import { addClassNamesToElement } from '@lexical/utils';
2
+ import { $applyNodeReplacement, ElementNode, } from 'lexical';
3
+ const VALID_LAYOUTS = new Set(['single', 'two-column']);
4
+ const VALID_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
5
+ /**
6
+ * A couplet (two-line verse unit), the one extensible primitive requirements
7
+ * doc §4.11 recommends in place of four fixed templates. `layout` picks
8
+ * single-column (two stacked misras) vs. two-column (built on the columns
9
+ * primitive, §4.9 — see blocks/poetryActions.ts for how children are
10
+ * restructured between the two); `align` is a literal per-instance override,
11
+ * independent of the document's own alignment setting (architecture doc
12
+ * §3.1). Both mutate in place via poetryActions so converting a couplet
13
+ * between layouts doesn't lose selection/undo coherence.
14
+ */
15
+ export class PoetryBlockNode extends ElementNode {
16
+ constructor(layout = 'single', align = 'justify', key) {
17
+ super(key);
18
+ this.__layout = layout;
19
+ this.__align = align;
20
+ }
21
+ static getType() {
22
+ return 'poetry-couplet';
23
+ }
24
+ static clone(node) {
25
+ return new PoetryBlockNode(node.__layout, node.__align, node.__key);
26
+ }
27
+ getLayout() {
28
+ return this.getLatest().__layout;
29
+ }
30
+ getAlign() {
31
+ return this.getLatest().__align;
32
+ }
33
+ /** Attribute-only — restructuring children for a layout change is
34
+ * poetryActions.ts's $setPoetryLayout, not a node method, since it needs
35
+ * $-context tree mutations beyond this node's own state. */
36
+ setLayoutAttribute(layout) {
37
+ const writable = this.getWritable();
38
+ writable.__layout = layout;
39
+ return writable;
40
+ }
41
+ setAlign(align) {
42
+ const writable = this.getWritable();
43
+ writable.__align = align;
44
+ return writable;
45
+ }
46
+ static importJSON(serializedNode) {
47
+ return $createPoetryBlockNode(serializedNode.layout, serializedNode.align);
48
+ }
49
+ exportJSON() {
50
+ return { ...super.exportJSON(), type: 'poetry-couplet', version: 1, layout: this.getLayout(), align: this.getAlign() };
51
+ }
52
+ static importDOM() {
53
+ return {
54
+ div: (domNode) => {
55
+ const rawLayout = domNode.getAttribute('data-likhari-poetry-layout');
56
+ if (!rawLayout)
57
+ return null;
58
+ const layout = VALID_LAYOUTS.has(rawLayout) ? rawLayout : 'single';
59
+ const rawAlign = domNode.getAttribute('data-likhari-poetry-align') ?? 'justify';
60
+ const align = VALID_ALIGNS.has(rawAlign) ? rawAlign : 'justify';
61
+ return { conversion: () => ({ node: $createPoetryBlockNode(layout, align) }), priority: 2 };
62
+ },
63
+ };
64
+ }
65
+ exportDOM() {
66
+ const element = document.createElement('div');
67
+ element.setAttribute('data-likhari-poetry-layout', this.getLayout());
68
+ element.setAttribute('data-likhari-poetry-align', this.getAlign());
69
+ element.style.textAlign = this.getAlign();
70
+ return { element };
71
+ }
72
+ createDOM(config) {
73
+ const element = document.createElement('div');
74
+ const base = config.theme.poetry ?? 'likhari-poetry';
75
+ addClassNamesToElement(element, base, `${base}--${this.__layout}`);
76
+ element.style.textAlign = this.__align;
77
+ return element;
78
+ }
79
+ // Only ever adds/removes the layout modifier class, never touches the
80
+ // element's full className — the reconciler separately classList.adds the
81
+ // per-block ltr/rtl theme class (editor.css) onto this same element, and
82
+ // a wholesale className reassignment here would silently wipe it out.
83
+ updateDOM(prevNode, dom, config) {
84
+ if (prevNode.__layout !== this.__layout) {
85
+ const base = config.theme.poetry ?? 'likhari-poetry';
86
+ dom.classList.remove(`${base}--${prevNode.__layout}`);
87
+ dom.classList.add(`${base}--${this.__layout}`);
88
+ }
89
+ if (prevNode.__align !== this.__align) {
90
+ dom.style.textAlign = this.__align;
91
+ }
92
+ return false;
93
+ }
94
+ canBeEmpty() {
95
+ return false;
96
+ }
97
+ isShadowRoot() {
98
+ return true;
99
+ }
100
+ }
101
+ export function $createPoetryBlockNode(layout = 'single', align = 'justify') {
102
+ return $applyNodeReplacement(new PoetryBlockNode(layout, align));
103
+ }
104
+ export function $isPoetryBlockNode(node) {
105
+ return node instanceof PoetryBlockNode;
106
+ }
@@ -0,0 +1,19 @@
1
+ import { type ParagraphNode } from 'lexical';
2
+ import { type PoetryAlign, type PoetryLayout, type PoetryBlockNode } from './PoetryNode.js';
3
+ /** The couplet's two misra paragraphs, in document order, regardless of
4
+ * which layout currently holds them. */
5
+ export declare function $getMisraParagraphs(node: PoetryBlockNode): ParagraphNode[];
6
+ /** The PoetryBlockNode the current selection is inside, or null. */
7
+ export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
8
+ /**
9
+ * Converts a couplet between single-column (two stacked misras) and
10
+ * two-column (built on the columns primitive, §4.9) in place — pulls the
11
+ * two misra paragraphs out of whichever shape currently holds them and
12
+ * rebuilds the other shape around the same paragraph nodes, so content,
13
+ * selection and undo history all survive the conversion.
14
+ */
15
+ export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
16
+ /** Inserts a new two-misra couplet after the selection's top-level block,
17
+ * in `layout`/`align`, and focuses its first misra. */
18
+ export declare function $insertPoetryCouplet(layout: PoetryLayout, align: PoetryAlign): boolean;
19
+ //# sourceMappingURL=poetryActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4E,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvH,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErI;wCACwC;AACxC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAW1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAclF;AAED;uDACuD;AACvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAmBtF"}
@@ -0,0 +1,76 @@
1
+ import { $findMatchingParent } from '@lexical/utils';
2
+ import { $createParagraphNode, $getSelection, $isParagraphNode, $isRangeSelection } from 'lexical';
3
+ import { $createLayoutContainerNode, $createLayoutItemNode, $isLayoutContainerNode, $isLayoutItemNode } from './LayoutNode.js';
4
+ import { $createPoetryBlockNode, $isPoetryBlockNode } from './PoetryNode.js';
5
+ /** The couplet's two misra paragraphs, in document order, regardless of
6
+ * which layout currently holds them. */
7
+ export function $getMisraParagraphs(node) {
8
+ if (node.getLayout() === 'single') {
9
+ return node.getChildren().filter($isParagraphNode);
10
+ }
11
+ const container = node.getChildren().find($isLayoutContainerNode);
12
+ if (!container)
13
+ return [];
14
+ return container
15
+ .getChildren()
16
+ .filter($isLayoutItemNode)
17
+ .map((item) => item.getFirstChild())
18
+ .filter($isParagraphNode);
19
+ }
20
+ /** The PoetryBlockNode the current selection is inside, or null. */
21
+ export function $getPoetryBlockFromSelection() {
22
+ const selection = $getSelection();
23
+ if (!$isRangeSelection(selection))
24
+ return null;
25
+ const match = $findMatchingParent(selection.anchor.getNode(), $isPoetryBlockNode);
26
+ return match ?? null;
27
+ }
28
+ /**
29
+ * Converts a couplet between single-column (two stacked misras) and
30
+ * two-column (built on the columns primitive, §4.9) in place — pulls the
31
+ * two misra paragraphs out of whichever shape currently holds them and
32
+ * rebuilds the other shape around the same paragraph nodes, so content,
33
+ * selection and undo history all survive the conversion.
34
+ */
35
+ export function $setPoetryLayout(node, layout) {
36
+ if (node.getLayout() === layout)
37
+ return;
38
+ const misras = $getMisraParagraphs(node);
39
+ for (const misra of misras)
40
+ misra.remove();
41
+ for (const leftover of node.getChildren())
42
+ leftover.remove();
43
+ if (layout === 'single') {
44
+ for (const misra of misras)
45
+ node.append(misra);
46
+ }
47
+ else {
48
+ const container = $createLayoutContainerNode('repeat(2, 1fr)');
49
+ for (const misra of misras)
50
+ container.append($createLayoutItemNode().append(misra));
51
+ node.append(container);
52
+ }
53
+ node.setLayoutAttribute(layout);
54
+ }
55
+ /** Inserts a new two-misra couplet after the selection's top-level block,
56
+ * in `layout`/`align`, and focuses its first misra. */
57
+ export function $insertPoetryCouplet(layout, align) {
58
+ const selection = $getSelection();
59
+ if (!$isRangeSelection(selection))
60
+ return false;
61
+ const anchorTopLevel = selection.anchor.getNode().getTopLevelElementOrThrow();
62
+ const node = $createPoetryBlockNode(layout, align);
63
+ const misraA = $createParagraphNode();
64
+ const misraB = $createParagraphNode();
65
+ if (layout === 'single') {
66
+ node.append(misraA, misraB);
67
+ }
68
+ else {
69
+ const container = $createLayoutContainerNode('repeat(2, 1fr)');
70
+ container.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
71
+ node.append(container);
72
+ }
73
+ anchorTopLevel.insertAfter(node);
74
+ misraA.selectStart();
75
+ return true;
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA8DA,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;AA2avD,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,+BAg+BxJ"}
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;AAwevD,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,+BAqgCxJ"}
@@ -14,6 +14,8 @@ import { $createImageNode } from '../image/ImageNode.js';
14
14
  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
+ import { $getPoetryBlockFromSelection, $setPoetryLayout } from '../blocks/poetryActions.js';
18
+ import { INSERT_POETRY_COUPLET_COMMAND } from '../plugins/PoetryPlugin.js';
17
19
  import { TableDialog } from './TableDialog.js';
18
20
  import { LayoutDialog } from './LayoutDialog.js';
19
21
  import { $getTableCellNodeFromLexicalNode, $isTableSelection, INSERT_TABLE_COMMAND } from '@lexical/table';
@@ -26,7 +28,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
26
28
  import { setStyleProperty } from '../utils/style.js';
27
29
  import { useStrings } from '../i18n/useStrings.js';
28
30
  import { usePortalTarget } from '../PortalTargetContext.js';
29
- 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, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
31
+ 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, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
30
32
  /** Sentinel for "no inline style": Lexical returns the default it is given when
31
33
  * the selection has none, so this tells "unstyled" (show the canvas default)
32
34
  * apart from "styled with a value the dropdown doesn't list" (show nothing). */
@@ -45,6 +47,9 @@ const INITIAL_STATE = {
45
47
  tableColumns: 1,
46
48
  canMergeCells: false,
47
49
  canUnmergeCell: false,
50
+ inPoetry: false,
51
+ poetryLayout: 'single',
52
+ poetryAlign: 'justify',
48
53
  canUndo: false,
49
54
  canRedo: false,
50
55
  };
@@ -156,6 +161,26 @@ function TableMenuItems({ strings, direction, rows, columns, canMergeCells, canU
156
161
  };
157
162
  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')] }));
158
163
  }
164
+ /** The poetry couplet menu's contents — layout (single/two-column) and
165
+ * literal alignment, both per-instance overrides (requirements doc §4.11:
166
+ * "set per CoupletNode, not inherited from a document-wide setting"). */
167
+ function PoetryMenuItems({ strings, alignIcons, layout, align, onAction, }) {
168
+ const t = strings.poetryMenu;
169
+ const a = strings.toolbar.alignOptions;
170
+ const item = (icon, label, action, active) => {
171
+ const Icon = icon;
172
+ return (_jsx(Menu.Item, { disabled: active, leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onAction(action), children: label }));
173
+ };
174
+ const setLayout = (next) => () => {
175
+ const node = $getPoetryBlockFromSelection();
176
+ if (node)
177
+ $setPoetryLayout(node, next);
178
+ };
179
+ const setAlign = (next) => () => {
180
+ $getPoetryBlockFromSelection()?.setAlign(next);
181
+ };
182
+ 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')] }));
183
+ }
159
184
  export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
160
185
  const [editor] = useLexicalComposerContext();
161
186
  const [state, setState] = useState(INITIAL_STATE);
@@ -180,7 +205,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
180
205
  if ($isTableSelection(selection)) {
181
206
  const { rows, columns } = $getTableSelectionSize();
182
207
  const canMergeCells = $canMergeSelectedCells();
183
- setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns, canMergeCells, canUnmergeCell: false }));
208
+ setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns, canMergeCells, canUnmergeCell: false, inPoetry: false }));
184
209
  return;
185
210
  }
186
211
  if (!$isRangeSelection(selection))
@@ -189,6 +214,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
189
214
  const inTable = $getTableCellNodeFromLexicalNode(anchorNode) !== null;
190
215
  const tableSize = inTable ? $getTableSelectionSize() : { rows: 1, columns: 1 };
191
216
  const canUnmergeCell = inTable && $canUnmergeSelectedCell();
217
+ const poetryBlock = $getPoetryBlockFromSelection();
192
218
  const element = anchorNode.getKey() === 'root' ? anchorNode : (anchorNode.getTopLevelElement() ?? anchorNode);
193
219
  const listParent = $findMatchingParent(anchorNode, $isListNode);
194
220
  const listType = listParent && $isListNode(listParent) ? listParent.getListType() : null;
@@ -229,6 +255,9 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
229
255
  tableColumns: tableSize.columns,
230
256
  canMergeCells: false,
231
257
  canUnmergeCell,
258
+ inPoetry: poetryBlock !== null,
259
+ poetryLayout: poetryBlock?.getLayout() ?? s.poetryLayout,
260
+ poetryAlign: poetryBlock?.getAlign() ?? s.poetryAlign,
232
261
  }));
233
262
  });
234
263
  }, [editor]);
@@ -457,9 +486,9 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
457
486
  root?.addEventListener('contextmenu', onContextMenu);
458
487
  });
459
488
  }, [editor, config.links]);
460
- // Table actions. The menu takes focus, so the selection is snapshotted when it
461
- // opens and restored before an action runs.
462
- const runTableAction = (action) => () => {
489
+ // Table/poetry contextual-menu actions. The menu takes focus, so the
490
+ // selection is snapshotted when it opens and restored before an action runs.
491
+ const runMenuAction = (action) => () => {
463
492
  restoreSelection();
464
493
  editor.update(action);
465
494
  editor.focus();
@@ -740,8 +769,9 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
740
769
  restoreSelection();
741
770
  openLinkDialog();
742
771
  }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
743
- 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 && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
744
- 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: runTableAction }) })] }) }, "tableActions")),
772
+ 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")),
773
+ 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")),
774
+ 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")),
745
775
  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")),
746
776
  ];
747
777
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
@@ -757,7 +787,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
757
787
  }, onRemove: removeLink, strings: strings }) })] })), config.tables && (_jsxs(Menu, { opened: tableContext !== null, onChange: (opened) => {
758
788
  if (!opened)
759
789
  setTableContext(null);
760
- }, 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: runTableAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
790
+ }, 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 }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
761
791
  position: 'absolute',
762
792
  visibility: 'hidden',
763
793
  pointerEvents: 'none',
@@ -42,14 +42,16 @@ export interface Strings {
42
42
  linkOptions: string;
43
43
  /** Toolbar button (shown while the caret is in a table) that opens the table actions menu. */
44
44
  tableOptions: string;
45
+ /** Toolbar button (shown while the caret is in a couplet) that opens the poetry layout/alignment menu. */
46
+ poetryOptions: string;
45
47
  insertLink: string;
46
48
  insertImage: string;
47
49
  insertPageBreak: string;
48
50
  insertTable: string;
49
51
  insertColumns: string;
50
52
  insertFootnote: string;
53
+ insertPoetryCouplet: string;
51
54
  insertHorizontalRule: string;
52
- poetryBlocks: string;
53
55
  comingSoon: (label: string) => string;
54
56
  autocorrect: string;
55
57
  textCleanup: string;
@@ -136,6 +138,11 @@ export interface Strings {
136
138
  mergeCells: string;
137
139
  unmergeCell: string;
138
140
  };
141
+ poetryMenu: {
142
+ menuLabel: string;
143
+ singleColumn: string;
144
+ twoColumn: string;
145
+ };
139
146
  link: {
140
147
  menuLabel: string;
141
148
  noUrl: string;
@@ -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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"}
@@ -33,14 +33,15 @@ const en = {
33
33
  alignment: 'Alignment',
34
34
  linkOptions: 'Link options',
35
35
  tableOptions: 'Table options',
36
+ poetryOptions: 'Poetry options',
36
37
  insertLink: 'Insert link (Ctrl+K)',
37
38
  insertImage: 'Insert image',
38
39
  insertPageBreak: 'Insert page break',
39
40
  insertTable: 'Insert table',
40
41
  insertColumns: 'Insert columns',
41
42
  insertFootnote: 'Insert footnote',
43
+ insertPoetryCouplet: 'Insert poetry couplet',
42
44
  insertHorizontalRule: 'Insert horizontal rule',
43
- poetryBlocks: 'Poetry blocks',
44
45
  comingSoon: (label) => `${label} (coming soon)`,
45
46
  autocorrect: 'Auto-correct',
46
47
  textCleanup: 'Text cleanup',
@@ -120,6 +121,11 @@ const en = {
120
121
  mergeCells: 'Merge cells',
121
122
  unmergeCell: 'Unmerge cell',
122
123
  },
124
+ poetryMenu: {
125
+ menuLabel: 'Poetry',
126
+ singleColumn: 'Single column',
127
+ twoColumn: 'Two column',
128
+ },
123
129
  link: {
124
130
  menuLabel: 'Link',
125
131
  noUrl: '(no URL)',
@@ -234,14 +240,15 @@ const ur = {
234
240
  alignment: 'سیدھ',
235
241
  linkOptions: 'لنک کے اختیارات',
236
242
  tableOptions: 'جدول کے اختیارات',
243
+ poetryOptions: 'شاعری کے اختیارات',
237
244
  insertLink: 'لنک شامل کریں (Ctrl+K)',
238
245
  insertImage: 'تصویر شامل کریں',
239
246
  insertPageBreak: 'صفحے کی تقسیم شامل کریں',
240
247
  insertTable: 'جدول شامل کریں',
241
248
  insertColumns: 'کالم شامل کریں',
242
249
  insertFootnote: 'فٹ نوٹ شامل کریں',
250
+ insertPoetryCouplet: 'شعر شامل کریں',
243
251
  insertHorizontalRule: 'افقی لکیر شامل کریں',
244
- poetryBlocks: 'شاعری کے بلاکس',
245
252
  comingSoon: (label) => `${label} (جلد آ رہا ہے)`,
246
253
  autocorrect: 'خودکار تصحیح',
247
254
  textCleanup: 'متن کی صفائی',
@@ -321,6 +328,11 @@ const ur = {
321
328
  mergeCells: 'خانے ضم کریں',
322
329
  unmergeCell: 'خانہ الگ کریں',
323
330
  },
331
+ poetryMenu: {
332
+ menuLabel: 'شاعری',
333
+ singleColumn: 'ایک کالم',
334
+ twoColumn: 'دو کالم',
335
+ },
324
336
  link: {
325
337
  menuLabel: 'لنک',
326
338
  noUrl: '(کوئی یو آر ایل نہیں)',
@@ -435,14 +447,15 @@ const paShahmukhi = {
435
447
  alignment: 'سیدھ',
436
448
  linkOptions: 'لنک دے اختیار',
437
449
  tableOptions: 'ٹیبل دے اختیار',
450
+ poetryOptions: 'شاعری دے اختیار',
438
451
  insertLink: 'لنک پاؤ (Ctrl+K)',
439
452
  insertImage: 'تصویر پاؤ',
440
453
  insertPageBreak: 'صفحے دی ونڈ پاؤ',
441
454
  insertTable: 'ٹیبل پاؤ',
442
455
  insertColumns: 'کالم پاؤ',
443
456
  insertFootnote: 'فٹ نوٹ پاؤ',
457
+ insertPoetryCouplet: 'شعر پاؤ',
444
458
  insertHorizontalRule: 'لیٹی لکیر پاؤ',
445
- poetryBlocks: 'شاعری بلاک',
446
459
  comingSoon: (label) => `${label} (چھیتی آ رہا اے)`,
447
460
  autocorrect: 'خودکار درستی',
448
461
  textCleanup: 'متن دی صفائی',
@@ -522,6 +535,11 @@ const paShahmukhi = {
522
535
  mergeCells: 'خانے رلاؤ',
523
536
  unmergeCell: 'خانہ الگ کرو',
524
537
  },
538
+ poetryMenu: {
539
+ menuLabel: 'شاعری',
540
+ singleColumn: 'اک کالم',
541
+ twoColumn: 'دو کالم',
542
+ },
525
543
  link: {
526
544
  menuLabel: 'لنک',
527
545
  noUrl: '(کوئی یو آر ایل نئیں)',
@@ -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;AAYlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAiB5C,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
@@ -8,6 +8,7 @@ import { PageBreakNode } from './blocks/PageBreakNode.js';
8
8
  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
+ import { PoetryBlockNode } from './blocks/PoetryNode.js';
11
12
  /**
12
13
  * Node types registered on the editor at construction time. Per
13
14
  * editor-architecture-design.md §2, node registration cannot be conditional
@@ -34,4 +35,5 @@ export const EDITOR_NODES = [
34
35
  FootnoteReferenceNode,
35
36
  FootnoteListNode,
36
37
  FootnoteItemNode,
38
+ PoetryBlockNode,
37
39
  ];
@@ -0,0 +1,14 @@
1
+ import { type LexicalCommand } from 'lexical';
2
+ import { type PoetryAlign, type PoetryLayout } from '../blocks/PoetryNode.js';
3
+ export declare const INSERT_POETRY_COUPLET_COMMAND: LexicalCommand<{
4
+ layout: PoetryLayout;
5
+ align: PoetryAlign;
6
+ }>;
7
+ /**
8
+ * Registers INSERT_POETRY_COUPLET_COMMAND (mirrors LayoutPlugin/FootnotePlugin's
9
+ * shape) and a node transform that removes a couplet left with no misra
10
+ * content — e.g. after undo/paste leaves it structurally empty — rather
11
+ * than letting an unselectable husk linger in the document.
12
+ */
13
+ export declare function PoetryPlugin(): null;
14
+ //# sourceMappingURL=PoetryPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAGtF,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;;;;;GAKG;AACH,wBAAgB,YAAY,SAqB3B"}
@@ -0,0 +1,27 @@
1
+ import { useEffect } from 'react';
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
4
+ import { mergeRegister } from '@lexical/utils';
5
+ import { LayoutContainerNode, LayoutItemNode } from '../blocks/LayoutNode.js';
6
+ import { PoetryBlockNode } from '../blocks/PoetryNode.js';
7
+ import { $insertPoetryCouplet } from '../blocks/poetryActions.js';
8
+ export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLET_COMMAND');
9
+ /**
10
+ * Registers INSERT_POETRY_COUPLET_COMMAND (mirrors LayoutPlugin/FootnotePlugin's
11
+ * shape) and a node transform that removes a couplet left with no misra
12
+ * content — e.g. after undo/paste leaves it structurally empty — rather
13
+ * than letting an unselectable husk linger in the document.
14
+ */
15
+ export function PoetryPlugin() {
16
+ const [editor] = useLexicalComposerContext();
17
+ useEffect(() => {
18
+ if (!editor.hasNodes([PoetryBlockNode, LayoutContainerNode, LayoutItemNode])) {
19
+ throw new Error('PoetryPlugin: PoetryBlockNode (and the columns nodes it composes) not registered on the editor');
20
+ }
21
+ return mergeRegister(editor.registerCommand(INSERT_POETRY_COUPLET_COMMAND, ({ layout, align }) => $insertPoetryCouplet(layout, align), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(PoetryBlockNode, (node) => {
22
+ if (node.getChildrenSize() === 0)
23
+ node.remove();
24
+ }));
25
+ }, [editor]);
26
+ return null;
27
+ }
@@ -374,6 +374,34 @@
374
374
  content: '[' counter(likhari-footnote-item) '] ';
375
375
  }
376
376
 
377
+ /* Poetry couplets (lexical-editor-spec.md §4.11, editor-ui-design-spec.md §4).
378
+ Own type scale and spacing, distinct from body text — the .likhari-ltr/
379
+ .likhari-rtl per-block classes (above) already switch font-family for this
380
+ element same as any other block; these rules layer poetry's own size/
381
+ line-height/gap on top, combined with those same classes. */
382
+ .likhari-poetry {
383
+ margin: 0 0 1.4em 0;
384
+ }
385
+
386
+ .likhari-poetry.likhari-ltr {
387
+ font-size: 15px;
388
+ line-height: 1.6;
389
+ }
390
+
391
+ .likhari-poetry.likhari-rtl {
392
+ font-size: 20px;
393
+ line-height: 2.1;
394
+ }
395
+
396
+ .likhari-poetry--two-column .likhari-layout-container {
397
+ margin: 0;
398
+ gap: 16px;
399
+ }
400
+
401
+ .likhari-poetry--two-column .likhari-layout-item {
402
+ text-align: inherit;
403
+ }
404
+
377
405
  .likhari-table {
378
406
  border-collapse: collapse;
379
407
  margin: 1em 0;
@@ -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,kBA+CzB,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"}
@@ -45,6 +45,7 @@ export const editorTheme = {
45
45
  footnoteReference: 'likhari-footnote-ref',
46
46
  footnoteList: 'likhari-footnote-list',
47
47
  footnoteItem: 'likhari-footnote-item',
48
+ poetry: 'likhari-poetry',
48
49
  table: 'likhari-table',
49
50
  tableRow: 'likhari-table-row',
50
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.17",
3
+ "version": "0.1.18-dev.124",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {