@inshapardaz/likhari-react 0.1.18 → 0.1.19-dev.182
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.
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +6 -2
- package/dist/blocks/PoetryNode.d.ts +70 -0
- package/dist/blocks/PoetryNode.d.ts.map +1 -0
- package/dist/blocks/PoetryNode.js +198 -0
- package/dist/blocks/PoetryResizer.d.ts +11 -0
- package/dist/blocks/PoetryResizer.d.ts.map +1 -0
- package/dist/blocks/PoetryResizer.js +94 -0
- package/dist/blocks/poetryActions.d.ts +117 -0
- package/dist/blocks/poetryActions.d.ts.map +1 -0
- package/dist/blocks/poetryActions.js +481 -0
- package/dist/components/FindReplaceBar.d.ts +13 -0
- package/dist/components/FindReplaceBar.d.ts.map +1 -0
- package/dist/components/FindReplaceBar.js +72 -0
- package/dist/components/Toolbar.d.ts +4 -1
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +81 -9
- package/dist/find/findReplaceActions.d.ts +23 -0
- package/dist/find/findReplaceActions.d.ts.map +1 -0
- package/dist/find/findReplaceActions.js +81 -0
- package/dist/i18n/strings.d.ts +34 -1
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +99 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +2 -0
- package/dist/plugins/PoetryPlugin.d.ts +19 -0
- package/dist/plugins/PoetryPlugin.d.ts.map +1 -0
- package/dist/plugins/PoetryPlugin.js +54 -0
- package/dist/spellcheck/dictionaries/en/LICENSE.txt +347 -0
- package/dist/spellcheck/dictionaries/en/index.aff +205 -0
- package/dist/spellcheck/dictionaries/en/index.dic +49569 -0
- package/dist/spellcheck/spellDictionaries.d.ts +40 -0
- package/dist/spellcheck/spellDictionaries.d.ts.map +1 -0
- package/dist/spellcheck/spellDictionaries.js +62 -0
- package/dist/theme/editor.css +278 -0
- package/dist/theme/editorTheme.d.ts.map +1 -1
- package/dist/theme/editorTheme.js +1 -0
- package/package.json +8 -6
package/dist/EditorRoot.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAOjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAahF,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,uGA2XrB,CAAC"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -23,7 +23,10 @@ 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';
|
|
27
|
+
import { PoetryResizer } from './blocks/PoetryResizer.js';
|
|
26
28
|
import { DraftRestore } from './components/DraftRestore.js';
|
|
29
|
+
import { FindReplaceBar } from './components/FindReplaceBar.js';
|
|
27
30
|
import { LeaveDialog } from './components/LeaveDialog.js';
|
|
28
31
|
import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
|
|
29
32
|
import { ImageOptionsContext } from './image/ImageOptionsContext.js';
|
|
@@ -65,6 +68,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
65
68
|
const editorStateRef = useRef(null);
|
|
66
69
|
const lastSavedJsonRef = useRef(null);
|
|
67
70
|
const [isDirty, setIsDirty] = useState(false);
|
|
71
|
+
const [findOpen, setFindOpen] = useState(false);
|
|
68
72
|
const isDirtyRef = useRef(isDirty);
|
|
69
73
|
isDirtyRef.current = isDirty;
|
|
70
74
|
const autosaveTimerRef = useRef(null);
|
|
@@ -277,9 +281,9 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
277
281
|
// rather than colliding on a shared one.
|
|
278
282
|
const portalTargetId = useId();
|
|
279
283
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
280
|
-
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
|
|
284
|
+
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, findOpen: findOpen, onToggleFind: () => setFindOpen((v) => !v), isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
|
|
281
285
|
? { 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: () => {
|
|
286
|
+
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsxs("div", { className: "likhari-canvas-frame", children: [config.findReplace && findOpen && _jsx(FindReplaceBar, { strings: strings, dir: dir, onClose: () => setFindOpen(false) }), _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.poetry.enabled && _jsx(PoetryResizer, {}), 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
287
|
handleSave();
|
|
284
288
|
settleLeave(true);
|
|
285
289
|
}, onSaveDraft: () => {
|
|
@@ -0,0 +1,70 @@
|
|
|
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' | 'staggered';
|
|
3
|
+
/** Vertical space between couplets — an ordered scale the menu steps through. */
|
|
4
|
+
export declare const POETRY_SPACINGS: readonly ["compact", "normal", "relaxed", "loose"];
|
|
5
|
+
export type PoetrySpacing = (typeof POETRY_SPACINGS)[number];
|
|
6
|
+
export declare const DEFAULT_POETRY_SPACING: PoetrySpacing;
|
|
7
|
+
/** Horizontal space either side of the divider in two-column layout. */
|
|
8
|
+
export declare const POETRY_GUTTERS: readonly ["compact", "normal", "relaxed", "loose"];
|
|
9
|
+
export type PoetryGutter = (typeof POETRY_GUTTERS)[number];
|
|
10
|
+
export declare const DEFAULT_POETRY_GUTTER: PoetryGutter;
|
|
11
|
+
/** Width of each couplet box in staggered layout, as a share of the block. */
|
|
12
|
+
export declare const POETRY_STAGGERS: readonly ["compact", "normal", "relaxed", "loose"];
|
|
13
|
+
export type PoetryStagger = (typeof POETRY_STAGGERS)[number];
|
|
14
|
+
export declare const DEFAULT_POETRY_STAGGER: PoetryStagger;
|
|
15
|
+
export type SerializedPoetryBlockNode = Spread<{
|
|
16
|
+
layout: PoetryLayout;
|
|
17
|
+
width?: number;
|
|
18
|
+
spacing?: PoetrySpacing;
|
|
19
|
+
gutter?: PoetryGutter;
|
|
20
|
+
stagger?: PoetryStagger;
|
|
21
|
+
}, SerializedElementNode>;
|
|
22
|
+
/**
|
|
23
|
+
* A poetry *section*: one or more couplets (two-line verse units) sharing
|
|
24
|
+
* one layout — single-column stacks each couplet's two misras directly as
|
|
25
|
+
* paragraph children (two per couplet, in order), two-column gives each
|
|
26
|
+
* couplet its own LayoutContainerNode row (built on the columns primitive,
|
|
27
|
+
* §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
|
|
28
|
+
* is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
|
|
29
|
+
* restructured/grown between couplets and layouts. `layout` applies to the
|
|
30
|
+
* whole section, not per couplet, and mutates in place via poetryActions so
|
|
31
|
+
* converting a section between layouts doesn't lose selection/undo
|
|
32
|
+
* coherence. One extensible primitive in place of four fixed templates,
|
|
33
|
+
* per requirements doc §4.11.
|
|
34
|
+
*/
|
|
35
|
+
export declare class PoetryBlockNode extends ElementNode {
|
|
36
|
+
__layout: PoetryLayout;
|
|
37
|
+
/** User-chosen width in px (drag-resized), overriding editor.css's default
|
|
38
|
+
* max-width: 70%. Undefined until the user resizes it. */
|
|
39
|
+
__width?: number;
|
|
40
|
+
__spacing: PoetrySpacing;
|
|
41
|
+
__gutter: PoetryGutter;
|
|
42
|
+
__stagger: PoetryStagger;
|
|
43
|
+
constructor(layout?: PoetryLayout, width?: number, spacing?: PoetrySpacing, gutter?: PoetryGutter, stagger?: PoetryStagger, key?: NodeKey);
|
|
44
|
+
static getType(): string;
|
|
45
|
+
static clone(node: PoetryBlockNode): PoetryBlockNode;
|
|
46
|
+
getLayout(): PoetryLayout;
|
|
47
|
+
getWidth(): number | undefined;
|
|
48
|
+
setWidth(width: number | undefined): this;
|
|
49
|
+
/** Attribute-only — restructuring children for a layout change is
|
|
50
|
+
* poetryActions.ts's $setPoetryLayout, not a node method, since it needs
|
|
51
|
+
* $-context tree mutations beyond this node's own state. */
|
|
52
|
+
setLayoutAttribute(layout: PoetryLayout): this;
|
|
53
|
+
getSpacing(): PoetrySpacing;
|
|
54
|
+
setSpacing(spacing: PoetrySpacing): this;
|
|
55
|
+
getGutter(): PoetryGutter;
|
|
56
|
+
setGutter(gutter: PoetryGutter): this;
|
|
57
|
+
getStagger(): PoetryStagger;
|
|
58
|
+
setStagger(stagger: PoetryStagger): this;
|
|
59
|
+
static importJSON(serializedNode: SerializedPoetryBlockNode): PoetryBlockNode;
|
|
60
|
+
exportJSON(): SerializedPoetryBlockNode;
|
|
61
|
+
static importDOM(): DOMConversionMap | null;
|
|
62
|
+
exportDOM(): DOMExportOutput;
|
|
63
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
64
|
+
updateDOM(prevNode: PoetryBlockNode, dom: HTMLElement, config: EditorConfig): boolean;
|
|
65
|
+
canBeEmpty(): false;
|
|
66
|
+
isShadowRoot(): boolean;
|
|
67
|
+
}
|
|
68
|
+
export declare function $createPoetryBlockNode(layout?: PoetryLayout, width?: number, spacing?: PoetrySpacing, gutter?: PoetryGutter, stagger?: PoetryStagger): PoetryBlockNode;
|
|
69
|
+
export declare function $isPoetryBlockNode(node: LexicalNode | null | undefined): node is PoetryBlockNode;
|
|
70
|
+
//# sourceMappingURL=PoetryNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoetryNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryNode.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { addClassNamesToElement } from '@lexical/utils';
|
|
2
|
+
import { $applyNodeReplacement, ElementNode, } from 'lexical';
|
|
3
|
+
/** Vertical space between couplets — an ordered scale the menu steps through. */
|
|
4
|
+
export const POETRY_SPACINGS = ['compact', 'normal', 'relaxed', 'loose'];
|
|
5
|
+
export const DEFAULT_POETRY_SPACING = 'normal';
|
|
6
|
+
/** Horizontal space either side of the divider in two-column layout. */
|
|
7
|
+
export const POETRY_GUTTERS = ['compact', 'normal', 'relaxed', 'loose'];
|
|
8
|
+
export const DEFAULT_POETRY_GUTTER = 'normal';
|
|
9
|
+
/** Width of each couplet box in staggered layout, as a share of the block. */
|
|
10
|
+
export const POETRY_STAGGERS = ['compact', 'normal', 'relaxed', 'loose'];
|
|
11
|
+
export const DEFAULT_POETRY_STAGGER = 'normal';
|
|
12
|
+
const VALID_LAYOUTS = new Set(['single', 'two-column', 'staggered']);
|
|
13
|
+
const VALID_SPACINGS = new Set(POETRY_SPACINGS);
|
|
14
|
+
const VALID_GUTTERS = new Set(POETRY_GUTTERS);
|
|
15
|
+
const VALID_STAGGERS = new Set(POETRY_STAGGERS);
|
|
16
|
+
/** Poetry is always justified: `text-align-last: justify` stretches a
|
|
17
|
+
* one-line misra across the full column width too, not just wrapped lines,
|
|
18
|
+
* so every line reads edge-to-edge. */
|
|
19
|
+
function applyJustifyStyle(element) {
|
|
20
|
+
element.style.textAlign = 'justify';
|
|
21
|
+
element.style.textAlignLast = 'justify';
|
|
22
|
+
}
|
|
23
|
+
/** A user-dragged width overrides editor.css's default max-width: 70% —
|
|
24
|
+
* clearing the inline style (rather than setting it to that same 70%)
|
|
25
|
+
* lets the CSS default keep tracking the canvas if it's ever resized. */
|
|
26
|
+
function applyWidthStyle(element, width) {
|
|
27
|
+
if (width === undefined)
|
|
28
|
+
element.style.removeProperty('max-width');
|
|
29
|
+
else
|
|
30
|
+
element.style.maxWidth = `${width}px`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A poetry *section*: one or more couplets (two-line verse units) sharing
|
|
34
|
+
* one layout — single-column stacks each couplet's two misras directly as
|
|
35
|
+
* paragraph children (two per couplet, in order), two-column gives each
|
|
36
|
+
* couplet its own LayoutContainerNode row (built on the columns primitive,
|
|
37
|
+
* §4.9) — see blocks/poetryActions.ts's $getCouplets for how either shape
|
|
38
|
+
* is read, and $setPoetryLayout/$exitPoetryOnEnter for how children are
|
|
39
|
+
* restructured/grown between couplets and layouts. `layout` applies to the
|
|
40
|
+
* whole section, not per couplet, and mutates in place via poetryActions so
|
|
41
|
+
* converting a section between layouts doesn't lose selection/undo
|
|
42
|
+
* coherence. One extensible primitive in place of four fixed templates,
|
|
43
|
+
* per requirements doc §4.11.
|
|
44
|
+
*/
|
|
45
|
+
export class PoetryBlockNode extends ElementNode {
|
|
46
|
+
constructor(layout = 'single', width, spacing = DEFAULT_POETRY_SPACING, gutter = DEFAULT_POETRY_GUTTER, stagger = DEFAULT_POETRY_STAGGER, key) {
|
|
47
|
+
super(key);
|
|
48
|
+
this.__layout = layout;
|
|
49
|
+
this.__width = width;
|
|
50
|
+
this.__spacing = spacing;
|
|
51
|
+
this.__gutter = gutter;
|
|
52
|
+
this.__stagger = stagger;
|
|
53
|
+
}
|
|
54
|
+
static getType() {
|
|
55
|
+
return 'poetry-couplet';
|
|
56
|
+
}
|
|
57
|
+
static clone(node) {
|
|
58
|
+
return new PoetryBlockNode(node.__layout, node.__width, node.__spacing, node.__gutter, node.__stagger, node.__key);
|
|
59
|
+
}
|
|
60
|
+
getLayout() {
|
|
61
|
+
return this.getLatest().__layout;
|
|
62
|
+
}
|
|
63
|
+
getWidth() {
|
|
64
|
+
return this.getLatest().__width;
|
|
65
|
+
}
|
|
66
|
+
setWidth(width) {
|
|
67
|
+
const writable = this.getWritable();
|
|
68
|
+
writable.__width = width;
|
|
69
|
+
return writable;
|
|
70
|
+
}
|
|
71
|
+
/** Attribute-only — restructuring children for a layout change is
|
|
72
|
+
* poetryActions.ts's $setPoetryLayout, not a node method, since it needs
|
|
73
|
+
* $-context tree mutations beyond this node's own state. */
|
|
74
|
+
setLayoutAttribute(layout) {
|
|
75
|
+
const writable = this.getWritable();
|
|
76
|
+
writable.__layout = layout;
|
|
77
|
+
return writable;
|
|
78
|
+
}
|
|
79
|
+
getSpacing() {
|
|
80
|
+
return this.getLatest().__spacing;
|
|
81
|
+
}
|
|
82
|
+
setSpacing(spacing) {
|
|
83
|
+
const writable = this.getWritable();
|
|
84
|
+
writable.__spacing = spacing;
|
|
85
|
+
return writable;
|
|
86
|
+
}
|
|
87
|
+
getGutter() {
|
|
88
|
+
return this.getLatest().__gutter;
|
|
89
|
+
}
|
|
90
|
+
setGutter(gutter) {
|
|
91
|
+
const writable = this.getWritable();
|
|
92
|
+
writable.__gutter = gutter;
|
|
93
|
+
return writable;
|
|
94
|
+
}
|
|
95
|
+
getStagger() {
|
|
96
|
+
return this.getLatest().__stagger;
|
|
97
|
+
}
|
|
98
|
+
setStagger(stagger) {
|
|
99
|
+
const writable = this.getWritable();
|
|
100
|
+
writable.__stagger = stagger;
|
|
101
|
+
return writable;
|
|
102
|
+
}
|
|
103
|
+
static importJSON(serializedNode) {
|
|
104
|
+
return $createPoetryBlockNode(serializedNode.layout, serializedNode.width, serializedNode.spacing, serializedNode.gutter, serializedNode.stagger);
|
|
105
|
+
}
|
|
106
|
+
exportJSON() {
|
|
107
|
+
const width = this.getWidth();
|
|
108
|
+
return {
|
|
109
|
+
...super.exportJSON(),
|
|
110
|
+
type: 'poetry-couplet',
|
|
111
|
+
version: 1,
|
|
112
|
+
layout: this.getLayout(),
|
|
113
|
+
spacing: this.getSpacing(),
|
|
114
|
+
gutter: this.getGutter(),
|
|
115
|
+
stagger: this.getStagger(),
|
|
116
|
+
...(width !== undefined ? { width } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
static importDOM() {
|
|
120
|
+
return {
|
|
121
|
+
div: (domNode) => {
|
|
122
|
+
const rawLayout = domNode.getAttribute('data-likhari-poetry-layout');
|
|
123
|
+
if (!rawLayout)
|
|
124
|
+
return null;
|
|
125
|
+
const layout = VALID_LAYOUTS.has(rawLayout) ? rawLayout : 'single';
|
|
126
|
+
const rawWidth = Number(domNode.getAttribute('data-likhari-poetry-width'));
|
|
127
|
+
const width = Number.isFinite(rawWidth) && rawWidth > 0 ? rawWidth : undefined;
|
|
128
|
+
const rawSpacing = domNode.getAttribute('data-likhari-poetry-spacing');
|
|
129
|
+
const spacing = VALID_SPACINGS.has(rawSpacing) ? rawSpacing : DEFAULT_POETRY_SPACING;
|
|
130
|
+
const rawGutter = domNode.getAttribute('data-likhari-poetry-gutter');
|
|
131
|
+
const gutter = VALID_GUTTERS.has(rawGutter) ? rawGutter : DEFAULT_POETRY_GUTTER;
|
|
132
|
+
const rawStagger = domNode.getAttribute('data-likhari-poetry-stagger');
|
|
133
|
+
const stagger = VALID_STAGGERS.has(rawStagger) ? rawStagger : DEFAULT_POETRY_STAGGER;
|
|
134
|
+
return { conversion: () => ({ node: $createPoetryBlockNode(layout, width, spacing, gutter, stagger) }), priority: 2 };
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
exportDOM() {
|
|
139
|
+
const element = document.createElement('div');
|
|
140
|
+
element.setAttribute('data-likhari-poetry-layout', this.getLayout());
|
|
141
|
+
element.setAttribute('data-likhari-poetry-spacing', this.getSpacing());
|
|
142
|
+
element.setAttribute('data-likhari-poetry-gutter', this.getGutter());
|
|
143
|
+
element.setAttribute('data-likhari-poetry-stagger', this.getStagger());
|
|
144
|
+
const width = this.getWidth();
|
|
145
|
+
if (width !== undefined)
|
|
146
|
+
element.setAttribute('data-likhari-poetry-width', String(width));
|
|
147
|
+
applyJustifyStyle(element);
|
|
148
|
+
applyWidthStyle(element, width);
|
|
149
|
+
return { element };
|
|
150
|
+
}
|
|
151
|
+
createDOM(config) {
|
|
152
|
+
const element = document.createElement('div');
|
|
153
|
+
const base = config.theme.poetry ?? 'likhari-poetry';
|
|
154
|
+
addClassNamesToElement(element, base, `${base}--${this.__layout}`, `${base}--spacing-${this.__spacing}`, `${base}--gutter-${this.__gutter}`, `${base}--stagger-${this.__stagger}`);
|
|
155
|
+
applyJustifyStyle(element);
|
|
156
|
+
applyWidthStyle(element, this.__width);
|
|
157
|
+
return element;
|
|
158
|
+
}
|
|
159
|
+
// Only ever adds/removes modifier classes, never touches the element's full
|
|
160
|
+
// className — the reconciler separately classList.adds the per-block ltr/rtl
|
|
161
|
+
// theme class (editor.css) onto this same element, and a wholesale className
|
|
162
|
+
// reassignment here would silently wipe it out.
|
|
163
|
+
updateDOM(prevNode, dom, config) {
|
|
164
|
+
const base = config.theme.poetry ?? 'likhari-poetry';
|
|
165
|
+
if (prevNode.__layout !== this.__layout) {
|
|
166
|
+
dom.classList.remove(`${base}--${prevNode.__layout}`);
|
|
167
|
+
dom.classList.add(`${base}--${this.__layout}`);
|
|
168
|
+
}
|
|
169
|
+
if (prevNode.__spacing !== this.__spacing) {
|
|
170
|
+
dom.classList.remove(`${base}--spacing-${prevNode.__spacing}`);
|
|
171
|
+
dom.classList.add(`${base}--spacing-${this.__spacing}`);
|
|
172
|
+
}
|
|
173
|
+
if (prevNode.__gutter !== this.__gutter) {
|
|
174
|
+
dom.classList.remove(`${base}--gutter-${prevNode.__gutter}`);
|
|
175
|
+
dom.classList.add(`${base}--gutter-${this.__gutter}`);
|
|
176
|
+
}
|
|
177
|
+
if (prevNode.__stagger !== this.__stagger) {
|
|
178
|
+
dom.classList.remove(`${base}--stagger-${prevNode.__stagger}`);
|
|
179
|
+
dom.classList.add(`${base}--stagger-${this.__stagger}`);
|
|
180
|
+
}
|
|
181
|
+
if (prevNode.__width !== this.__width) {
|
|
182
|
+
applyWidthStyle(dom, this.__width);
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
canBeEmpty() {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
isShadowRoot() {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export function $createPoetryBlockNode(layout = 'single', width, spacing = DEFAULT_POETRY_SPACING, gutter = DEFAULT_POETRY_GUTTER, stagger = DEFAULT_POETRY_STAGGER) {
|
|
194
|
+
return $applyNodeReplacement(new PoetryBlockNode(layout, width, spacing, gutter, stagger));
|
|
195
|
+
}
|
|
196
|
+
export function $isPoetryBlockNode(node) {
|
|
197
|
+
return node instanceof PoetryBlockNode;
|
|
198
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A floating drag handle over the currently-active poetry couplet's
|
|
3
|
+
* inline-end edge, letting the user resize how wide the block reads on the
|
|
4
|
+
* page — "size it according to the length of the couplet" — rather than
|
|
5
|
+
* being stuck with editor.css's default max-width: 70%. Rendered as a
|
|
6
|
+
* portal into the editor's own portal-target element, never as a child of
|
|
7
|
+
* the couplet's own DOM, so it can't interfere with Lexical's
|
|
8
|
+
* reconciliation of the couplet's real (editable) children.
|
|
9
|
+
*/
|
|
10
|
+
export declare function PoetryResizer(): import("react").JSX.Element | null;
|
|
11
|
+
//# sourceMappingURL=PoetryResizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoetryResizer.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryResizer.tsx"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAgB,aAAa,uCAqF5B"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
5
|
+
import { $getNodeByKey } from 'lexical';
|
|
6
|
+
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
7
|
+
import { $getPoetryBlockFromSelection } from './poetryActions.js';
|
|
8
|
+
import { $isPoetryBlockNode } from './PoetryNode.js';
|
|
9
|
+
const MIN_WIDTH_PX = 160;
|
|
10
|
+
function measure(el) {
|
|
11
|
+
const box = el.getBoundingClientRect();
|
|
12
|
+
return { top: box.top, left: box.left, width: box.width, height: box.height, rtl: getComputedStyle(el).direction === 'rtl' };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A floating drag handle over the currently-active poetry couplet's
|
|
16
|
+
* inline-end edge, letting the user resize how wide the block reads on the
|
|
17
|
+
* page — "size it according to the length of the couplet" — rather than
|
|
18
|
+
* being stuck with editor.css's default max-width: 70%. Rendered as a
|
|
19
|
+
* portal into the editor's own portal-target element, never as a child of
|
|
20
|
+
* the couplet's own DOM, so it can't interfere with Lexical's
|
|
21
|
+
* reconciliation of the couplet's real (editable) children.
|
|
22
|
+
*/
|
|
23
|
+
export function PoetryResizer() {
|
|
24
|
+
const [editor] = useLexicalComposerContext();
|
|
25
|
+
const portalSelector = usePortalTarget();
|
|
26
|
+
const [activeKey, setActiveKey] = useState(null);
|
|
27
|
+
const [box, setBox] = useState(null);
|
|
28
|
+
const dragStart = useRef(null);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const recompute = () => {
|
|
31
|
+
if (dragStart.current)
|
|
32
|
+
return; // the pointermove handler owns `box` mid-drag
|
|
33
|
+
editor.getEditorState().read(() => {
|
|
34
|
+
const couplet = $getPoetryBlockFromSelection();
|
|
35
|
+
const el = couplet ? editor.getElementByKey(couplet.getKey()) : null;
|
|
36
|
+
if (!couplet || !el) {
|
|
37
|
+
setActiveKey(null);
|
|
38
|
+
setBox(null);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setActiveKey(couplet.getKey());
|
|
42
|
+
setBox(measure(el));
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
recompute();
|
|
46
|
+
const unregister = editor.registerUpdateListener(recompute);
|
|
47
|
+
window.addEventListener('resize', recompute);
|
|
48
|
+
window.addEventListener('scroll', recompute, true);
|
|
49
|
+
return () => {
|
|
50
|
+
unregister();
|
|
51
|
+
window.removeEventListener('resize', recompute);
|
|
52
|
+
window.removeEventListener('scroll', recompute, true);
|
|
53
|
+
};
|
|
54
|
+
}, [editor]);
|
|
55
|
+
if (!box || !activeKey)
|
|
56
|
+
return null;
|
|
57
|
+
const onPointerDown = (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
60
|
+
dragStart.current = { x: event.clientX, width: box.width, rtl: box.rtl };
|
|
61
|
+
};
|
|
62
|
+
const onPointerMove = (event) => {
|
|
63
|
+
const start = dragStart.current;
|
|
64
|
+
const el = editor.getElementByKey(activeKey);
|
|
65
|
+
if (!start || !el)
|
|
66
|
+
return;
|
|
67
|
+
const delta = event.clientX - start.x;
|
|
68
|
+
const signedDelta = start.rtl ? -delta : delta;
|
|
69
|
+
const canvasMax = editor.getRootElement()?.clientWidth ?? start.width + signedDelta;
|
|
70
|
+
const width = Math.round(Math.min(Math.max(start.width + signedDelta, MIN_WIDTH_PX), canvasMax));
|
|
71
|
+
// Live visual feedback: the couplet is centered (margin: auto), so
|
|
72
|
+
// resizing it moves both edges — re-measure after mutating rather than
|
|
73
|
+
// computing the new position from the drag delta alone.
|
|
74
|
+
el.style.maxWidth = `${width}px`;
|
|
75
|
+
setBox(measure(el));
|
|
76
|
+
};
|
|
77
|
+
const onPointerUp = () => {
|
|
78
|
+
const start = dragStart.current;
|
|
79
|
+
dragStart.current = null;
|
|
80
|
+
const el = editor.getElementByKey(activeKey);
|
|
81
|
+
const width = el ? Math.round(el.getBoundingClientRect().width) : null;
|
|
82
|
+
if (start && width !== null) {
|
|
83
|
+
editor.update(() => {
|
|
84
|
+
const node = $getNodeByKey(activeKey);
|
|
85
|
+
if ($isPoetryBlockNode(node))
|
|
86
|
+
node.setWidth(width);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const handleX = box.rtl ? box.left : box.left + box.width;
|
|
91
|
+
const handle = (_jsx("div", { className: "likhari-poetry-resize-handle", style: { position: 'fixed', top: box.top, left: handleX, height: box.height }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, role: "separator", "aria-orientation": "vertical" }));
|
|
92
|
+
const portalRoot = portalSelector ? document.querySelector(portalSelector) : null;
|
|
93
|
+
return portalRoot ? createPortal(handle, portalRoot) : handle;
|
|
94
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type ParagraphNode } from 'lexical';
|
|
2
|
+
import { type PoetryBlockNode, type PoetryLayout } from './PoetryNode.js';
|
|
3
|
+
/** One couplet's two misra paragraphs, in order. */
|
|
4
|
+
export type Couplet = [ParagraphNode, ParagraphNode];
|
|
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.
|
|
12
|
+
*/
|
|
13
|
+
export declare function $getCouplets(node: PoetryBlockNode): Couplet[];
|
|
14
|
+
/** Every misra in the block, flattened across all its couplets — for
|
|
15
|
+
* operations (like $setPoetryLayout) that don't care about couplet
|
|
16
|
+
* boundaries, just the full set of lines. */
|
|
17
|
+
export declare function $getMisraParagraphs(node: PoetryBlockNode): ParagraphNode[];
|
|
18
|
+
/** The PoetryBlockNode the current selection is inside, or null. */
|
|
19
|
+
export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
|
|
20
|
+
/**
|
|
21
|
+
* The explicit "Insert couplet above/below" menu actions — mirrors the
|
|
22
|
+
* table menu's "Insert row before/after". Targets the couplet the caret is
|
|
23
|
+
* currently in, falling back to the block's last couplet (so the toolbar's
|
|
24
|
+
* always-available insert button has somewhere sensible to act on even
|
|
25
|
+
* without a precise caret position).
|
|
26
|
+
*/
|
|
27
|
+
export declare function $insertCoupletRelativeToSelection(position: 'before' | 'after'): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Converts every couplet in the block between single-column (two stacked
|
|
30
|
+
* misras) and two-column (built on the columns primitive, §4.9) in place —
|
|
31
|
+
* pulls each couplet's two misra paragraphs out of whichever shape
|
|
32
|
+
* currently holds them and rebuilds the other shape around the same
|
|
33
|
+
* paragraph nodes, preserving couplet order, so content, selection and
|
|
34
|
+
* undo history all survive the conversion.
|
|
35
|
+
*/
|
|
36
|
+
export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
|
|
37
|
+
/** A block at the very end of the document would otherwise leave no
|
|
38
|
+
* editable block to click or arrow down into — appends one empty paragraph
|
|
39
|
+
* if the block doesn't already have a next sibling. Idempotent: once a
|
|
40
|
+
* sibling exists (that paragraph, or anything else), this is a no-op, so
|
|
41
|
+
* it's safe to call from both the insert command and a node transform that
|
|
42
|
+
* keeps the invariant even after later edits (e.g. deleting the trailing
|
|
43
|
+
* paragraph, or JSON content loaded without one). */
|
|
44
|
+
export declare function $ensureTrailingParagraph(node: PoetryBlockNode): void;
|
|
45
|
+
/**
|
|
46
|
+
* Inserts a poetry couplet at the caret. If the caret is already inside a
|
|
47
|
+
* poetry block, inserts a new couplet right after the one the caret is in
|
|
48
|
+
* (same as the "Insert couplet below" menu action — table-row-insert
|
|
49
|
+
* semantics, in its existing layout: "one poetry block can contain one or
|
|
50
|
+
* more couplets, in one or two column layout", so a block's layout is
|
|
51
|
+
* fixed once it has couplets, not chosen per couplet) and focuses its first
|
|
52
|
+
* misra. Otherwise creates a new block (in `layout`) with one
|
|
53
|
+
* couplet, after the selection's top-level element.
|
|
54
|
+
*/
|
|
55
|
+
export declare function $insertPoetryCouplet(layout: PoetryLayout): boolean;
|
|
56
|
+
/** Removes the couplet the selection is inside. If it's the block's only
|
|
57
|
+
* couplet, removes the whole block (an empty poetry section serves no
|
|
58
|
+
* purpose) — the explicit, always-available counterpart to
|
|
59
|
+
* $deletePoetryOnBackspace's empty-couplet shortcut (mirrors the table
|
|
60
|
+
* menu's own "Delete table" action). */
|
|
61
|
+
export declare function $deletePoetryCouplet(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Backspace at the start of a misra never merges it into its neighbour: the
|
|
64
|
+
* block's lines are paired into couplets, so merging one line away would
|
|
65
|
+
* shift every couplet after it (and take the previous couplet's last line
|
|
66
|
+
* with it). Instead, Backspace at the start of an empty couplet removes that
|
|
67
|
+
* couplet, and at the start of any other line it moves the caret to the end
|
|
68
|
+
* of the previous line. Other caret positions (deleting within text) fall
|
|
69
|
+
* through to normal handling.
|
|
70
|
+
*/
|
|
71
|
+
export declare function $deletePoetryOnBackspace(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Forward Delete at the end of a misra never merges the next line into it, for
|
|
74
|
+
* the same reason as $deletePoetryOnBackspace: the caret moves to the start of
|
|
75
|
+
* the next line instead. Other caret positions fall through to normal handling.
|
|
76
|
+
*/
|
|
77
|
+
export declare function $deletePoetryForward(): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Enter inside a poetry block never grows a couplet past its fixed two
|
|
80
|
+
* misras. The block's *last* couplet being completely empty is always the
|
|
81
|
+
* exit trigger, regardless of which of its two (both-empty) misras the
|
|
82
|
+
* caret happens to be on — pressing Enter once on a non-empty last couplet
|
|
83
|
+
* appends a fresh empty one to continue into ("new couplet after last"),
|
|
84
|
+
* and pressing Enter again on that still-untouched couplet exits ("double
|
|
85
|
+
* enter to exit the block"): the same empty-item convention most editors
|
|
86
|
+
* use for lists. Outside that case: pressed in a couplet's first misra,
|
|
87
|
+
* Enter moves the caret to the second (already there; nothing inserted —
|
|
88
|
+
* this is what stops ParagraphNode's default insertNewAfter from growing
|
|
89
|
+
* the couplet, which would otherwise insert the new paragraph as a sibling
|
|
90
|
+
* of wherever the caret was, i.e. still inside the couplet); pressed in
|
|
91
|
+
* the last misra of a couplet that isn't the block's last, it moves to the
|
|
92
|
+
* next couplet's first misra.
|
|
93
|
+
*/
|
|
94
|
+
export declare function $exitPoetryOnEnter(): boolean;
|
|
95
|
+
/** Steps the caret's poetry block one notch looser (+1) or tighter (-1) on
|
|
96
|
+
* the couplet-spacing scale; clamps at either end. */
|
|
97
|
+
export declare function $adjustPoetrySpacing(step: 1 | -1): boolean;
|
|
98
|
+
/** Steps the caret's two-column gutter one notch wider (+1) or narrower (-1);
|
|
99
|
+
* clamps at either end. */
|
|
100
|
+
export declare function $adjustPoetryGutter(step: 1 | -1): boolean;
|
|
101
|
+
/** Steps the caret's staggered couplet width one notch narrower (-1) or wider
|
|
102
|
+
* (+1); clamps at either end. */
|
|
103
|
+
export declare function $adjustPoetryStagger(step: 1 | -1): boolean;
|
|
104
|
+
/** Whether the couplet the caret is in is centered, or null outside a
|
|
105
|
+
* two-column poetry block (centering only applies there). */
|
|
106
|
+
export declare function $getCoupletCenteredFromSelection(): boolean | null;
|
|
107
|
+
/** Centers (or un-centers) the caret's couplet within a two-column block by
|
|
108
|
+
* rebuilding its row around the same two misra paragraphs. */
|
|
109
|
+
export declare function $setCoupletCentered(centered: boolean): boolean;
|
|
110
|
+
/** Removes the whole poetry block the caret is in, including every couplet. */
|
|
111
|
+
export declare function $deletePoetryBlock(): boolean;
|
|
112
|
+
/** Single-column blocks pair their paragraphs into couplets, so a line left
|
|
113
|
+
* without a partner (from pasted or imported content) would be invisible to
|
|
114
|
+
* the couplet logic. Give it an empty partner instead, so every line belongs
|
|
115
|
+
* to a couplet and can be deleted like any other. */
|
|
116
|
+
export declare function $completeSingleCoupletBlock(node: PoetryBlockNode): void;
|
|
117
|
+
//# 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,EAOL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,EAAE,CAqB7D;AAED;;6CAE6C;AAC7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAE1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AA0DD;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAevF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAmBlF;AAED;;;;;;qDAMqD;AACrD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAiBlE;AAED;;;;wCAIwC;AACxC,wBAAgB,oBAAoB,IAAI,OAAO,CAwB9C;AAUD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAsClD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAkB9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAgD5C;AAED;sDACsD;AACtD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAO1D;AAED;2BAC2B;AAC3B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAOzD;AAED;iCACiC;AACjC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAO1D;AAYD;6DAC6D;AAC7D,wBAAgB,gCAAgC,IAAI,OAAO,GAAG,IAAI,CAUjE;AAED;8DAC8D;AAC9D,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAuB9D;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,IAAI,OAAO,CAK5C;AAED;;;qDAGqD;AACrD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAGvE"}
|