@llui/markdown-editor 0.2.14 → 0.4.0
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/editor.d.ts +22 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +45 -6
- package/dist/editor.js.map +1 -1
- package/dist/effects.d.ts +0 -1
- package/dist/effects.d.ts.map +1 -1
- package/dist/effects.js +4 -1
- package/dist/effects.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/plugins/block-drag.d.ts +73 -0
- package/dist/plugins/block-drag.d.ts.map +1 -0
- package/dist/plugins/block-drag.js +760 -0
- package/dist/plugins/block-drag.js.map +1 -0
- package/dist/plugins/code-language.d.ts +60 -0
- package/dist/plugins/code-language.d.ts.map +1 -0
- package/dist/plugins/code-language.js +308 -0
- package/dist/plugins/code-language.js.map +1 -0
- package/dist/plugins/frontmatter.d.ts +45 -0
- package/dist/plugins/frontmatter.d.ts.map +1 -0
- package/dist/plugins/frontmatter.js +281 -0
- package/dist/plugins/frontmatter.js.map +1 -0
- package/dist/plugins/overlay.d.ts +1 -0
- package/dist/plugins/overlay.d.ts.map +1 -1
- package/dist/plugins/overlay.js +7 -0
- package/dist/plugins/overlay.js.map +1 -1
- package/dist/plugins/table.d.ts.map +1 -1
- package/dist/plugins/table.js +77 -15
- package/dist/plugins/table.js.map +1 -1
- package/dist/plugins/wikilink.d.ts +91 -0
- package/dist/plugins/wikilink.d.ts.map +1 -0
- package/dist/plugins/wikilink.js +467 -0
- package/dist/plugins/wikilink.js.map +1 -0
- package/dist/styles/block-drag.css +63 -0
- package/dist/styles/editor.css +64 -6
- package/dist/theme.d.ts +2 -5
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +15 -11
- package/dist/theme.js.map +1 -1
- package/dist/transformers/code.d.ts +24 -0
- package/dist/transformers/code.d.ts.map +1 -0
- package/dist/transformers/code.js +167 -0
- package/dist/transformers/code.js.map +1 -0
- package/dist/transformers/gfm.d.ts +9 -1
- package/dist/transformers/gfm.d.ts.map +1 -1
- package/dist/transformers/gfm.js +16 -4
- package/dist/transformers/gfm.js.map +1 -1
- package/dist/transformers/registry.d.ts +3 -0
- package/dist/transformers/registry.d.ts.map +1 -1
- package/dist/transformers/registry.js +26 -0
- package/dist/transformers/registry.js.map +1 -1
- package/package.json +45 -31
- package/src/editor.ts +77 -9
- package/src/effects.ts +4 -2
- package/src/index.ts +61 -9
- package/src/plugins/block-drag.ts +912 -0
- package/src/plugins/code-language.ts +378 -0
- package/src/plugins/frontmatter.ts +324 -0
- package/src/plugins/overlay.ts +7 -0
- package/src/plugins/table.ts +87 -13
- package/src/plugins/wikilink.ts +553 -0
- package/src/styles/block-drag.css +63 -0
- package/src/styles/editor.css +64 -6
- package/src/theme.ts +15 -11
- package/src/transformers/code.ts +174 -0
- package/src/transformers/gfm.ts +16 -4
- package/src/transformers/registry.ts +27 -0
- package/dist/__llui_deps.json +0 -274
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { TextNode, type EditorConfig, type LexicalEditor, type LexicalNode, type LexicalUpdateJSON, type NodeKey, type SerializedTextNode, type Spread } from 'lexical';
|
|
2
|
+
import type { MarkdownPlugin } from './types.js';
|
|
3
|
+
/** A parsed wikilink. `alias` is `null` when the target is shown verbatim. */
|
|
4
|
+
export interface WikiLink {
|
|
5
|
+
target: string;
|
|
6
|
+
alias: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Parse the content BETWEEN the brackets. Returns `null` when the content is not
|
|
10
|
+
* a valid wikilink body.
|
|
11
|
+
*
|
|
12
|
+
* Deliberate choices, each load-bearing for exact round-tripping:
|
|
13
|
+
* * split on the FIRST `|` only, so `[[a|b|c]]` has alias `b|c` and re-exports
|
|
14
|
+
* byte-identically;
|
|
15
|
+
* * an EMPTY alias (`[[a|]]`) normalizes to no alias — the alternative
|
|
16
|
+
* (keeping `alias: ''`) would render a zero-width, unclickable node;
|
|
17
|
+
* * NO trimming. `[[ a ]]` keeps its spaces, because trimming would make
|
|
18
|
+
* import→export lossy. Presentation trimming is the host's call in
|
|
19
|
+
* `onNavigate`/`resolve`, not the document's.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseWikiLinkInner(inner: string): WikiLink | null;
|
|
22
|
+
/** Sanitize a target. Returns `null` when nothing usable survives. */
|
|
23
|
+
export declare function sanitizeWikiLinkTarget(raw: string): string | null;
|
|
24
|
+
/** Sanitize an alias. Returns `null` when nothing usable survives. */
|
|
25
|
+
export declare function sanitizeWikiLinkAlias(raw: string | null): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Serialize a wikilink back to markdown. Inverse of {@link parseWikiLinkInner}
|
|
28
|
+
* for every link built through this module's constructors — see
|
|
29
|
+
* {@link sanitizeWikiLinkTarget} for why that qualifier is load-bearing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatWikiLink(link: WikiLink): string;
|
|
32
|
+
export type SerializedWikiLinkNode = Spread<{
|
|
33
|
+
target: string;
|
|
34
|
+
alias: string | null;
|
|
35
|
+
}, SerializedTextNode>;
|
|
36
|
+
/**
|
|
37
|
+
* An atomic inline wikilink. Extends `TextNode` so the caret, selection and
|
|
38
|
+
* text formats behave exactly as they do for prose, while `token` mode keeps it
|
|
39
|
+
* indivisible: the user can delete it or move past it, but never edit its
|
|
40
|
+
* interior into a state where the visible alias disagrees with `__target`.
|
|
41
|
+
*/
|
|
42
|
+
export declare class WikiLinkNode extends TextNode {
|
|
43
|
+
__target: string;
|
|
44
|
+
__alias: string | null;
|
|
45
|
+
static getType(): string;
|
|
46
|
+
static clone(node: WikiLinkNode): WikiLinkNode;
|
|
47
|
+
constructor(target: string, alias: string | null, text?: string, key?: NodeKey);
|
|
48
|
+
static importJSON(serializedNode: SerializedWikiLinkNode): WikiLinkNode;
|
|
49
|
+
updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedWikiLinkNode>): this;
|
|
50
|
+
exportJSON(): SerializedWikiLinkNode;
|
|
51
|
+
createDOM(config: EditorConfig, editor?: LexicalEditor): HTMLElement;
|
|
52
|
+
updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean;
|
|
53
|
+
getTarget(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Repoint the link. The display text follows only when there is no alias.
|
|
56
|
+
* The value is sanitized (see {@link sanitizeWikiLinkTarget}); a target with
|
|
57
|
+
* nothing representable in it leaves the link untouched rather than producing
|
|
58
|
+
* one that corrupts the document on export.
|
|
59
|
+
*/
|
|
60
|
+
setTarget(target: string): this;
|
|
61
|
+
getAlias(): string | null;
|
|
62
|
+
/** Set (or clear, with `null`) the alias; the display text follows. */
|
|
63
|
+
setAlias(alias: string | null): this;
|
|
64
|
+
getLink(): WikiLink;
|
|
65
|
+
canInsertTextBefore(): boolean;
|
|
66
|
+
canInsertTextAfter(): boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a wikilink node. `target`/`alias` are sanitized to values the `[[…]]`
|
|
70
|
+
* syntax can express (see {@link sanitizeWikiLinkTarget}); a target with nothing
|
|
71
|
+
* usable left falls back to the literal text `Page` rather than yielding an
|
|
72
|
+
* invisible token.
|
|
73
|
+
*/
|
|
74
|
+
export declare function $createWikiLinkNode(target: string, alias?: string | null): WikiLinkNode;
|
|
75
|
+
export declare function $isWikiLinkNode(node: LexicalNode | null | undefined): node is WikiLinkNode;
|
|
76
|
+
export interface WikiLinkPluginOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Called when the user activates a wikilink. This is the host's resolution
|
|
79
|
+
* seam: `@llui/markdown-editor` knows nothing about what a target names.
|
|
80
|
+
*
|
|
81
|
+
* The notification travels the same route as every other plugin event —
|
|
82
|
+
* `ctx.emit` → the editor's update loop → this plugin's reducer → an effect —
|
|
83
|
+
* rather than a raw DOM event, so an activation is an ordinary TEA message
|
|
84
|
+
* that shows up in devtools, replay and agent traces.
|
|
85
|
+
*/
|
|
86
|
+
onNavigate?: (link: WikiLink) => void;
|
|
87
|
+
/** Text used as the target when the insert command runs with no selection. */
|
|
88
|
+
placeholderTarget?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare function wikilinkPlugin(opts?: WikiLinkPluginOptions): MarkdownPlugin;
|
|
91
|
+
//# sourceMappingURL=wikilink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wikilink.d.ts","sourceRoot":"","sources":["../../src/plugins/wikilink.ts"],"names":[],"mappings":"AAiEA,OAAO,EAQL,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAA;AAKhB,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,YAAY,CAAA;AAI7D,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAkBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAajE;AAwCD,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAMvE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAErD;AAmBD,MAAM,MAAM,sBAAsB,GAAG,MAAM,CACzC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACxC,kBAAkB,CACnB,CAAA;AAED;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAEtB,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY;gBAIlC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO;IAM9E,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,sBAAsB,GAAG,YAAY;IAMvE,cAAc,CAAC,cAAc,EAAE,iBAAiB,CAAC,sBAAsB,CAAC,GAAG,IAAI;IAQ/E,UAAU,IAAI,sBAAsB;IAIpC,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,WAAW;IAgBpE,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAc1E,SAAS,IAAI,MAAM;IAInB;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAS/B,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQpC,OAAO,IAAI,QAAQ;IAOnB,mBAAmB,IAAI,OAAO;IAI9B,kBAAkB,IAAI,OAAO;CAG9B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,GAAG,YAAY,CAK7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,YAAY,CAE1F;AAuGD,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IACrC,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAgB,cAAc,CAAC,IAAI,GAAE,qBAA0B,GAAG,cAAc,CA2E/E"}
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
// Wikilink plugin — `[[target]]` / `[[target|alias]]` inline references.
|
|
2
|
+
//
|
|
3
|
+
// The ALIAS is what the reader sees; the TARGET is what the link points at.
|
|
4
|
+
// Semantics are lifted from lance's markdown-it inline rule: a wikilink opens
|
|
5
|
+
// with `[[`, closes at the FIRST `]]`, splits on the FIRST `|`, and is rejected
|
|
6
|
+
// when the inner content is empty or contains a nested `[[`.
|
|
7
|
+
//
|
|
8
|
+
// ── Why a custom TextNode and NOT `@lexical/link` ────────────────────────────
|
|
9
|
+
//
|
|
10
|
+
// Reusing the link infrastructure looks tempting (a wikilink IS a link) but is
|
|
11
|
+
// wrong here on four counts:
|
|
12
|
+
//
|
|
13
|
+
// 1. A `LinkNode` is an ELEMENT node: the display text lives in its children,
|
|
14
|
+
// the destination in `getURL()`. A wikilink's target is not a URL — it is
|
|
15
|
+
// a document name resolved by the host. Storing it in `url` means every
|
|
16
|
+
// wikilink flows through this package's link-security layer
|
|
17
|
+
// (`sanitizeLinkUrl` + the global `registerLinkSanitizer` node transform,
|
|
18
|
+
// see `../security.ts`), which exists precisely to rewrite/unwrap hrefs it
|
|
19
|
+
// does not recognize. A wikilink would have to be carved out of the one
|
|
20
|
+
// enforcement point the editor relies on — a security seam we refuse to cut.
|
|
21
|
+
// 2. Because the display text is a child text node, editing it desyncs the
|
|
22
|
+
// alias from the target silently: type inside `[[Page|alias]]` and you get
|
|
23
|
+
// a link whose text no longer matches anything the exporter can round-trip.
|
|
24
|
+
// A `TextNode` subclass in `token` mode is ATOMIC — it is selected, moved
|
|
25
|
+
// and deleted as one unit, so target/alias can never drift.
|
|
26
|
+
// 3. `$toggleLink`, the link dialog, the floating link toolbar and autolink
|
|
27
|
+
// would all treat a wikilink as an ordinary hyperlink and happily rewrite it.
|
|
28
|
+
// 4. Both directions of the markdown conversion then live in ONE text-match
|
|
29
|
+
// transformer over ONE node type, instead of being split across the LINK
|
|
30
|
+
// transformer with a sentinel-scheme escape hatch.
|
|
31
|
+
//
|
|
32
|
+
// A `DecoratorNode` (the `LLuiDecoratorNode` bridge used by math/mermaid) was
|
|
33
|
+
// the other candidate and is also wrong: a decorator is an opaque, non-editable
|
|
34
|
+
// island. A wikilink is inline prose — the caret must move through and around
|
|
35
|
+
// it naturally, and it must inherit the surrounding text format. `TextNode` is
|
|
36
|
+
// the node kind that gives that for free.
|
|
37
|
+
//
|
|
38
|
+
// ── Transformer ordering ─────────────────────────────────────────────────────
|
|
39
|
+
//
|
|
40
|
+
// `transformers/registry.ts` ranks by type (text-match last) and, within a rank,
|
|
41
|
+
// by plugin array order. `@lexical/markdown` resolves competing text-match
|
|
42
|
+
// transformers with `findOutermostTextMatchTransformer`, which prefers the
|
|
43
|
+
// EARLIEST/outermost match and falls back to array order on a tie.
|
|
44
|
+
//
|
|
45
|
+
// * Typing (`registerMarkdownShortcuts`) indexes transformers by their single
|
|
46
|
+
// `trigger` character. Ours is `]`, LINK's is `)`, so they can never collide.
|
|
47
|
+
// * On IMPORT, LINK's `importRegExp` requires a `](`, which `[[a]]` and
|
|
48
|
+
// `[[a|b]]` never contain — plain wikilinks are safe at any position.
|
|
49
|
+
// * The ambiguity is a wikilink on a line that also contains a `](` — either
|
|
50
|
+
// immediately (`[[a|b]](c)`) or LATER on the same line
|
|
51
|
+
// (`**[[A]]** and [b](url)`). LINK's import pattern has a LAZY label
|
|
52
|
+
// (`\[(.+?)\]\(…\)`), so it can match starting at the WIKILINK's own `[` and
|
|
53
|
+
// span across to that later `](`. Both rules then match at the same start
|
|
54
|
+
// index, and a tie is broken by array order.
|
|
55
|
+
//
|
|
56
|
+
// That made fidelity depend on plugin order, and the failure was silent: with
|
|
57
|
+
// `wikilinkPlugin()` listed after `corePlugin()`, LINK swallowed the `**`
|
|
58
|
+
// delimiters and re-exported them ESCAPED, losing the bold outright.
|
|
59
|
+
//
|
|
60
|
+
// This is now resolved STRUCTURALLY, not by documentation: the transformer
|
|
61
|
+
// declares `setTransformerPrecedence(WIKILINK_TRANSFORMER, -10)` (see the
|
|
62
|
+
// bottom of this file), so the registry orders it ahead of LINK in EITHER
|
|
63
|
+
// plugin order. `test/composition.test.ts` pins both orders. To get the
|
|
64
|
+
// CommonMark reading of `[[a|b]](c)` instead, omit `wikilinkPlugin()`.
|
|
65
|
+
import { $applyNodeReplacement, $getNearestNodeFromDOMNode, $getSelection, $isRangeSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, KEY_ENTER_COMMAND, TextNode, } from 'lexical';
|
|
66
|
+
import { mergeRegister } from '@lexical/utils';
|
|
67
|
+
import { setTransformerPrecedence } from '../transformers/registry.js';
|
|
68
|
+
import { definePluginUI } from './ui.js';
|
|
69
|
+
const PLUGIN = 'wikilink';
|
|
70
|
+
// ── Parsing ──────────────────────────────────────────────────────────────────
|
|
71
|
+
/**
|
|
72
|
+
* Matches `[[inner]]` where `inner` is non-empty and contains neither `[[` nor
|
|
73
|
+
* `]]`. Encoding both rejections IN the pattern (rather than validating inside
|
|
74
|
+
* `replace`) is what reproduces lance's scan-and-retry behaviour: markdown-it
|
|
75
|
+
* returns `false` at a bad `[[` and re-attempts one character later, so
|
|
76
|
+
* `[[a[[b]]` yields the INNER `[[b]]`. A regex that matched greedily at index 0
|
|
77
|
+
* and then bailed in `replace` would swallow the valid inner link instead.
|
|
78
|
+
*/
|
|
79
|
+
const WIKILINK_RE_SOURCE = String.raw `\[\[((?:(?!\[\[|\]\])[\s\S])+)\]\]`;
|
|
80
|
+
const WIKILINK_IMPORT_RE = new RegExp(WIKILINK_RE_SOURCE);
|
|
81
|
+
/** Same pattern anchored at the caret, for the live typing shortcut. */
|
|
82
|
+
const WIKILINK_TYPING_RE = new RegExp(`${WIKILINK_RE_SOURCE}$`);
|
|
83
|
+
/**
|
|
84
|
+
* Parse the content BETWEEN the brackets. Returns `null` when the content is not
|
|
85
|
+
* a valid wikilink body.
|
|
86
|
+
*
|
|
87
|
+
* Deliberate choices, each load-bearing for exact round-tripping:
|
|
88
|
+
* * split on the FIRST `|` only, so `[[a|b|c]]` has alias `b|c` and re-exports
|
|
89
|
+
* byte-identically;
|
|
90
|
+
* * an EMPTY alias (`[[a|]]`) normalizes to no alias — the alternative
|
|
91
|
+
* (keeping `alias: ''`) would render a zero-width, unclickable node;
|
|
92
|
+
* * NO trimming. `[[ a ]]` keeps its spaces, because trimming would make
|
|
93
|
+
* import→export lossy. Presentation trimming is the host's call in
|
|
94
|
+
* `onNavigate`/`resolve`, not the document's.
|
|
95
|
+
*/
|
|
96
|
+
export function parseWikiLinkInner(inner) {
|
|
97
|
+
if (inner.length === 0 || inner.includes('[['))
|
|
98
|
+
return null;
|
|
99
|
+
const pipe = inner.indexOf('|');
|
|
100
|
+
const target = pipe >= 0 ? inner.slice(0, pipe) : inner;
|
|
101
|
+
// A blank target is as unusable as an empty one: `[[ ]]` would build a
|
|
102
|
+
// token-mode node whose only glyph is a space — invisible, unclickable and
|
|
103
|
+
// (being a token) uneditable except by deleting it wholesale. Same rejection
|
|
104
|
+
// the empty case gets. Note this is a BLANK check, not a trim: a target that
|
|
105
|
+
// has any non-space content keeps its spaces verbatim, so `[[ a ]]` still
|
|
106
|
+
// round-trips byte-identically.
|
|
107
|
+
if (target.trim().length === 0)
|
|
108
|
+
return null;
|
|
109
|
+
const rawAlias = pipe >= 0 ? inner.slice(pipe + 1) : null;
|
|
110
|
+
return { target, alias: rawAlias !== null && rawAlias.trim().length > 0 ? rawAlias : null };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Constrain a target/alias to the values the `[[…]]` syntax can actually
|
|
114
|
+
* express, so that {@link formatWikiLink} is INJECTIVE.
|
|
115
|
+
*
|
|
116
|
+
* Without this, `formatWikiLink` is not the inverse it claims to be: a target
|
|
117
|
+
* of `a|b` emits `[[a|b]]`, which reads back as target `a` with alias `b` — the
|
|
118
|
+
* link silently repoints itself and its visible text changes on the next load.
|
|
119
|
+
* A target of `a]]b` emits `[[a]]b]]`, which reads back as a wikilink to `a`
|
|
120
|
+
* followed by the literal text `b]]`.
|
|
121
|
+
*
|
|
122
|
+
* ── Why sanitize rather than introduce an escape dialect ─────────────────────
|
|
123
|
+
*
|
|
124
|
+
* Backslash-escaping `|`/`[`/`]` would make every value representable, but it
|
|
125
|
+
* forks the syntax away from the markdown-it/Obsidian rule this plugin exists
|
|
126
|
+
* to match: `[[a\|b]]` is not a wikilink to `a|b` anywhere else in the
|
|
127
|
+
* ecosystem, it is a wikilink to the literal `a\` with alias `b`. Emitting it
|
|
128
|
+
* would corrupt the document for every OTHER reader of the same file, which is
|
|
129
|
+
* strictly worse than declining to represent a target no wikilink dialect can
|
|
130
|
+
* express. Documents are shared; our in-memory model is not.
|
|
131
|
+
*
|
|
132
|
+
* So the unrepresentable characters are removed at the point a link is BUILT
|
|
133
|
+
* (`$createWikiLinkNode`, `setTarget`, `setAlias`, the insert command) — every
|
|
134
|
+
* route by which a value can enter the document. Everything downstream may then
|
|
135
|
+
* assume `parse(format(link)) === link`.
|
|
136
|
+
*/
|
|
137
|
+
function sanitizePart(raw, stripPipe) {
|
|
138
|
+
let out = raw
|
|
139
|
+
// Markdown is line-oriented: a newline inside `[[…]]` cannot survive an
|
|
140
|
+
// export/import cycle at all (neither half of the split matches).
|
|
141
|
+
.replace(/\s+/g, ' ')
|
|
142
|
+
// `[[` would open a nested link (which the import rule rejects outright);
|
|
143
|
+
// `]]` would close this one early and spill the remainder as literal text.
|
|
144
|
+
.replace(/\[\[+/g, '[')
|
|
145
|
+
.replace(/\]\]+/g, ']');
|
|
146
|
+
if (stripPipe)
|
|
147
|
+
out = out.replace(/\|/g, '');
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
/** Sanitize a target. Returns `null` when nothing usable survives. */
|
|
151
|
+
export function sanitizeWikiLinkTarget(raw) {
|
|
152
|
+
const out = sanitizePart(raw, true);
|
|
153
|
+
return out.trim().length > 0 ? out : null;
|
|
154
|
+
}
|
|
155
|
+
/** Sanitize an alias. Returns `null` when nothing usable survives. */
|
|
156
|
+
export function sanitizeWikiLinkAlias(raw) {
|
|
157
|
+
if (raw === null)
|
|
158
|
+
return null;
|
|
159
|
+
// A pipe is legal in an alias: the parser splits on the FIRST pipe only, so
|
|
160
|
+
// `[[a|b|c]]` unambiguously means alias `b|c` and re-exports byte-identically.
|
|
161
|
+
const out = sanitizePart(raw, false);
|
|
162
|
+
return out.trim().length > 0 ? out : null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Serialize a wikilink back to markdown. Inverse of {@link parseWikiLinkInner}
|
|
166
|
+
* for every link built through this module's constructors — see
|
|
167
|
+
* {@link sanitizeWikiLinkTarget} for why that qualifier is load-bearing.
|
|
168
|
+
*/
|
|
169
|
+
export function formatWikiLink(link) {
|
|
170
|
+
return link.alias === null ? `[[${link.target}]]` : `[[${link.target}|${link.alias}]]`;
|
|
171
|
+
}
|
|
172
|
+
/** The text a wikilink displays: the alias when present, else the target. */
|
|
173
|
+
function displayText(link) {
|
|
174
|
+
return link.alias ?? link.target;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* What a screen reader announces. An aliased link must name BOTH halves: the
|
|
178
|
+
* visible text is the alias, so the destination is otherwise unhearable.
|
|
179
|
+
*/
|
|
180
|
+
function ariaLabel(link) {
|
|
181
|
+
return link.alias === null
|
|
182
|
+
? `${link.target}, wiki link`
|
|
183
|
+
: `${link.alias}, wiki link to ${link.target}`;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An atomic inline wikilink. Extends `TextNode` so the caret, selection and
|
|
187
|
+
* text formats behave exactly as they do for prose, while `token` mode keeps it
|
|
188
|
+
* indivisible: the user can delete it or move past it, but never edit its
|
|
189
|
+
* interior into a state where the visible alias disagrees with `__target`.
|
|
190
|
+
*/
|
|
191
|
+
export class WikiLinkNode extends TextNode {
|
|
192
|
+
__target;
|
|
193
|
+
__alias;
|
|
194
|
+
static getType() {
|
|
195
|
+
return 'wikilink';
|
|
196
|
+
}
|
|
197
|
+
static clone(node) {
|
|
198
|
+
return new WikiLinkNode(node.__target, node.__alias, node.__text, node.__key);
|
|
199
|
+
}
|
|
200
|
+
constructor(target, alias, text, key) {
|
|
201
|
+
super(text ?? displayText({ target, alias }), key);
|
|
202
|
+
this.__target = target;
|
|
203
|
+
this.__alias = alias;
|
|
204
|
+
}
|
|
205
|
+
static importJSON(serializedNode) {
|
|
206
|
+
return $createWikiLinkNode(serializedNode.target, serializedNode.alias).updateFromJSON(serializedNode);
|
|
207
|
+
}
|
|
208
|
+
updateFromJSON(serializedNode) {
|
|
209
|
+
const self = super.updateFromJSON(serializedNode);
|
|
210
|
+
const writable = self.getWritable();
|
|
211
|
+
writable.__target = serializedNode.target;
|
|
212
|
+
writable.__alias = serializedNode.alias;
|
|
213
|
+
return writable;
|
|
214
|
+
}
|
|
215
|
+
exportJSON() {
|
|
216
|
+
return { ...super.exportJSON(), alias: this.getAlias(), target: this.getTarget() };
|
|
217
|
+
}
|
|
218
|
+
createDOM(config, editor) {
|
|
219
|
+
const dom = super.createDOM(config, editor);
|
|
220
|
+
// `data-scope` / `data-part` is this package's styling contract (see the
|
|
221
|
+
// other plugins); `data-wikilink` additionally carries the target so a host
|
|
222
|
+
// stylesheet can react to it and the click handler can find the element.
|
|
223
|
+
dom.setAttribute('data-scope', 'md-wikilink');
|
|
224
|
+
dom.setAttribute('data-part', 'link');
|
|
225
|
+
dom.setAttribute('data-wikilink', this.__target);
|
|
226
|
+
if (this.__alias !== null)
|
|
227
|
+
dom.setAttribute('data-wikilink-alias', this.__alias);
|
|
228
|
+
// Without a role, assistive tech reads a wikilink as ordinary prose, and an
|
|
229
|
+
// aliased one gives no way to hear where it points.
|
|
230
|
+
dom.setAttribute('role', 'link');
|
|
231
|
+
dom.setAttribute('aria-label', ariaLabel(this.getLink()));
|
|
232
|
+
return dom;
|
|
233
|
+
}
|
|
234
|
+
updateDOM(prevNode, dom, config) {
|
|
235
|
+
const recreated = super.updateDOM(prevNode, dom, config);
|
|
236
|
+
if (recreated)
|
|
237
|
+
return true;
|
|
238
|
+
if (prevNode.__target !== this.__target)
|
|
239
|
+
dom.setAttribute('data-wikilink', this.__target);
|
|
240
|
+
if (prevNode.__alias !== this.__alias) {
|
|
241
|
+
if (this.__alias === null)
|
|
242
|
+
dom.removeAttribute('data-wikilink-alias');
|
|
243
|
+
else
|
|
244
|
+
dom.setAttribute('data-wikilink-alias', this.__alias);
|
|
245
|
+
}
|
|
246
|
+
if (prevNode.__target !== this.__target || prevNode.__alias !== this.__alias) {
|
|
247
|
+
dom.setAttribute('aria-label', ariaLabel(this.getLink()));
|
|
248
|
+
}
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
getTarget() {
|
|
252
|
+
return this.getLatest().__target;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Repoint the link. The display text follows only when there is no alias.
|
|
256
|
+
* The value is sanitized (see {@link sanitizeWikiLinkTarget}); a target with
|
|
257
|
+
* nothing representable in it leaves the link untouched rather than producing
|
|
258
|
+
* one that corrupts the document on export.
|
|
259
|
+
*/
|
|
260
|
+
setTarget(target) {
|
|
261
|
+
const clean = sanitizeWikiLinkTarget(target);
|
|
262
|
+
if (clean === null)
|
|
263
|
+
return this.getWritable();
|
|
264
|
+
const writable = this.getWritable();
|
|
265
|
+
writable.__target = clean;
|
|
266
|
+
if (writable.__alias === null)
|
|
267
|
+
writable.setTextContent(clean);
|
|
268
|
+
return writable;
|
|
269
|
+
}
|
|
270
|
+
getAlias() {
|
|
271
|
+
return this.getLatest().__alias;
|
|
272
|
+
}
|
|
273
|
+
/** Set (or clear, with `null`) the alias; the display text follows. */
|
|
274
|
+
setAlias(alias) {
|
|
275
|
+
const normalized = sanitizeWikiLinkAlias(alias);
|
|
276
|
+
const writable = this.getWritable();
|
|
277
|
+
writable.__alias = normalized;
|
|
278
|
+
writable.setTextContent(displayText({ target: writable.__target, alias: normalized }));
|
|
279
|
+
return writable;
|
|
280
|
+
}
|
|
281
|
+
getLink() {
|
|
282
|
+
const self = this.getLatest();
|
|
283
|
+
return { target: self.__target, alias: self.__alias };
|
|
284
|
+
}
|
|
285
|
+
// An atomic token: typing against either edge must produce a sibling text
|
|
286
|
+
// node, never extend the wikilink's text (which would desync it from target).
|
|
287
|
+
canInsertTextBefore() {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
canInsertTextAfter() {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Build a wikilink node. `target`/`alias` are sanitized to values the `[[…]]`
|
|
296
|
+
* syntax can express (see {@link sanitizeWikiLinkTarget}); a target with nothing
|
|
297
|
+
* usable left falls back to the literal text `Page` rather than yielding an
|
|
298
|
+
* invisible token.
|
|
299
|
+
*/
|
|
300
|
+
export function $createWikiLinkNode(target, alias = null) {
|
|
301
|
+
const cleanTarget = sanitizeWikiLinkTarget(target) ?? 'Page';
|
|
302
|
+
return $applyNodeReplacement(new WikiLinkNode(cleanTarget, sanitizeWikiLinkAlias(alias)).setMode('token'));
|
|
303
|
+
}
|
|
304
|
+
export function $isWikiLinkNode(node) {
|
|
305
|
+
return node instanceof WikiLinkNode;
|
|
306
|
+
}
|
|
307
|
+
// ── Transformer ──────────────────────────────────────────────────────────────
|
|
308
|
+
const WIKILINK_TRANSFORMER = {
|
|
309
|
+
dependencies: [WikiLinkNode],
|
|
310
|
+
// The THIRD parameter is `exportFormat`, not the `selection` that
|
|
311
|
+
// `ElementTransformer['export']` takes — see `TextMatchTransformer` in
|
|
312
|
+
// @lexical/markdown's MarkdownTransformers.ts, and the call site in
|
|
313
|
+
// MarkdownExport.ts `$exportChildren`, which supplies
|
|
314
|
+
// `(textNode, textContent) => exportTextFormat(...)`.
|
|
315
|
+
//
|
|
316
|
+
// Ignoring it exports a bold wikilink as a bare one, and — worse — TEARS any
|
|
317
|
+
// surrounding formatted run in two, because `$exportChildren` emits our
|
|
318
|
+
// unformatted string in the middle of it: `a **b [[P]] c** d` came back as
|
|
319
|
+
// `a **b** [[P]] **c** d`.
|
|
320
|
+
export: (node, _exportChildren, exportFormat) => {
|
|
321
|
+
if (!$isWikiLinkNode(node))
|
|
322
|
+
return null;
|
|
323
|
+
const markdown = formatWikiLink(node.getLink());
|
|
324
|
+
// Only route through `exportFormat` when there IS a format to apply.
|
|
325
|
+
// `exportTextFormat` backslash-escapes `*_\`~\\` in its input, which for an
|
|
326
|
+
// unformatted link would rewrite targets containing those characters; the
|
|
327
|
+
// raw string keeps the byte-exact round-trip the plugin guarantees.
|
|
328
|
+
return node.getFormat() === 0 ? markdown : exportFormat(node, markdown);
|
|
329
|
+
},
|
|
330
|
+
importRegExp: WIKILINK_IMPORT_RE,
|
|
331
|
+
regExp: WIKILINK_TYPING_RE,
|
|
332
|
+
trigger: ']',
|
|
333
|
+
replace: (textNode, match) => {
|
|
334
|
+
const link = parseWikiLinkInner(match[1] ?? '');
|
|
335
|
+
if (!link)
|
|
336
|
+
return;
|
|
337
|
+
// `LexicalNode.replace()` does NOT carry `__format`/`__style` onto the
|
|
338
|
+
// replacement (verified in lexical@0.48.0 LexicalNode.ts) and
|
|
339
|
+
// `$createWikiLinkNode` builds a node with format 0, so without this the
|
|
340
|
+
// node silently loses bold/italic/strikethrough/underline — falsifying this
|
|
341
|
+
// module's own stated reason for subclassing TextNode ("it must inherit the
|
|
342
|
+
// surrounding text format").
|
|
343
|
+
const replacement = $createWikiLinkNode(link.target, link.alias);
|
|
344
|
+
replacement.setFormat(textNode.getFormat());
|
|
345
|
+
replacement.setStyle(textNode.getStyle());
|
|
346
|
+
textNode.replace(replacement);
|
|
347
|
+
// Returning nothing (rather than the new node) stops `importTextTransformers`
|
|
348
|
+
// from recursing INTO the wikilink: an alias is literal text, so `[[a|**b**]]`
|
|
349
|
+
// must display `**b**`, not bold `b`.
|
|
350
|
+
},
|
|
351
|
+
type: 'text-match',
|
|
352
|
+
};
|
|
353
|
+
// A wikilink must beat upstream's LINK when both match at the SAME index, in
|
|
354
|
+
// EITHER plugin order. LINK's import pattern is `\[(.+?)\]\(…\)` with a LAZY
|
|
355
|
+
// label, so on a line like `**[[A]]** and [b](url)` it matches starting at the
|
|
356
|
+
// wikilink's own `[`, spanning all the way to the later `](`. Tied start index →
|
|
357
|
+
// resolved by array order → listing `wikilinkPlugin()` after `corePlugin()`
|
|
358
|
+
// handed the span to LINK, which swallowed the `**` delimiters and re-exported
|
|
359
|
+
// them ESCAPED: the bold was silently lost.
|
|
360
|
+
//
|
|
361
|
+
// Declaring the precedence makes the wikilink reading structural rather than a
|
|
362
|
+
// documented ordering requirement a consumer can get wrong. The CommonMark
|
|
363
|
+
// reading of `[[a|b]](c)` is still reachable — omit `wikilinkPlugin()`.
|
|
364
|
+
setTransformerPrecedence(WIKILINK_TRANSFORMER, -10);
|
|
365
|
+
/** Resolve the wikilink a click landed on, if any. Must run in an editor scope. */
|
|
366
|
+
function $wikiLinkFromEvent(event) {
|
|
367
|
+
const target = event.target;
|
|
368
|
+
if (!(target instanceof Node))
|
|
369
|
+
return null;
|
|
370
|
+
const direct = $getNearestNodeFromDOMNode(target);
|
|
371
|
+
if ($isWikiLinkNode(direct))
|
|
372
|
+
return direct;
|
|
373
|
+
// A click can land on the inner text node of a formatted wikilink; climb to
|
|
374
|
+
// the nearest element carrying our marker attribute and map that back.
|
|
375
|
+
const element = target instanceof Element ? target : target.parentElement;
|
|
376
|
+
const marked = element?.closest('[data-wikilink]');
|
|
377
|
+
if (!marked)
|
|
378
|
+
return null;
|
|
379
|
+
const node = $getNearestNodeFromDOMNode(marked);
|
|
380
|
+
return $isWikiLinkNode(node) ? node : null;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The wikilink the caret is on or immediately beside, if any. Must run in an
|
|
384
|
+
* editor scope. A token-mode node is selected as a whole, so the anchor lands
|
|
385
|
+
* either ON it or on an adjacent sibling.
|
|
386
|
+
*/
|
|
387
|
+
function $selectedWikiLink() {
|
|
388
|
+
const selection = $getSelection();
|
|
389
|
+
if (!$isRangeSelection(selection))
|
|
390
|
+
return null;
|
|
391
|
+
const anchor = selection.anchor.getNode();
|
|
392
|
+
if ($isWikiLinkNode(anchor))
|
|
393
|
+
return anchor;
|
|
394
|
+
for (const candidate of [anchor.getPreviousSibling(), anchor.getNextSibling()]) {
|
|
395
|
+
if ($isWikiLinkNode(candidate))
|
|
396
|
+
return candidate;
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
export function wikilinkPlugin(opts = {}) {
|
|
401
|
+
const placeholderTarget = opts.placeholderTarget ?? 'Page';
|
|
402
|
+
const item = {
|
|
403
|
+
id: PLUGIN,
|
|
404
|
+
label: 'Wiki link',
|
|
405
|
+
icon: 'wikilink',
|
|
406
|
+
group: 'inline',
|
|
407
|
+
keywords: ['wiki', 'wikilink', 'backlink', 'reference', '[['],
|
|
408
|
+
run: (editor) => editor.update(() => {
|
|
409
|
+
const selection = $getSelection();
|
|
410
|
+
if (!$isRangeSelection(selection))
|
|
411
|
+
return;
|
|
412
|
+
// A range selection's text can span blocks (newlines) and can contain
|
|
413
|
+
// `|` or `]]` — none of which a `[[…]]` can carry. Sanitizing here, at
|
|
414
|
+
// the boundary where user input enters the document, is what stops the
|
|
415
|
+
// command from minting a link that destroys itself on the next reload.
|
|
416
|
+
const selected = sanitizeWikiLinkTarget(selection.getTextContent());
|
|
417
|
+
// Selected prose becomes the alias of a same-named target, so the
|
|
418
|
+
// sentence reads unchanged and the author only has to fix the target.
|
|
419
|
+
const node = $createWikiLinkNode(selected ?? placeholderTarget, null);
|
|
420
|
+
selection.insertNodes([node]);
|
|
421
|
+
node.selectNext(0, 0);
|
|
422
|
+
}),
|
|
423
|
+
surfaces: ['toolbar', 'floating', 'slash', 'context'],
|
|
424
|
+
};
|
|
425
|
+
return {
|
|
426
|
+
name: PLUGIN,
|
|
427
|
+
nodes: [WikiLinkNode],
|
|
428
|
+
transformers: [WIKILINK_TRANSFORMER],
|
|
429
|
+
items: [item],
|
|
430
|
+
register: (editor, ctx) => {
|
|
431
|
+
const activate = (node) => {
|
|
432
|
+
const { target, alias } = node.getLink();
|
|
433
|
+
ctx.emit({ type: 'plugin', name: PLUGIN, msg: { type: 'activate', target, alias } });
|
|
434
|
+
return true;
|
|
435
|
+
};
|
|
436
|
+
return mergeRegister(editor.registerCommand(CLICK_COMMAND, (event) => {
|
|
437
|
+
const node = $wikiLinkFromEvent(event);
|
|
438
|
+
if (!node)
|
|
439
|
+
return false;
|
|
440
|
+
event.preventDefault();
|
|
441
|
+
return activate(node);
|
|
442
|
+
}, COMMAND_PRIORITY_LOW),
|
|
443
|
+
// Keyboard parity. A wikilink is token-mode text, so the caret can rest
|
|
444
|
+
// on it, but CLICK_COMMAND is unreachable without a pointer — activation
|
|
445
|
+
// was mouse-only. Mod+Enter (rather than bare Enter) keeps the ordinary
|
|
446
|
+
// paragraph-splitting Enter intact while the caret sits beside a link.
|
|
447
|
+
editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
|
|
448
|
+
if (!event || !(event.metaKey || event.ctrlKey))
|
|
449
|
+
return false;
|
|
450
|
+
const node = $selectedWikiLink();
|
|
451
|
+
if (!node)
|
|
452
|
+
return false;
|
|
453
|
+
event.preventDefault();
|
|
454
|
+
return activate(node);
|
|
455
|
+
}, COMMAND_PRIORITY_LOW));
|
|
456
|
+
},
|
|
457
|
+
ui: definePluginUI({
|
|
458
|
+
init: () => ({ last: null }),
|
|
459
|
+
update: (_state, msg) => {
|
|
460
|
+
const link = { target: msg.target, alias: msg.alias };
|
|
461
|
+
return [{ last: link }, [{ type: 'navigate', link }]];
|
|
462
|
+
},
|
|
463
|
+
onEffect: (effect) => opts.onNavigate?.(effect.link),
|
|
464
|
+
}),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
//# sourceMappingURL=wikilink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wikilink.js","sourceRoot":"","sources":["../../src/plugins/wikilink.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,6DAA6D;AAC7D,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,6BAA6B;AAC7B,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,6EAA6E;AAC7E,iEAAiE;AACjE,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,kFAAkF;AAClF,6EAA6E;AAC7E,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,iEAAiE;AACjE,8EAA8E;AAC9E,mFAAmF;AACnF,8EAA8E;AAC9E,8EAA8E;AAC9E,wDAAwD;AACxD,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,0CAA0C;AAC1C,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,iFAAiF;AACjF,2EAA2E;AAC3E,2EAA2E;AAC3E,mEAAmE;AACnE,EAAE;AACF,gFAAgF;AAChF,kFAAkF;AAClF,0EAA0E;AAC1E,0EAA0E;AAC1E,+EAA+E;AAC/E,2DAA2D;AAC3D,yEAAyE;AACzE,iFAAiF;AACjF,8EAA8E;AAC9E,iDAAiD;AACjD,EAAE;AACF,kFAAkF;AAClF,8EAA8E;AAC9E,yEAAyE;AACzE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAE3E,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,GAQT,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAGxC,MAAM,MAAM,GAAG,UAAU,CAAA;AAQzB,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAA,oCAAoC,CAAA;AAEzE,MAAM,kBAAkB,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC,CAAA;AACzD,wEAAwE;AACxE,MAAM,kBAAkB,GAAG,IAAI,MAAM,CAAC,GAAG,kBAAkB,GAAG,CAAC,CAAA;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACvD,uEAAuE;IACvE,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,gCAAgC;IAChC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3C,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACzD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAC7F,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,SAAkB;IACnD,IAAI,GAAG,GAAG,GAAG;QACX,wEAAwE;QACxE,kEAAkE;SACjE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;QACrB,0EAA0E;QAC1E,2EAA2E;SAC1E,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IACzB,IAAI,SAAS;QAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC3C,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACnC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3C,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,qBAAqB,CAAC,GAAkB;IACtD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC7B,4EAA4E;IAC5E,+EAA+E;IAC/E,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACpC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,IAAI,CAAA;AACxF,CAAC;AAED,6EAA6E;AAC7E,SAAS,WAAW,CAAC,IAAc;IACjC,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAA;AAClC,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAc;IAC/B,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI;QACxB,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,aAAa;QAC7B,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,kBAAkB,IAAI,CAAC,MAAM,EAAE,CAAA;AAClD,CAAC;AASD;;;;;GAKG;AACH,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACxC,QAAQ,CAAQ;IAChB,OAAO,CAAe;IAEtB,MAAM,CAAC,OAAO;QACZ,OAAO,UAAU,CAAA;IACnB,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,IAAkB;QAC7B,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IAC/E,CAAC;IAED,YAAY,MAAc,EAAE,KAAoB,EAAE,IAAa,EAAE,GAAa;QAC5E,KAAK,CAAC,IAAI,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;QAClD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,cAAsC;QACtD,OAAO,mBAAmB,CAAC,cAAc,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,cAAc,CACpF,cAAc,CACf,CAAA;IACH,CAAC;IAED,cAAc,CAAC,cAAyD;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QACnC,QAAQ,CAAC,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAA;QACzC,QAAQ,CAAC,OAAO,GAAG,cAAc,CAAC,KAAK,CAAA;QACvC,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,UAAU;QACR,OAAO,EAAE,GAAG,KAAK,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAA;IACpF,CAAC;IAED,SAAS,CAAC,MAAoB,EAAE,MAAsB;QACpD,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC3C,yEAAyE;QACzE,4EAA4E;QAC5E,yEAAyE;QACzE,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAA;QAC7C,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QACrC,GAAG,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAChD,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAAE,GAAG,CAAC,YAAY,CAAC,qBAAqB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAChF,4EAA4E;QAC5E,oDAAoD;QACpD,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QACzD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,SAAS,CAAC,QAAc,EAAE,GAAgB,EAAE,MAAoB;QAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACxD,IAAI,SAAS;YAAE,OAAO,IAAI,CAAA;QAC1B,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;YAAE,GAAG,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QACzF,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;gBAAE,GAAG,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAA;;gBAChE,GAAG,CAAC,YAAY,CAAC,qBAAqB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5D,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7E,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QAC3D,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAA;IAClC,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAc;QACtB,MAAM,KAAK,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAA;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QACnC,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAA;QACzB,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI;YAAE,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;QAC7D,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAA;IACjC,CAAC;IAED,uEAAuE;IACvE,QAAQ,CAAC,KAAoB;QAC3B,MAAM,UAAU,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAA;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QACnC,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAA;QAC7B,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;QACtF,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;QAC7B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAA;IACvD,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,mBAAmB;QACjB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,kBAAkB;QAChB,OAAO,KAAK,CAAA;IACd,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,QAAuB,IAAI;IAC7E,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,MAAM,CAAA;IAC5D,OAAO,qBAAqB,CAC1B,IAAI,YAAY,CAAC,WAAW,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAC7E,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAoC;IAClE,OAAO,IAAI,YAAY,YAAY,CAAA;AACrC,CAAC;AAED,gFAAgF;AAEhF,MAAM,oBAAoB,GAAyB;IACjD,YAAY,EAAE,CAAC,YAAY,CAAC;IAC5B,kEAAkE;IAClE,uEAAuE;IACvE,oEAAoE;IACpE,sDAAsD;IACtD,sDAAsD;IACtD,EAAE;IACF,6EAA6E;IAC7E,wEAAwE;IACxE,2EAA2E;IAC3E,2BAA2B;IAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,YAAY,EAAE,EAAE;QAC9C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACvC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/C,qEAAqE;QACrE,4EAA4E;QAC5E,0EAA0E;QAC1E,oEAAoE;QACpE,OAAO,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACzE,CAAC;IACD,YAAY,EAAE,kBAAkB;IAChC,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,uEAAuE;QACvE,8DAA8D;QAC9D,yEAAyE;QACzE,4EAA4E;QAC5E,4EAA4E;QAC5E,6BAA6B;QAC7B,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAChE,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;QAC3C,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;QACzC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC7B,8EAA8E;QAC9E,+EAA+E;QAC/E,sCAAsC;IACxC,CAAC;IACD,IAAI,EAAE,YAAY;CACnB,CAAA;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,iFAAiF;AACjF,4EAA4E;AAC5E,+EAA+E;AAC/E,4CAA4C;AAC5C,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,wEAAwE;AACxE,wBAAwB,CAAC,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAA;AAanD,mFAAmF;AACnF,SAAS,kBAAkB,CAAC,KAAiB;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,IAAI,CAAC,CAAC,MAAM,YAAY,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC1C,MAAM,MAAM,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAA;IACjD,IAAI,eAAe,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IAC1C,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,OAAO,GAAG,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAA;IACzE,MAAM,MAAM,GAAG,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,MAAM,IAAI,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAA;IAC/C,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5C,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB;IACxB,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;IACjC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAA;IACzC,IAAI,eAAe,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IAC1C,KAAK,MAAM,SAAS,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC;QAC/E,IAAI,eAAe,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAA;IAClD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAiBD,MAAM,UAAU,cAAc,CAAC,OAA8B,EAAE;IAC7D,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,MAAM,CAAA;IAE1D,MAAM,IAAI,GAAgB;QACxB,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC;QAC7D,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CACd,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;YACjB,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;YACjC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC;gBAAE,OAAM;YACzC,sEAAsE;YACtE,uEAAuE;YACvE,uEAAuE;YACvE,uEAAuE;YACvE,MAAM,QAAQ,GAAG,sBAAsB,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC,CAAA;YACnE,kEAAkE;YAClE,sEAAsE;YACtE,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,IAAI,iBAAiB,EAAE,IAAI,CAAC,CAAA;YACrE,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;YAC7B,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACvB,CAAC,CAAC;QACJ,QAAQ,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC;KACtD,CAAA;IAED,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,CAAC,YAAY,CAAC;QACrB,YAAY,EAAE,CAAC,oBAAoB,CAAC;QACpC,KAAK,EAAE,CAAC,IAAI,CAAC;QACb,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YACxB,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAQ,EAAE;gBAC5C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;gBACxC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;gBACpF,OAAO,IAAI,CAAA;YACb,CAAC,CAAA;YACD,OAAO,aAAa,CAClB,MAAM,CAAC,eAAe,CACpB,aAAa,EACb,CAAC,KAAiB,EAAE,EAAE;gBACpB,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;gBACtC,IAAI,CAAC,IAAI;oBAAE,OAAO,KAAK,CAAA;gBACvB,KAAK,CAAC,cAAc,EAAE,CAAA;gBACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;YACvB,CAAC,EACD,oBAAoB,CACrB;YACD,wEAAwE;YACxE,yEAAyE;YACzE,wEAAwE;YACxE,uEAAuE;YACvE,MAAM,CAAC,eAAe,CACpB,iBAAiB,EACjB,CAAC,KAA2B,EAAE,EAAE;gBAC9B,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC;oBAAE,OAAO,KAAK,CAAA;gBAC7D,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;gBAChC,IAAI,CAAC,IAAI;oBAAE,OAAO,KAAK,CAAA;gBACvB,KAAK,CAAC,cAAc,EAAE,CAAA;gBACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;YACvB,CAAC,EACD,oBAAoB,CACrB,CACF,CAAA;QACH,CAAC;QACD,EAAE,EAAE,cAAc,CAA6C;YAC7D,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;gBACtB,MAAM,IAAI,GAAa,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAA;gBAC/D,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YACvD,CAAC;YACD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;SACrD,CAAC;KACH,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* Block drag-and-drop reordering (`blockDragPlugin`).
|
|
2
|
+
*
|
|
3
|
+
* Geometry is set inline by the plugin (measured, therefore dynamic); everything
|
|
4
|
+
* here is appearance only. Import alongside `editor.css`:
|
|
5
|
+
* import '@llui/markdown-editor/styles/block-drag.css'
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
[data-scope='md-block-drag'][data-part='handle'] {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-scope='md-block-drag'][data-part='grip'] {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
width: 22px;
|
|
18
|
+
height: 22px;
|
|
19
|
+
padding: 0;
|
|
20
|
+
border: 0;
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
background: transparent;
|
|
23
|
+
color: var(--md-muted, #9aa0a6);
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
line-height: 1;
|
|
26
|
+
cursor: grab;
|
|
27
|
+
opacity: 0.7;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[data-scope='md-block-drag'][data-part='grip']:hover {
|
|
31
|
+
background: var(--md-hover, rgba(127, 127, 127, 0.14));
|
|
32
|
+
opacity: 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Keyboard users must be able to see the grip they have focused. */
|
|
36
|
+
[data-scope='md-block-drag'][data-part='grip']:focus-visible {
|
|
37
|
+
outline: 2px solid var(--md-accent, #3b82f6);
|
|
38
|
+
outline-offset: 1px;
|
|
39
|
+
opacity: 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Grab mode is a live, invisible-by-default mode — give it a strong affordance. */
|
|
43
|
+
[data-scope='md-block-drag'][data-part='grip'][data-grabbed] {
|
|
44
|
+
background: var(--md-accent, #3b82f6);
|
|
45
|
+
color: #fff;
|
|
46
|
+
cursor: grabbing;
|
|
47
|
+
opacity: 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-scope='md-block-drag'][data-part='indicator'] {
|
|
51
|
+
height: 2px;
|
|
52
|
+
border-radius: 1px;
|
|
53
|
+
background: var(--md-accent, #3b82f6);
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
58
|
+
[data-scope='md-block-drag'][data-part='grip'] {
|
|
59
|
+
transition:
|
|
60
|
+
opacity 120ms ease,
|
|
61
|
+
background-color 120ms ease;
|
|
62
|
+
}
|
|
63
|
+
}
|