@haklex/rich-editor 0.1.1 → 0.3.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.
Files changed (65) hide show
  1. package/dist/AlertQuoteEditNode-C55sxsR3.js +267 -0
  2. package/dist/KaTeXRenderer-CQQT3BMw.js +215 -0
  3. package/dist/LinkCardRenderer-CigqFwCv.js +45 -0
  4. package/dist/MermaidPlugin-BrOr-wQi.js +67 -0
  5. package/dist/RubyRenderer-jOkydJHg.js +15 -0
  6. package/dist/SubmitShortcutPlugin-DhyVFzoj.js +2186 -0
  7. package/dist/commands-entry.mjs +54 -74
  8. package/dist/components/decorators/PollEditDecorator.d.ts +13 -0
  9. package/dist/components/decorators/PollEditDecorator.d.ts.map +1 -0
  10. package/dist/components/renderers/PollRenderer.d.ts +3 -0
  11. package/dist/components/renderers/PollRenderer.d.ts.map +1 -0
  12. package/dist/config-B5BuLljq.js +1633 -0
  13. package/dist/config-edit.d.ts.map +1 -1
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/context/PollDataContext.d.ts +11 -0
  16. package/dist/context/PollDataContext.d.ts.map +1 -0
  17. package/dist/extractPolls-DO31LNrp.js +116 -0
  18. package/dist/grid.css-CJCkLTZc.js +44 -0
  19. package/dist/index.d.ts +5 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.mjs +121 -180
  22. package/dist/katex.css-CIOEOXyd.js +145 -0
  23. package/dist/node-registry-Dz5OTkh4.js +946 -0
  24. package/dist/nodes/PollEditNode.d.ts +14 -0
  25. package/dist/nodes/PollEditNode.d.ts.map +1 -0
  26. package/dist/nodes/PollNode.d.ts +52 -0
  27. package/dist/nodes/PollNode.d.ts.map +1 -0
  28. package/dist/nodes-entry.d.ts +3 -0
  29. package/dist/nodes-entry.d.ts.map +1 -1
  30. package/dist/nodes-entry.mjs +5 -50
  31. package/dist/normalizeSerializedEditorState-B-1wmGzd.js +78 -0
  32. package/dist/plugins-entry.mjs +3 -28
  33. package/dist/renderers-entry.mjs +41 -61
  34. package/dist/rich-editor.css +2 -1
  35. package/dist/static-entry.d.ts +5 -0
  36. package/dist/static-entry.d.ts.map +1 -1
  37. package/dist/static-entry.mjs +16 -66
  38. package/dist/styles/index.d.ts +2 -0
  39. package/dist/styles/index.d.ts.map +1 -1
  40. package/dist/styles/poll-edit.css.d.ts +35 -0
  41. package/dist/styles/poll-edit.css.d.ts.map +1 -0
  42. package/dist/styles/poll.css.d.ts +43 -0
  43. package/dist/styles/poll.css.d.ts.map +1 -0
  44. package/dist/styles-entry.mjs +3 -21
  45. package/dist/theme-B5B2EOWM.js +1099 -0
  46. package/dist/types/poll.d.ts +36 -0
  47. package/dist/types/poll.d.ts.map +1 -0
  48. package/dist/types/renderer-config.d.ts +3 -0
  49. package/dist/types/renderer-config.d.ts.map +1 -1
  50. package/dist/utils/extractPolls.d.ts +4 -0
  51. package/dist/utils/extractPolls.d.ts.map +1 -0
  52. package/package.json +30 -30
  53. package/dist/AlertQuoteEditNode-sPNf3_7P.js +0 -293
  54. package/dist/KaTeXRenderer-CQyQzNTJ.js +0 -218
  55. package/dist/LinkCardRenderer-QmkOlyXb.js +0 -36
  56. package/dist/MermaidPlugin-DKuGUcCG.js +0 -101
  57. package/dist/PresentDialogContext-DRroMIoK.js +0 -71
  58. package/dist/RubyRenderer-CJQmODir.js +0 -14
  59. package/dist/SubmitShortcutPlugin-D9uKYHda.js +0 -2427
  60. package/dist/config-Dl3ZkytB.js +0 -1362
  61. package/dist/grid.css-Md5-Cfx_.js +0 -11
  62. package/dist/katex.css-Csc-7N7u.js +0 -28
  63. package/dist/node-registry-CovhHUB6.js +0 -824
  64. package/dist/normalizeSerializedEditorState-k5G4xSi9.js +0 -85
  65. package/dist/theme-lEwScxEX.js +0 -1113
@@ -0,0 +1,1633 @@
1
+ import { A as AlertQuoteNode, C as ImageNode, E as FootnoteNode, F as useNestedContentRenderer, M as extractTextContent, _ as KaTeXInlineNode, b as KaTeXBlockNode, c as SpoilerNode, d as MermaidNode, i as TagNode, j as _defineProperty, m as MentionNode } from "./theme-B5B2EOWM.js";
2
+ import { l as RendererWrapper, m as useVariant, s as useFootnoteDefinitions, u as createRendererDecoration } from "./KaTeXRenderer-CQQT3BMw.js";
3
+ import { l as semanticClassNames, r as clsx, u as sharedStyles } from "./katex.css-CIOEOXyd.js";
4
+ import { i as detailsStyles, n as gridStyles, r as detailsClassNames, t as gridClassNames } from "./grid.css-CJCkLTZc.js";
5
+ import { t as LinkCardRenderer } from "./LinkCardRenderer-CigqFwCv.js";
6
+ import { AutoLinkNode, LinkNode } from "@lexical/link";
7
+ import { createContext, createElement, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
8
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
9
+ import { $createTextNode, $insertNodes, DecoratorNode, ElementNode, TextNode } from "lexical";
10
+ import { HorizontalRuleNode } from "@lexical/extension";
11
+ import { CodeNode } from "@lexical/code-core";
12
+ import { ListItemNode, ListNode } from "@lexical/list";
13
+ import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ import { ChevronRight, Code, Link, MessageSquareQuote, Video } from "lucide-react";
16
+ import { customAlphabet } from "nanoid";
17
+ //#region src/components/renderers/BannerRenderer.tsx
18
+ var BannerRenderer = ({ type }) => {
19
+ return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
20
+ };
21
+ //#endregion
22
+ //#region src/components/renderers/BannerStaticDecorator.tsx
23
+ function BannerStaticDecorator({ bannerType, contentState }) {
24
+ const renderContent = useNestedContentRenderer();
25
+ return /* @__PURE__ */ jsxs("div", {
26
+ className: "rich-banner-inner",
27
+ children: [/* @__PURE__ */ jsx(RendererWrapper, {
28
+ defaultRenderer: BannerRenderer,
29
+ props: { type: bannerType },
30
+ rendererKey: "Banner"
31
+ }), /* @__PURE__ */ jsx("div", {
32
+ className: "rich-banner-content",
33
+ children: renderContent(contentState)
34
+ })]
35
+ });
36
+ }
37
+ //#endregion
38
+ //#region src/nodes/BannerNode.ts
39
+ var LEGACY_TYPE_MAP = {
40
+ info: "note",
41
+ success: "tip",
42
+ error: "caution"
43
+ };
44
+ function normalizeBannerType(type) {
45
+ if (type in LEGACY_TYPE_MAP) return LEGACY_TYPE_MAP[type];
46
+ return type || "note";
47
+ }
48
+ var BANNER_TYPES = [
49
+ "note",
50
+ "tip",
51
+ "important",
52
+ "warning",
53
+ "caution"
54
+ ];
55
+ var BANNER_LABELS = {
56
+ note: "Note",
57
+ tip: "Tip",
58
+ important: "Important",
59
+ warning: "Warning",
60
+ caution: "Caution"
61
+ };
62
+ var BannerNode = class BannerNode extends DecoratorNode {
63
+ static getType() {
64
+ return "banner";
65
+ }
66
+ static clone(node) {
67
+ return new BannerNode(node.__bannerType, node.__contentState, node.__key);
68
+ }
69
+ constructor(bannerType, contentState, key) {
70
+ super(key);
71
+ _defineProperty(this, "__bannerType", void 0);
72
+ _defineProperty(this, "__contentState", void 0);
73
+ this.__bannerType = bannerType;
74
+ this.__contentState = contentState || { root: {
75
+ children: [{
76
+ type: "paragraph",
77
+ children: [],
78
+ direction: null,
79
+ format: "",
80
+ indent: 0,
81
+ textFormat: 0,
82
+ textStyle: "",
83
+ version: 1
84
+ }],
85
+ direction: null,
86
+ format: "",
87
+ indent: 0,
88
+ type: "root",
89
+ version: 1
90
+ } };
91
+ }
92
+ createDOM(_config) {
93
+ const div = document.createElement("div");
94
+ div.className = `rich-banner rich-banner-${this.__bannerType}`;
95
+ return div;
96
+ }
97
+ updateDOM(prevNode, dom) {
98
+ if (prevNode.__bannerType !== this.__bannerType) dom.className = `rich-banner rich-banner-${this.__bannerType}`;
99
+ return false;
100
+ }
101
+ isInline() {
102
+ return false;
103
+ }
104
+ getBannerType() {
105
+ return this.__bannerType;
106
+ }
107
+ setBannerType(bannerType) {
108
+ const writable = this.getWritable();
109
+ writable.__bannerType = bannerType;
110
+ }
111
+ getContentState() {
112
+ return this.getLatest().__contentState;
113
+ }
114
+ setContentState(state) {
115
+ const writable = this.getWritable();
116
+ writable.__contentState = state;
117
+ }
118
+ getTextContent() {
119
+ return extractTextContent(this.__contentState);
120
+ }
121
+ static importJSON(serializedNode) {
122
+ const legacy = serializedNode;
123
+ const bannerType = normalizeBannerType(serializedNode.bannerType);
124
+ if (serializedNode.content) return new BannerNode(bannerType, serializedNode.content);
125
+ if (legacy.children) return new BannerNode(bannerType, { root: {
126
+ children: legacy.children,
127
+ direction: null,
128
+ format: "",
129
+ indent: 0,
130
+ type: "root",
131
+ version: 1
132
+ } });
133
+ return new BannerNode(bannerType);
134
+ }
135
+ exportJSON() {
136
+ return {
137
+ ...super.exportJSON(),
138
+ type: "banner",
139
+ bannerType: this.__bannerType,
140
+ content: this.__contentState,
141
+ version: 1
142
+ };
143
+ }
144
+ decorate(_editor, _config) {
145
+ return createElement(BannerStaticDecorator, {
146
+ bannerType: this.__bannerType,
147
+ contentState: this.__contentState
148
+ });
149
+ }
150
+ };
151
+ function $isBannerNode(node) {
152
+ return node instanceof BannerNode;
153
+ }
154
+ //#endregion
155
+ //#region src/context/ColorSchemeContext.tsx
156
+ var ColorSchemeContext = createContext("light");
157
+ function ColorSchemeProvider({ colorScheme, children }) {
158
+ return /* @__PURE__ */ jsx(ColorSchemeContext.Provider, {
159
+ value: colorScheme,
160
+ children
161
+ });
162
+ }
163
+ function useColorScheme() {
164
+ return use(ColorSchemeContext);
165
+ }
166
+ //#endregion
167
+ //#region src/utils/shiki.ts
168
+ var codeToHtmlFn = null;
169
+ var shikiLoadPromise = null;
170
+ function loadCodeToHtml() {
171
+ if (codeToHtmlFn) return Promise.resolve(codeToHtmlFn);
172
+ if (!shikiLoadPromise) shikiLoadPromise = import("shiki/bundle/web").then((mod) => {
173
+ codeToHtmlFn = mod.codeToHtml;
174
+ return mod.codeToHtml;
175
+ }).catch((err) => {
176
+ shikiLoadPromise = null;
177
+ throw err;
178
+ });
179
+ return shikiLoadPromise;
180
+ }
181
+ //#endregion
182
+ //#region src/components/renderers/CodeBlockRenderer.tsx
183
+ function CodeBlockRenderer({ code, language, showLineNumbers: showLineNumbersProp }) {
184
+ const variant = useVariant();
185
+ const showLineNumbers = showLineNumbersProp ?? variant !== "comment";
186
+ const shikiTheme = useColorScheme() === "dark" ? "github-dark" : "github-light";
187
+ const [highlightedHtml, setHighlightedHtml] = useState(null);
188
+ const [copied, setCopied] = useState(false);
189
+ const copyTimerRef = useRef(void 0);
190
+ useEffect(() => {
191
+ let cancelled = false;
192
+ setHighlightedHtml(null);
193
+ loadCodeToHtml().then((toHtml) => toHtml(code, {
194
+ lang: language,
195
+ theme: shikiTheme
196
+ })).then((html) => {
197
+ if (!cancelled) setHighlightedHtml(html);
198
+ }).catch(() => {
199
+ if (!cancelled) setHighlightedHtml(null);
200
+ });
201
+ return () => {
202
+ cancelled = true;
203
+ };
204
+ }, [
205
+ code,
206
+ language,
207
+ shikiTheme
208
+ ]);
209
+ useEffect(() => {
210
+ return () => clearTimeout(copyTimerRef.current);
211
+ }, []);
212
+ const handleCopy = useCallback(() => {
213
+ navigator.clipboard.writeText(code).then(() => {
214
+ setCopied(true);
215
+ clearTimeout(copyTimerRef.current);
216
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
217
+ }).catch(() => {});
218
+ }, [code]);
219
+ const header = language ? /* @__PURE__ */ jsxs("div", {
220
+ className: "rich-code-block-header",
221
+ children: [/* @__PURE__ */ jsx("span", {
222
+ className: "rich-code-block-lang",
223
+ children: language
224
+ }), /* @__PURE__ */ jsx("button", {
225
+ "aria-label": copied ? "Copied to clipboard" : "Copy code",
226
+ className: "rich-code-block-copy",
227
+ type: "button",
228
+ onClick: handleCopy,
229
+ children: copied ? "Copied" : "Copy"
230
+ })]
231
+ }) : null;
232
+ const wrapperClass = showLineNumbers ? "rich-code-block rich-code-block-numbered" : "rich-code-block";
233
+ if (highlightedHtml) return /* @__PURE__ */ jsxs("div", {
234
+ className: wrapperClass,
235
+ children: [header, /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } })]
236
+ });
237
+ const lines = code.split("\n");
238
+ return /* @__PURE__ */ jsxs("div", {
239
+ className: wrapperClass,
240
+ children: [header, /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("span", {
241
+ className: "line",
242
+ children: [line, i < lines.length - 1 ? "\n" : ""]
243
+ }, i)) }) })]
244
+ });
245
+ }
246
+ //#endregion
247
+ //#region src/nodes/CodeBlockNode.ts
248
+ var CodeBlockNode = class CodeBlockNode extends DecoratorNode {
249
+ static getType() {
250
+ return "code-block";
251
+ }
252
+ static clone(node) {
253
+ return new CodeBlockNode(node.__code, node.__language, node.__key);
254
+ }
255
+ constructor(code, language, key) {
256
+ super(key);
257
+ _defineProperty(this, "__code", void 0);
258
+ _defineProperty(this, "__language", void 0);
259
+ this.__code = code;
260
+ this.__language = language;
261
+ }
262
+ createDOM(_config) {
263
+ const div = document.createElement("div");
264
+ div.className = "rich-code-block-wrapper";
265
+ return div;
266
+ }
267
+ updateDOM() {
268
+ return false;
269
+ }
270
+ isInline() {
271
+ return false;
272
+ }
273
+ isKeyboardSelectable() {
274
+ return true;
275
+ }
276
+ static importJSON(serializedNode) {
277
+ return $createCodeBlockNode(serializedNode.code, serializedNode.language);
278
+ }
279
+ exportJSON() {
280
+ return {
281
+ ...super.exportJSON(),
282
+ type: "code-block",
283
+ code: this.__code,
284
+ language: this.__language,
285
+ version: 1
286
+ };
287
+ }
288
+ getCode() {
289
+ return this.__code;
290
+ }
291
+ setCode(code) {
292
+ const writable = this.getWritable();
293
+ writable.__code = code;
294
+ }
295
+ getLanguage() {
296
+ return this.__language;
297
+ }
298
+ setLanguage(language) {
299
+ const writable = this.getWritable();
300
+ writable.__language = language;
301
+ }
302
+ decorate(_editor, _config) {
303
+ return createRendererDecoration("CodeBlock", CodeBlockRenderer, {
304
+ code: this.__code,
305
+ language: this.__language
306
+ });
307
+ }
308
+ };
309
+ _defineProperty(CodeBlockNode, "commandItems", [{
310
+ title: "Code Block",
311
+ icon: createElement(Code, { size: 20 }),
312
+ description: "Syntax-highlighted code",
313
+ keywords: [
314
+ "code",
315
+ "snippet",
316
+ "codeblock"
317
+ ],
318
+ section: "MEDIA",
319
+ placement: ["slash", "toolbar"],
320
+ group: "insert",
321
+ onSelect: (editor) => {
322
+ editor.update(() => {
323
+ $insertNodes([$createCodeBlockNode("", "text")]);
324
+ });
325
+ }
326
+ }]);
327
+ function $createCodeBlockNode(code, language) {
328
+ return new CodeBlockNode(code, language);
329
+ }
330
+ function $isCodeBlockNode(node) {
331
+ return node instanceof CodeBlockNode;
332
+ }
333
+ //#endregion
334
+ //#region src/nodes/CommentNode.ts
335
+ var DEFAULT_COMMENT_TEXT = "comment";
336
+ var CommentNode = class CommentNode extends TextNode {
337
+ static getType() {
338
+ return "comment";
339
+ }
340
+ static clone(node) {
341
+ return new CommentNode(node.__text, node.__key);
342
+ }
343
+ static importDOM() {
344
+ return {
345
+ "#comment": () => ({
346
+ conversion: (domNode) => {
347
+ if (!(domNode instanceof Comment)) return null;
348
+ return { node: $createCommentNode(domNode.data) };
349
+ },
350
+ priority: 4
351
+ }),
352
+ "span": () => ({
353
+ conversion: (domNode) => {
354
+ if (!(domNode instanceof HTMLElement)) return null;
355
+ if (!domNode.classList.contains(semanticClassNames.comment)) return null;
356
+ return { node: $createCommentNode(domNode.dataset.comment ?? domNode.textContent ?? "") };
357
+ },
358
+ priority: 2
359
+ })
360
+ };
361
+ }
362
+ constructor(text, key) {
363
+ super(text, key);
364
+ }
365
+ createDOM(config) {
366
+ const element = super.createDOM(config);
367
+ element.classList.add(semanticClassNames.comment, sharedStyles.comment);
368
+ element.dataset.comment = this.__text;
369
+ return element;
370
+ }
371
+ updateDOM(prevNode, dom, config) {
372
+ const updated = super.updateDOM(prevNode, dom, config);
373
+ dom.classList.add(semanticClassNames.comment, sharedStyles.comment);
374
+ if (prevNode.__text !== this.__text) dom.dataset.comment = this.__text;
375
+ return updated;
376
+ }
377
+ exportDOM(_editor) {
378
+ return { element: document.createComment(this.getTextContent()) };
379
+ }
380
+ static importJSON(serializedNode) {
381
+ const node = $createCommentNode(serializedNode.text ?? "");
382
+ node.setFormat(serializedNode.format ?? 0);
383
+ node.setDetail(serializedNode.detail ?? 0);
384
+ node.setMode(serializedNode.mode ?? "normal");
385
+ node.setStyle(serializedNode.style ?? "");
386
+ return node;
387
+ }
388
+ exportJSON() {
389
+ return {
390
+ ...super.exportJSON(),
391
+ type: "comment",
392
+ version: 1
393
+ };
394
+ }
395
+ };
396
+ _defineProperty(CommentNode, "commandItems", [{
397
+ title: "HTML Comment",
398
+ icon: createElement(MessageSquareQuote, { size: 20 }),
399
+ description: "Insert an HTML comment node",
400
+ keywords: [
401
+ "comment",
402
+ "html",
403
+ "annotation",
404
+ "hidden",
405
+ "<!-- -->"
406
+ ],
407
+ section: "INLINE",
408
+ placement: ["slash", "toolbar"],
409
+ group: "insert",
410
+ onSelect: (editor, _queryString) => {
411
+ editor.update(() => {
412
+ const node = $createCommentNode(DEFAULT_COMMENT_TEXT);
413
+ $insertNodes([node]);
414
+ node.select(0, node.getTextContentSize());
415
+ });
416
+ }
417
+ }]);
418
+ function $createCommentNode(text) {
419
+ return new CommentNode(text);
420
+ }
421
+ function $createCommentPlaceholderNode() {
422
+ return $createCommentNode(DEFAULT_COMMENT_TEXT);
423
+ }
424
+ function $isCommentNode(node) {
425
+ return node instanceof CommentNode;
426
+ }
427
+ //#endregion
428
+ //#region src/utils/lucide-dom.ts
429
+ var SVG_NS = "http://www.w3.org/2000/svg";
430
+ var DEFAULT_ATTRS = {
431
+ xmlns: SVG_NS,
432
+ width: "24",
433
+ height: "24",
434
+ viewBox: "0 0 24 24",
435
+ fill: "none",
436
+ stroke: "currentColor",
437
+ "stroke-width": "2",
438
+ "stroke-linecap": "round",
439
+ "stroke-linejoin": "round"
440
+ };
441
+ function createLucideSvg(iconNode, attrs = {}) {
442
+ const svg = document.createElementNS(SVG_NS, "svg");
443
+ const merged = {
444
+ ...DEFAULT_ATTRS,
445
+ ...attrs
446
+ };
447
+ for (const [k, v] of Object.entries(merged)) svg.setAttribute(k, v);
448
+ for (const [tag, elAttrs] of iconNode) {
449
+ const el = document.createElementNS(SVG_NS, tag);
450
+ for (const [k, v] of Object.entries(elAttrs)) {
451
+ if (k === "key") continue;
452
+ el.setAttribute(k, v);
453
+ }
454
+ svg.append(el);
455
+ }
456
+ return svg;
457
+ }
458
+ //#endregion
459
+ //#region src/nodes/DetailsNode.ts
460
+ var ChevronRightIconNode = [["path", { d: "M8 6L12 10L8 14" }]];
461
+ var DetailsNode = class DetailsNode extends ElementNode {
462
+ static getType() {
463
+ return "details";
464
+ }
465
+ static clone(node) {
466
+ return new DetailsNode(node.__summary, node.__open, node.__key);
467
+ }
468
+ constructor(summary, open = false, key) {
469
+ super(key);
470
+ _defineProperty(this, "__summary", void 0);
471
+ _defineProperty(this, "__open", void 0);
472
+ this.__summary = summary;
473
+ this.__open = open;
474
+ }
475
+ createDOM(_config) {
476
+ const details = document.createElement("details");
477
+ details.className = `${detailsClassNames.details} ${detailsStyles.details}`;
478
+ if (this.__open) details.open = true;
479
+ const summary = document.createElement("summary");
480
+ summary.className = `${detailsClassNames.summary} ${detailsStyles.summary}`;
481
+ const chevron = document.createElement("span");
482
+ chevron.className = `${detailsClassNames.chevron} ${detailsStyles.chevron}`;
483
+ chevron.setAttribute("aria-hidden", "true");
484
+ chevron.append(createLucideSvg(ChevronRightIconNode, {
485
+ "width": "20",
486
+ "height": "20",
487
+ "viewBox": "0 0 20 20",
488
+ "stroke-width": "1.5"
489
+ }));
490
+ summary.append(chevron);
491
+ const label = document.createElement("span");
492
+ label.className = `${detailsClassNames.summaryText} ${detailsStyles.summaryText}`;
493
+ label.textContent = this.__summary;
494
+ summary.append(label);
495
+ const content = document.createElement("div");
496
+ content.className = `${detailsClassNames.content} ${detailsStyles.content}`;
497
+ details.append(summary, content);
498
+ return details;
499
+ }
500
+ updateDOM(prevNode, dom) {
501
+ const details = dom;
502
+ if (prevNode.__open !== this.__open) details.open = this.__open;
503
+ if (prevNode.__summary !== this.__summary) {
504
+ const label = dom.querySelector(`.${detailsClassNames.summaryText}`);
505
+ if (label) label.textContent = this.__summary;
506
+ }
507
+ return false;
508
+ }
509
+ static importJSON(serializedNode) {
510
+ return $createDetailsNode(serializedNode.summary, serializedNode.open);
511
+ }
512
+ exportJSON() {
513
+ return {
514
+ ...super.exportJSON(),
515
+ type: "details",
516
+ summary: this.__summary,
517
+ open: this.__open,
518
+ version: 1
519
+ };
520
+ }
521
+ getSummary() {
522
+ return this.getLatest().__summary;
523
+ }
524
+ setSummary(summary) {
525
+ const writable = this.getWritable();
526
+ writable.__summary = summary;
527
+ }
528
+ getOpen() {
529
+ return this.getLatest().__open;
530
+ }
531
+ setOpen(open) {
532
+ const writable = this.getWritable();
533
+ writable.__open = open;
534
+ }
535
+ toggleOpen() {
536
+ this.setOpen(!this.getOpen());
537
+ }
538
+ getDOMSlot(element) {
539
+ const content = element.querySelector(`.${detailsClassNames.content}`);
540
+ return super.getDOMSlot(element).withElement(content);
541
+ }
542
+ isInline() {
543
+ return false;
544
+ }
545
+ };
546
+ _defineProperty(DetailsNode, "slashMenuItems", [{
547
+ title: "Details",
548
+ icon: createElement(ChevronRight, { size: 20 }),
549
+ description: "Collapsible content block",
550
+ keywords: [
551
+ "details",
552
+ "toggle",
553
+ "collapse",
554
+ "accordion"
555
+ ],
556
+ section: "ADVANCED",
557
+ onSelect: (editor) => {
558
+ editor.update(() => {
559
+ $insertNodes([$createDetailsNode("Details")]);
560
+ });
561
+ }
562
+ }]);
563
+ function $createDetailsNode(summary, open = false) {
564
+ return new DetailsNode(summary, open);
565
+ }
566
+ //#endregion
567
+ //#region src/components/renderers/FootnoteSectionRenderer.tsx
568
+ function FootnoteSectionRenderer({ definitions }) {
569
+ const { displayNumberMap } = useFootnoteDefinitions();
570
+ const sortedEntries = Object.entries(definitions).sort(([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0));
571
+ if (sortedEntries.length === 0) return null;
572
+ return /* @__PURE__ */ jsxs("div", {
573
+ role: "doc-endnotes",
574
+ className: clsx("rich-footnote-section-content", semanticClassNames.footnoteSection, sharedStyles.footnoteSection),
575
+ children: [/* @__PURE__ */ jsx("hr", { className: clsx(semanticClassNames.footnoteSectionDivider, sharedStyles.footnoteSectionDivider) }), /* @__PURE__ */ jsx("ol", {
576
+ className: clsx(semanticClassNames.footnoteSectionList, sharedStyles.footnoteSectionList),
577
+ children: sortedEntries.map(([identifier, content]) => {
578
+ return /* @__PURE__ */ jsx(FootnoteSectionItem, {
579
+ content,
580
+ displayNum: displayNumberMap[identifier] ?? identifier,
581
+ identifier
582
+ }, identifier);
583
+ })
584
+ })]
585
+ });
586
+ }
587
+ function FootnoteSectionItem({ identifier, content, displayNum }) {
588
+ const targetId = `footnote-${identifier}`;
589
+ const refId = `footnote-ref-${identifier}`;
590
+ const handleBackClick = useCallback((e) => {
591
+ e.preventDefault();
592
+ const refElement = document.getElementById(refId);
593
+ if (!refElement) return;
594
+ refElement.scrollIntoView({
595
+ behavior: "smooth",
596
+ block: "center"
597
+ });
598
+ refElement.classList.add(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
599
+ window.setTimeout(() => {
600
+ refElement.classList.remove(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
601
+ }, 1200);
602
+ }, [refId]);
603
+ return /* @__PURE__ */ jsxs("li", {
604
+ className: clsx(semanticClassNames.footnoteSectionItem, sharedStyles.footnoteSectionItem),
605
+ id: targetId,
606
+ value: typeof displayNum === "number" ? displayNum : void 0,
607
+ children: [/* @__PURE__ */ jsx("span", {
608
+ className: "rich-footnote-section-item-content",
609
+ children: content
610
+ }), /* @__PURE__ */ jsx("a", {
611
+ "aria-label": `Back to reference ${displayNum}`,
612
+ className: clsx(semanticClassNames.footnoteBackRef, sharedStyles.footnoteBackRef),
613
+ href: `#${refId}`,
614
+ role: "doc-backlink",
615
+ onClick: handleBackClick,
616
+ children: "↩"
617
+ })]
618
+ });
619
+ }
620
+ //#endregion
621
+ //#region src/nodes/FootnoteSectionNode.ts
622
+ var FootnoteSectionNode = class FootnoteSectionNode extends DecoratorNode {
623
+ static getType() {
624
+ return "footnote-section";
625
+ }
626
+ static clone(node) {
627
+ return new FootnoteSectionNode({ ...node.__definitions }, node.__key);
628
+ }
629
+ constructor(definitions, key) {
630
+ super(key);
631
+ _defineProperty(this, "__definitions", void 0);
632
+ this.__definitions = definitions;
633
+ }
634
+ createDOM(_config) {
635
+ const div = document.createElement("div");
636
+ div.className = `${semanticClassNames.footnoteSection} ${sharedStyles.footnoteSection}`;
637
+ return div;
638
+ }
639
+ updateDOM() {
640
+ return false;
641
+ }
642
+ isInline() {
643
+ return false;
644
+ }
645
+ static importJSON(serializedNode) {
646
+ return $createFootnoteSectionNode(serializedNode.definitions);
647
+ }
648
+ exportJSON() {
649
+ return {
650
+ ...super.exportJSON(),
651
+ type: "footnote-section",
652
+ definitions: this.__definitions,
653
+ version: 1
654
+ };
655
+ }
656
+ getDefinitions() {
657
+ return this.getLatest().__definitions;
658
+ }
659
+ setDefinitions(definitions) {
660
+ const writable = this.getWritable();
661
+ writable.__definitions = definitions;
662
+ }
663
+ getDefinition(identifier) {
664
+ return this.getLatest().__definitions[identifier];
665
+ }
666
+ setDefinition(identifier, content) {
667
+ const writable = this.getWritable();
668
+ writable.__definitions = {
669
+ ...writable.__definitions,
670
+ [identifier]: content
671
+ };
672
+ }
673
+ removeDefinition(identifier) {
674
+ const writable = this.getWritable();
675
+ const { [identifier]: _, ...rest } = writable.__definitions;
676
+ writable.__definitions = rest;
677
+ }
678
+ decorate(_editor, _config) {
679
+ return createRendererDecoration("FootnoteSection", FootnoteSectionRenderer, {
680
+ definitions: this.__definitions,
681
+ nodeKey: this.__key
682
+ });
683
+ }
684
+ };
685
+ function $createFootnoteSectionNode(definitions = {}) {
686
+ return new FootnoteSectionNode(definitions);
687
+ }
688
+ function $isFootnoteSectionNode(node) {
689
+ return node instanceof FootnoteSectionNode;
690
+ }
691
+ //#endregion
692
+ //#region src/components/renderers/GridStaticDecorator.tsx
693
+ function GridStaticDecorator({ cols, gap, cellStates }) {
694
+ const renderContent = useNestedContentRenderer();
695
+ return /* @__PURE__ */ jsx("div", {
696
+ className: clsx(gridClassNames.inner, gridStyles.inner),
697
+ style: {
698
+ display: "grid",
699
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
700
+ gap
701
+ },
702
+ children: cellStates.map((state, i) => /* @__PURE__ */ jsx("div", {
703
+ className: clsx(gridClassNames.cell, gridStyles.cell),
704
+ children: renderContent(state)
705
+ }, i))
706
+ });
707
+ }
708
+ //#endregion
709
+ //#region src/nodes/GridContainerNode.ts
710
+ var GridContainerNode = class GridContainerNode extends DecoratorNode {
711
+ static getType() {
712
+ return "grid-container";
713
+ }
714
+ static clone(node) {
715
+ return new GridContainerNode(node.__cols, node.__gap, [...node.__cellStates], node.__key);
716
+ }
717
+ constructor(cols = 2, gap, cellStates, key) {
718
+ super(key);
719
+ _defineProperty(this, "__cols", void 0);
720
+ _defineProperty(this, "__gap", void 0);
721
+ _defineProperty(this, "__cellStates", void 0);
722
+ this.__cols = cols;
723
+ this.__gap = gap || "16px";
724
+ if (cellStates) this.__cellStates = cellStates;
725
+ else {
726
+ const emptyState = { root: {
727
+ children: [{
728
+ type: "paragraph",
729
+ children: [],
730
+ direction: null,
731
+ format: "",
732
+ indent: 0,
733
+ textFormat: 0,
734
+ textStyle: "",
735
+ version: 1
736
+ }],
737
+ direction: null,
738
+ format: "",
739
+ indent: 0,
740
+ type: "root",
741
+ version: 1
742
+ } };
743
+ this.__cellStates = Array.from({ length: cols }, () => emptyState);
744
+ }
745
+ }
746
+ createDOM(_config) {
747
+ const div = document.createElement("div");
748
+ div.className = `${gridClassNames.container} ${gridStyles.container}`;
749
+ return div;
750
+ }
751
+ updateDOM() {
752
+ return false;
753
+ }
754
+ isInline() {
755
+ return false;
756
+ }
757
+ getCols() {
758
+ return this.getLatest().__cols;
759
+ }
760
+ setCols(cols) {
761
+ const writable = this.getWritable();
762
+ const prev = writable.__cellStates.length;
763
+ writable.__cols = cols;
764
+ if (cols > prev) {
765
+ const emptyState = { root: {
766
+ children: [{
767
+ type: "paragraph",
768
+ children: [],
769
+ direction: null,
770
+ format: "",
771
+ indent: 0,
772
+ textFormat: 0,
773
+ textStyle: "",
774
+ version: 1
775
+ }],
776
+ direction: null,
777
+ format: "",
778
+ indent: 0,
779
+ type: "root",
780
+ version: 1
781
+ } };
782
+ for (let i = prev; i < cols; i++) writable.__cellStates.push(emptyState);
783
+ }
784
+ }
785
+ getGap() {
786
+ return this.getLatest().__gap;
787
+ }
788
+ setGap(gap) {
789
+ const writable = this.getWritable();
790
+ writable.__gap = gap;
791
+ }
792
+ getCellStates() {
793
+ return this.getLatest().__cellStates;
794
+ }
795
+ addCells(count) {
796
+ const writable = this.getWritable();
797
+ const emptyState = { root: {
798
+ children: [{
799
+ type: "paragraph",
800
+ children: [],
801
+ direction: null,
802
+ format: "",
803
+ indent: 0,
804
+ textFormat: 0,
805
+ textStyle: "",
806
+ version: 1
807
+ }],
808
+ direction: null,
809
+ format: "",
810
+ indent: 0,
811
+ type: "root",
812
+ version: 1
813
+ } };
814
+ for (let i = 0; i < count; i++) writable.__cellStates.push(emptyState);
815
+ }
816
+ removeCells(count) {
817
+ const states = this.getWritable().__cellStates;
818
+ const toRemove = Math.min(count, states.length);
819
+ for (let i = 0; i < toRemove; i++) {
820
+ const state = states.at(-1);
821
+ if (!state) break;
822
+ if (!(extractTextContent(state).trim() === "")) break;
823
+ states.pop();
824
+ }
825
+ }
826
+ getTextContent() {
827
+ return this.__cellStates.map((s) => extractTextContent(s)).join("\n");
828
+ }
829
+ static importJSON(serializedNode) {
830
+ const legacy = serializedNode;
831
+ const cols = legacy.cols || 2;
832
+ const rawGap = legacy.gap;
833
+ const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
834
+ if (legacy.cells && legacy.cells.length > 0) return new GridContainerNode(cols, gap, legacy.cells);
835
+ if (legacy.children) return new GridContainerNode(cols, gap, legacy.children.map((child) => {
836
+ return { root: {
837
+ children: [child],
838
+ direction: null,
839
+ format: "",
840
+ indent: 0,
841
+ type: "root",
842
+ version: 1
843
+ } };
844
+ }));
845
+ return new GridContainerNode(cols, gap);
846
+ }
847
+ exportJSON() {
848
+ return {
849
+ ...super.exportJSON(),
850
+ type: "grid-container",
851
+ cols: this.__cols,
852
+ gap: this.__gap,
853
+ cells: this.__cellStates,
854
+ version: 1
855
+ };
856
+ }
857
+ decorate(_editor, _config) {
858
+ return createElement(GridStaticDecorator, {
859
+ cols: this.__cols,
860
+ gap: this.__gap,
861
+ cellStates: this.__cellStates
862
+ });
863
+ }
864
+ };
865
+ function $createGridContainerNode(cols = 2, gap) {
866
+ return new GridContainerNode(cols, gap);
867
+ }
868
+ function $isGridContainerNode(node) {
869
+ return node instanceof GridContainerNode;
870
+ }
871
+ //#endregion
872
+ //#region src/nodes/LinkCardNode.ts
873
+ var LinkCardNode = class LinkCardNode extends DecoratorNode {
874
+ static getType() {
875
+ return "link-card";
876
+ }
877
+ static clone(node) {
878
+ return new LinkCardNode({
879
+ url: node.__url,
880
+ source: node.__source,
881
+ id: node.__id,
882
+ title: node.__title,
883
+ description: node.__description,
884
+ favicon: node.__favicon,
885
+ image: node.__image
886
+ }, node.__key);
887
+ }
888
+ constructor(payload, key) {
889
+ super(key);
890
+ _defineProperty(this, "__url", void 0);
891
+ _defineProperty(this, "__source", void 0);
892
+ _defineProperty(this, "__id", void 0);
893
+ _defineProperty(this, "__title", void 0);
894
+ _defineProperty(this, "__description", void 0);
895
+ _defineProperty(this, "__favicon", void 0);
896
+ _defineProperty(this, "__image", void 0);
897
+ this.__url = payload.url;
898
+ this.__source = payload.source;
899
+ this.__id = payload.id;
900
+ this.__title = payload.title;
901
+ this.__description = payload.description;
902
+ this.__favicon = payload.favicon;
903
+ this.__image = payload.image;
904
+ }
905
+ createDOM(_config) {
906
+ const div = document.createElement("div");
907
+ div.className = "rich-link-card-wrapper";
908
+ return div;
909
+ }
910
+ updateDOM() {
911
+ return false;
912
+ }
913
+ isInline() {
914
+ return false;
915
+ }
916
+ static importJSON(serializedNode) {
917
+ return $createLinkCardNode({
918
+ url: serializedNode.url,
919
+ source: serializedNode.source,
920
+ id: serializedNode.id,
921
+ title: serializedNode.title,
922
+ description: serializedNode.description,
923
+ favicon: serializedNode.favicon,
924
+ image: serializedNode.image
925
+ });
926
+ }
927
+ exportJSON() {
928
+ return {
929
+ ...super.exportJSON(),
930
+ type: "link-card",
931
+ url: this.__url,
932
+ source: this.__source,
933
+ id: this.__id,
934
+ title: this.__title,
935
+ description: this.__description,
936
+ favicon: this.__favicon,
937
+ image: this.__image,
938
+ version: 1
939
+ };
940
+ }
941
+ getUrl() {
942
+ return this.getLatest().__url;
943
+ }
944
+ setUrl(url) {
945
+ const writable = this.getWritable();
946
+ writable.__url = url;
947
+ }
948
+ getSource() {
949
+ return this.getLatest().__source;
950
+ }
951
+ setSource(source) {
952
+ const writable = this.getWritable();
953
+ writable.__source = source;
954
+ }
955
+ getId() {
956
+ return this.getLatest().__id;
957
+ }
958
+ setId(id) {
959
+ const writable = this.getWritable();
960
+ writable.__id = id;
961
+ }
962
+ decorate(_editor, _config) {
963
+ return createRendererDecoration("LinkCard", LinkCardRenderer, {
964
+ url: this.__url,
965
+ source: this.__source,
966
+ id: this.__id,
967
+ title: this.__title,
968
+ description: this.__description,
969
+ favicon: this.__favicon,
970
+ image: this.__image
971
+ });
972
+ }
973
+ };
974
+ _defineProperty(LinkCardNode, "commandItems", [{
975
+ title: "Link Card",
976
+ icon: createElement(Link, { size: 20 }),
977
+ description: "Link preview card",
978
+ keywords: [
979
+ "link",
980
+ "card",
981
+ "bookmark",
982
+ "embed"
983
+ ],
984
+ section: "MEDIA",
985
+ placement: ["slash", "toolbar"],
986
+ group: "insert",
987
+ onSelect: (editor) => {
988
+ editor.update(() => {
989
+ $insertNodes([$createLinkCardNode({ url: "" })]);
990
+ });
991
+ }
992
+ }]);
993
+ function $createLinkCardNode(payload) {
994
+ return new LinkCardNode(payload);
995
+ }
996
+ function $isLinkCardNode(node) {
997
+ return node instanceof LinkCardNode;
998
+ }
999
+ //#endregion
1000
+ //#region src/context/PollDataContext.tsx
1001
+ var PollDataContext = createContext({
1002
+ adapter: null,
1003
+ initialStates: {}
1004
+ });
1005
+ function PollDataProvider({ adapter, initialStates, children }) {
1006
+ const value = useMemo(() => ({
1007
+ adapter,
1008
+ initialStates: initialStates ?? {}
1009
+ }), [adapter, initialStates]);
1010
+ return /* @__PURE__ */ jsx(PollDataContext.Provider, {
1011
+ value,
1012
+ children
1013
+ });
1014
+ }
1015
+ function usePollDataAdapter() {
1016
+ return use(PollDataContext).adapter;
1017
+ }
1018
+ function useInitialPollState(pollId) {
1019
+ return use(PollDataContext).initialStates[pollId];
1020
+ }
1021
+ //#endregion
1022
+ //#region src/styles/poll.css.ts
1023
+ var pollClasses = {
1024
+ container: "_1m5xzan0",
1025
+ meta: "_1m5xzan1",
1026
+ question: "_1m5xzan2",
1027
+ optionList: "_1m5xzan3",
1028
+ option: "_1m5xzan4",
1029
+ optionInteractive: "_1m5xzan5",
1030
+ optionSelected: "_1m5xzan6",
1031
+ optionDisabled: "_1m5xzan7",
1032
+ tint: "_1m5xzan8",
1033
+ tintActive: "_1m5xzan9",
1034
+ optionRow: "_1m5xzana",
1035
+ optionLabel: "_1m5xzanb",
1036
+ optionPct: "_1m5xzanc",
1037
+ optionPctActive: "_1m5xzand",
1038
+ hint: "_1m5xzane",
1039
+ submit: "_1m5xzanf",
1040
+ submitActive: "_1m5xzang",
1041
+ footer: "_1m5xzanh",
1042
+ errorMessage: "_1m5xzani",
1043
+ skeleton: "_1m5xzanj"
1044
+ };
1045
+ //#endregion
1046
+ //#region src/components/renderers/PollRenderer.tsx
1047
+ function shouldShowTallies(state, showResults) {
1048
+ if (showResults === "after-vote") return state.userVote !== void 0 || state.closed;
1049
+ if (showResults === "after-close") return state.closed;
1050
+ return true;
1051
+ }
1052
+ function classNames(...names) {
1053
+ return names.filter((n) => Boolean(n)).join(" ");
1054
+ }
1055
+ function PollStaticFallback({ question, options }) {
1056
+ return /* @__PURE__ */ jsxs("div", {
1057
+ className: pollClasses.container,
1058
+ children: [/* @__PURE__ */ jsx("p", {
1059
+ className: pollClasses.question,
1060
+ children: question
1061
+ }), /* @__PURE__ */ jsx("ul", {
1062
+ className: pollClasses.optionList,
1063
+ children: options.map((option) => /* @__PURE__ */ jsx("li", {
1064
+ className: pollClasses.option,
1065
+ children: /* @__PURE__ */ jsx("div", {
1066
+ className: pollClasses.optionRow,
1067
+ children: /* @__PURE__ */ jsx("span", {
1068
+ className: pollClasses.optionLabel,
1069
+ children: option.label
1070
+ })
1071
+ })
1072
+ }, option.id))
1073
+ })]
1074
+ });
1075
+ }
1076
+ function PollInteractive({ adapter, closeAt, mode, options, pollId, question, showResults }) {
1077
+ const initialState = useInitialPollState(pollId);
1078
+ const liveState = adapter.usePollState(pollId);
1079
+ const submit = adapter.useSubmit(pollId);
1080
+ const state = liveState ?? initialState ?? {
1081
+ tallies: {},
1082
+ totalVotes: 0,
1083
+ status: "loading",
1084
+ closed: false,
1085
+ canVote: false
1086
+ };
1087
+ const [pendingSelection, setPendingSelection] = useState([]);
1088
+ const [isSubmitting, setIsSubmitting] = useState(false);
1089
+ const showTallies = shouldShowTallies(state, showResults);
1090
+ const userVoted = state.userVote !== void 0;
1091
+ const isClosed = state.closed;
1092
+ const canInteract = !userVoted && !isClosed && state.canVote && state.status !== "loading";
1093
+ const tallyShare = useCallback((optionId) => {
1094
+ if (!showTallies || state.totalVotes <= 0) return 0;
1095
+ const tally = state.tallies[optionId] ?? 0;
1096
+ return Math.max(0, Math.min(1, tally / state.totalVotes));
1097
+ }, [
1098
+ showTallies,
1099
+ state.tallies,
1100
+ state.totalVotes
1101
+ ]);
1102
+ const handleSingleClick = useCallback(async (optionId) => {
1103
+ if (!canInteract || isSubmitting) return;
1104
+ setIsSubmitting(true);
1105
+ try {
1106
+ await submit([optionId]);
1107
+ } finally {
1108
+ setIsSubmitting(false);
1109
+ }
1110
+ }, [
1111
+ canInteract,
1112
+ isSubmitting,
1113
+ submit
1114
+ ]);
1115
+ const handleMultiToggle = useCallback((optionId) => {
1116
+ if (!canInteract || isSubmitting) return;
1117
+ setPendingSelection((prev) => prev.includes(optionId) ? prev.filter((id) => id !== optionId) : [...prev, optionId]);
1118
+ }, [canInteract, isSubmitting]);
1119
+ const handleMultiSubmit = useCallback(async () => {
1120
+ if (!canInteract || isSubmitting || pendingSelection.length === 0) return;
1121
+ setIsSubmitting(true);
1122
+ try {
1123
+ await submit([...pendingSelection]);
1124
+ } finally {
1125
+ setIsSubmitting(false);
1126
+ }
1127
+ }, [
1128
+ canInteract,
1129
+ isSubmitting,
1130
+ pendingSelection,
1131
+ submit
1132
+ ]);
1133
+ const metaLabel = useMemo(() => {
1134
+ const parts = [mode === "single" ? "Single choice" : "Multiple choice"];
1135
+ if (isClosed) parts.push("Voting closed");
1136
+ else if (userVoted) parts.push("Voted");
1137
+ return parts.join(" · ");
1138
+ }, [
1139
+ mode,
1140
+ isClosed,
1141
+ userVoted
1142
+ ]);
1143
+ if (state.status === "loading") return /* @__PURE__ */ jsxs("div", {
1144
+ className: pollClasses.container,
1145
+ children: [
1146
+ /* @__PURE__ */ jsx("p", {
1147
+ className: pollClasses.meta,
1148
+ children: metaLabel
1149
+ }),
1150
+ /* @__PURE__ */ jsx("p", {
1151
+ className: pollClasses.question,
1152
+ children: question
1153
+ }),
1154
+ /* @__PURE__ */ jsx("ul", {
1155
+ className: pollClasses.optionList,
1156
+ children: options.map((option) => /* @__PURE__ */ jsx("li", { className: pollClasses.skeleton }, option.id))
1157
+ })
1158
+ ]
1159
+ });
1160
+ return /* @__PURE__ */ jsxs("div", {
1161
+ className: pollClasses.container,
1162
+ children: [
1163
+ /* @__PURE__ */ jsx("p", {
1164
+ className: pollClasses.meta,
1165
+ children: metaLabel
1166
+ }),
1167
+ /* @__PURE__ */ jsx("p", {
1168
+ className: pollClasses.question,
1169
+ children: question
1170
+ }),
1171
+ /* @__PURE__ */ jsx("ul", {
1172
+ className: pollClasses.optionList,
1173
+ children: options.map((option) => {
1174
+ const isUserChoice = state.userVote?.includes(option.id) ?? false;
1175
+ const isPending = pendingSelection.includes(option.id);
1176
+ const tintWidth = (() => {
1177
+ if (canInteract && mode === "multiple" && isPending) return 1;
1178
+ return tallyShare(option.id);
1179
+ })();
1180
+ const tintShouldBeActive = isUserChoice || isPending;
1181
+ const labelHighlighted = isUserChoice || isPending;
1182
+ const handleClick = canInteract ? mode === "single" ? () => handleSingleClick(option.id) : () => handleMultiToggle(option.id) : void 0;
1183
+ return /* @__PURE__ */ jsxs("li", {
1184
+ className: classNames(pollClasses.option, canInteract ? pollClasses.optionInteractive : pollClasses.optionDisabled, labelHighlighted && pollClasses.optionSelected),
1185
+ onClick: handleClick,
1186
+ ...canInteract ? {
1187
+ role: "button",
1188
+ tabIndex: 0,
1189
+ onKeyDown: (event) => {
1190
+ if (event.key === "Enter" || event.key === " ") {
1191
+ event.preventDefault();
1192
+ handleClick?.();
1193
+ }
1194
+ }
1195
+ } : {},
1196
+ children: [/* @__PURE__ */ jsx("span", {
1197
+ "aria-hidden": true,
1198
+ style: { width: `${tintWidth * 100}%` },
1199
+ className: classNames(pollClasses.tint, tintShouldBeActive && pollClasses.tintActive)
1200
+ }), /* @__PURE__ */ jsxs("div", {
1201
+ className: pollClasses.optionRow,
1202
+ children: [/* @__PURE__ */ jsxs("span", {
1203
+ className: pollClasses.optionLabel,
1204
+ children: [labelHighlighted && "✓ ", option.label]
1205
+ }), showTallies && (userVoted || isClosed) ? /* @__PURE__ */ jsxs("span", {
1206
+ className: classNames(pollClasses.optionPct, labelHighlighted && pollClasses.optionPctActive),
1207
+ children: [Math.round(tallyShare(option.id) * 100), "%"]
1208
+ }) : canInteract && mode === "single" ? /* @__PURE__ */ jsx("span", {
1209
+ className: pollClasses.hint,
1210
+ children: "Click to vote"
1211
+ }) : null]
1212
+ })]
1213
+ }, option.id);
1214
+ })
1215
+ }),
1216
+ canInteract && mode === "multiple" && /* @__PURE__ */ jsx("button", {
1217
+ disabled: pendingSelection.length === 0 || isSubmitting,
1218
+ type: "button",
1219
+ className: classNames(pollClasses.submit, pendingSelection.length > 0 && !isSubmitting && pollClasses.submitActive),
1220
+ onClick: handleMultiSubmit,
1221
+ children: isSubmitting ? "Submitting…" : pendingSelection.length > 0 ? `Submit ${pendingSelection.length} ${pendingSelection.length === 1 ? "item" : "items"}` : "Submit (select at least one)"
1222
+ }),
1223
+ /* @__PURE__ */ jsxs("div", {
1224
+ className: pollClasses.footer,
1225
+ children: [(userVoted || isClosed) && state.totalVotes > 0 ? /* @__PURE__ */ jsxs("span", { children: [
1226
+ state.totalVotes.toLocaleString(),
1227
+ " ",
1228
+ state.totalVotes === 1 ? "vote" : "votes"
1229
+ ] }) : /* @__PURE__ */ jsx("span", {}), closeAt && !isClosed ? /* @__PURE__ */ jsxs("span", { children: ["Closes ", closeAt] }) : null]
1230
+ }),
1231
+ state.status === "error" && state.errorMessage ? /* @__PURE__ */ jsx("p", {
1232
+ className: pollClasses.errorMessage,
1233
+ children: state.errorMessage
1234
+ }) : !state.canVote && !userVoted && !isClosed && state.errorMessage ? /* @__PURE__ */ jsx("p", {
1235
+ className: pollClasses.errorMessage,
1236
+ children: state.errorMessage
1237
+ }) : null
1238
+ ]
1239
+ });
1240
+ }
1241
+ function PollRenderer(props) {
1242
+ const adapter = usePollDataAdapter();
1243
+ if (!adapter) return /* @__PURE__ */ jsx(PollStaticFallback, { ...props });
1244
+ return /* @__PURE__ */ jsx(PollInteractive, {
1245
+ adapter,
1246
+ ...props
1247
+ });
1248
+ }
1249
+ //#endregion
1250
+ //#region src/nodes/PollNode.ts
1251
+ var idAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
1252
+ var makePollIdSuffix = customAlphabet(idAlphabet, 10);
1253
+ var makeOptionIdSuffix = customAlphabet(idAlphabet, 6);
1254
+ function createPollId() {
1255
+ return `p_${makePollIdSuffix()}`;
1256
+ }
1257
+ function createOptionId() {
1258
+ return `o_${makeOptionIdSuffix()}`;
1259
+ }
1260
+ var PollNode = class PollNode extends DecoratorNode {
1261
+ static getType() {
1262
+ return "poll";
1263
+ }
1264
+ static clone(node) {
1265
+ return new PollNode({
1266
+ pollId: node.__pollId,
1267
+ question: node.__question,
1268
+ options: node.__options,
1269
+ mode: node.__mode,
1270
+ closeAt: node.__closeAt,
1271
+ showResults: node.__showResults
1272
+ }, node.__key);
1273
+ }
1274
+ constructor(payload = {}, key) {
1275
+ super(key);
1276
+ _defineProperty(this, "__pollId", void 0);
1277
+ _defineProperty(this, "__question", void 0);
1278
+ _defineProperty(this, "__options", void 0);
1279
+ _defineProperty(this, "__mode", void 0);
1280
+ _defineProperty(this, "__closeAt", void 0);
1281
+ _defineProperty(this, "__showResults", void 0);
1282
+ this.__pollId = payload.pollId ?? createPollId();
1283
+ this.__question = payload.question ?? "";
1284
+ this.__options = payload.options && payload.options.length > 0 ? payload.options : [{
1285
+ id: createOptionId(),
1286
+ label: ""
1287
+ }, {
1288
+ id: createOptionId(),
1289
+ label: ""
1290
+ }];
1291
+ this.__mode = payload.mode ?? "single";
1292
+ this.__closeAt = payload.closeAt;
1293
+ this.__showResults = payload.showResults;
1294
+ }
1295
+ createDOM(_config) {
1296
+ const div = document.createElement("div");
1297
+ div.className = "rich-poll-wrapper";
1298
+ return div;
1299
+ }
1300
+ updateDOM() {
1301
+ return false;
1302
+ }
1303
+ isInline() {
1304
+ return false;
1305
+ }
1306
+ getPollId() {
1307
+ return this.getLatest().__pollId;
1308
+ }
1309
+ getQuestion() {
1310
+ return this.getLatest().__question;
1311
+ }
1312
+ setQuestion(question) {
1313
+ const writable = this.getWritable();
1314
+ writable.__question = question;
1315
+ }
1316
+ getOptions() {
1317
+ return this.getLatest().__options;
1318
+ }
1319
+ setOptions(options) {
1320
+ const writable = this.getWritable();
1321
+ writable.__options = options;
1322
+ }
1323
+ getMode() {
1324
+ return this.getLatest().__mode;
1325
+ }
1326
+ setMode(mode) {
1327
+ const writable = this.getWritable();
1328
+ writable.__mode = mode;
1329
+ }
1330
+ getCloseAt() {
1331
+ return this.getLatest().__closeAt;
1332
+ }
1333
+ setCloseAt(closeAt) {
1334
+ const writable = this.getWritable();
1335
+ writable.__closeAt = closeAt;
1336
+ }
1337
+ getShowResults() {
1338
+ return this.getLatest().__showResults;
1339
+ }
1340
+ setShowResults(showResults) {
1341
+ const writable = this.getWritable();
1342
+ writable.__showResults = showResults;
1343
+ }
1344
+ static importJSON(serializedNode) {
1345
+ return new PollNode({
1346
+ pollId: serializedNode.pollId,
1347
+ question: serializedNode.question,
1348
+ options: serializedNode.options,
1349
+ mode: serializedNode.mode,
1350
+ closeAt: serializedNode.closeAt,
1351
+ showResults: serializedNode.showResults
1352
+ });
1353
+ }
1354
+ exportJSON() {
1355
+ return {
1356
+ ...super.exportJSON(),
1357
+ type: "poll",
1358
+ pollId: this.__pollId,
1359
+ question: this.__question,
1360
+ options: this.__options,
1361
+ mode: this.__mode,
1362
+ ...this.__closeAt ? { closeAt: this.__closeAt } : {},
1363
+ ...this.__showResults ? { showResults: this.__showResults } : {},
1364
+ version: 1
1365
+ };
1366
+ }
1367
+ decorate(_editor, _config) {
1368
+ return createRendererDecoration("Poll", PollRenderer, {
1369
+ pollId: this.__pollId,
1370
+ question: this.__question,
1371
+ options: this.__options,
1372
+ mode: this.__mode,
1373
+ closeAt: this.__closeAt,
1374
+ showResults: this.__showResults
1375
+ });
1376
+ }
1377
+ };
1378
+ function $createPollNode(payload) {
1379
+ return new PollNode(payload);
1380
+ }
1381
+ function $isPollNode(node) {
1382
+ return node instanceof PollNode;
1383
+ }
1384
+ //#endregion
1385
+ //#region src/nodes/RubyNode.ts
1386
+ function readBaseTextFromRuby(element) {
1387
+ let base = "";
1388
+ for (const child of Array.from(element.childNodes)) {
1389
+ if (child.nodeType === Node.TEXT_NODE) {
1390
+ base += child.textContent ?? "";
1391
+ continue;
1392
+ }
1393
+ if (child.nodeType !== Node.ELEMENT_NODE) continue;
1394
+ const childElement = child;
1395
+ const tag = childElement.tagName.toLowerCase();
1396
+ if (tag === "rt" || tag === "rp") continue;
1397
+ base += childElement.textContent ?? "";
1398
+ }
1399
+ return base;
1400
+ }
1401
+ var RubyNode = class RubyNode extends ElementNode {
1402
+ static getType() {
1403
+ return "ruby";
1404
+ }
1405
+ static clone(node) {
1406
+ return new RubyNode(node.__reading, node.__key);
1407
+ }
1408
+ constructor(reading, key) {
1409
+ super(key);
1410
+ _defineProperty(this, "__reading", void 0);
1411
+ this.__reading = reading;
1412
+ }
1413
+ static importJSON(serializedNode) {
1414
+ return $createRubyNode(serializedNode.reading ?? "");
1415
+ }
1416
+ exportJSON() {
1417
+ return {
1418
+ ...super.exportJSON(),
1419
+ type: "ruby",
1420
+ reading: this.__reading,
1421
+ version: 1
1422
+ };
1423
+ }
1424
+ static importDOM() {
1425
+ return { ruby: () => ({
1426
+ conversion: (domNode) => {
1427
+ if (!(domNode instanceof HTMLElement)) return null;
1428
+ const reading = domNode.querySelector("rt")?.textContent ?? "";
1429
+ const baseText = readBaseTextFromRuby(domNode);
1430
+ const node = $createRubyNode(reading);
1431
+ if (baseText) node.append($createTextNode(baseText));
1432
+ return { node };
1433
+ },
1434
+ priority: 2
1435
+ }) };
1436
+ }
1437
+ exportDOM() {
1438
+ const ruby = document.createElement("ruby");
1439
+ ruby.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
1440
+ const baseText = this.getTextContent();
1441
+ if (baseText) ruby.append(baseText);
1442
+ if (this.__reading) {
1443
+ const rt = document.createElement("rt");
1444
+ rt.className = `${semanticClassNames.rubyRt} ${sharedStyles.rubyRt}`;
1445
+ rt.textContent = this.__reading;
1446
+ ruby.append(rt);
1447
+ }
1448
+ return { element: ruby };
1449
+ }
1450
+ createDOM(_config) {
1451
+ const span = document.createElement("span");
1452
+ span.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
1453
+ if (this.__reading) span.dataset.ruby = this.__reading;
1454
+ return span;
1455
+ }
1456
+ updateDOM(prevNode, dom) {
1457
+ if (prevNode.__reading !== this.__reading) if (this.__reading) dom.dataset.ruby = this.__reading;
1458
+ else delete dom.dataset.ruby;
1459
+ return false;
1460
+ }
1461
+ canInsertTextBefore() {
1462
+ return true;
1463
+ }
1464
+ canInsertTextAfter() {
1465
+ return true;
1466
+ }
1467
+ isInline() {
1468
+ return true;
1469
+ }
1470
+ getReading() {
1471
+ return this.getLatest().__reading;
1472
+ }
1473
+ setReading(reading) {
1474
+ const writable = this.getWritable();
1475
+ writable.__reading = reading;
1476
+ }
1477
+ };
1478
+ function $createRubyNode(reading) {
1479
+ return new RubyNode(reading);
1480
+ }
1481
+ function $isRubyNode(node) {
1482
+ return node instanceof RubyNode;
1483
+ }
1484
+ //#endregion
1485
+ //#region src/components/renderers/VideoRenderer.tsx
1486
+ function VideoRenderer({ src, poster, width, height }) {
1487
+ return /* @__PURE__ */ jsx("figure", {
1488
+ className: "rich-video",
1489
+ children: /* @__PURE__ */ jsx("video", {
1490
+ controls: true,
1491
+ height,
1492
+ poster,
1493
+ preload: "metadata",
1494
+ src,
1495
+ style: {
1496
+ maxWidth: "100%",
1497
+ height: "auto"
1498
+ },
1499
+ width
1500
+ })
1501
+ });
1502
+ }
1503
+ //#endregion
1504
+ //#region src/nodes/VideoNode.ts
1505
+ var VideoNode = class VideoNode extends DecoratorNode {
1506
+ static getType() {
1507
+ return "video";
1508
+ }
1509
+ static clone(node) {
1510
+ return new VideoNode({
1511
+ src: node.__src,
1512
+ poster: node.__poster,
1513
+ width: node.__width,
1514
+ height: node.__height
1515
+ }, node.__key);
1516
+ }
1517
+ constructor(payload, key) {
1518
+ super(key);
1519
+ _defineProperty(this, "__src", void 0);
1520
+ _defineProperty(this, "__poster", void 0);
1521
+ _defineProperty(this, "__width", void 0);
1522
+ _defineProperty(this, "__height", void 0);
1523
+ this.__src = payload.src;
1524
+ this.__poster = payload.poster;
1525
+ this.__width = payload.width;
1526
+ this.__height = payload.height;
1527
+ }
1528
+ createDOM(_config) {
1529
+ const div = document.createElement("div");
1530
+ div.className = "rich-video-wrapper";
1531
+ return div;
1532
+ }
1533
+ updateDOM() {
1534
+ return false;
1535
+ }
1536
+ isInline() {
1537
+ return false;
1538
+ }
1539
+ static importJSON(serializedNode) {
1540
+ return $createVideoNode({
1541
+ src: serializedNode.src,
1542
+ poster: serializedNode.poster,
1543
+ width: serializedNode.width,
1544
+ height: serializedNode.height
1545
+ });
1546
+ }
1547
+ exportJSON() {
1548
+ return {
1549
+ ...super.exportJSON(),
1550
+ type: "video",
1551
+ src: this.__src,
1552
+ poster: this.__poster,
1553
+ width: this.__width,
1554
+ height: this.__height,
1555
+ version: 1
1556
+ };
1557
+ }
1558
+ getSrc() {
1559
+ return this.getLatest().__src;
1560
+ }
1561
+ setSrc(src) {
1562
+ const writable = this.getWritable();
1563
+ writable.__src = src;
1564
+ }
1565
+ decorate(_editor, _config) {
1566
+ return createRendererDecoration("Video", VideoRenderer, {
1567
+ src: this.__src,
1568
+ poster: this.__poster,
1569
+ width: this.__width,
1570
+ height: this.__height
1571
+ });
1572
+ }
1573
+ };
1574
+ _defineProperty(VideoNode, "commandItems", [{
1575
+ title: "Video",
1576
+ icon: createElement(Video, { size: 20 }),
1577
+ description: "Embed a video",
1578
+ keywords: [
1579
+ "video",
1580
+ "media",
1581
+ "mp4"
1582
+ ],
1583
+ section: "MEDIA",
1584
+ placement: ["slash", "toolbar"],
1585
+ group: "insert",
1586
+ onSelect: (editor) => {
1587
+ editor.update(() => {
1588
+ $insertNodes([$createVideoNode({ src: "" })]);
1589
+ });
1590
+ }
1591
+ }]);
1592
+ function $createVideoNode(payload) {
1593
+ return new VideoNode(payload);
1594
+ }
1595
+ //#endregion
1596
+ //#region src/config.ts
1597
+ var builtinNodes = [
1598
+ HeadingNode,
1599
+ QuoteNode,
1600
+ ListNode,
1601
+ ListItemNode,
1602
+ LinkNode,
1603
+ AutoLinkNode,
1604
+ HorizontalRuleNode,
1605
+ TableNode,
1606
+ TableCellNode,
1607
+ TableRowNode,
1608
+ CodeNode
1609
+ ];
1610
+ var customNodes = [
1611
+ SpoilerNode,
1612
+ MentionNode,
1613
+ KaTeXInlineNode,
1614
+ KaTeXBlockNode,
1615
+ ImageNode,
1616
+ AlertQuoteNode,
1617
+ CodeBlockNode,
1618
+ FootnoteNode,
1619
+ FootnoteSectionNode,
1620
+ VideoNode,
1621
+ LinkCardNode,
1622
+ CommentNode,
1623
+ DetailsNode,
1624
+ GridContainerNode,
1625
+ BannerNode,
1626
+ MermaidNode,
1627
+ RubyNode,
1628
+ TagNode,
1629
+ PollNode
1630
+ ];
1631
+ var allNodes = [...builtinNodes, ...customNodes];
1632
+ //#endregion
1633
+ export { $createCommentPlaceholderNode as A, BANNER_TYPES as B, GridContainerNode as C, $createDetailsNode as D, FootnoteSectionNode as E, CodeBlockRenderer as F, normalizeBannerType as H, ColorSchemeProvider as I, useColorScheme as L, CommentNode as M, $isCodeBlockNode as N, DetailsNode as O, CodeBlockNode as P, $isBannerNode as R, $isGridContainerNode as S, $isFootnoteSectionNode as T, BannerRenderer as U, BannerNode as V, usePollDataAdapter as _, $createRubyNode as a, LinkCardNode as b, $createPollNode as c, createOptionId as d, createPollId as f, useInitialPollState as g, PollDataProvider as h, VideoNode as i, $isCommentNode as j, $createCommentNode as k, $isPollNode as l, pollClasses as m, builtinNodes as n, $isRubyNode as o, PollRenderer as p, customNodes as r, RubyNode as s, allNodes as t, PollNode as u, $createLinkCardNode as v, $createFootnoteSectionNode as w, $createGridContainerNode as x, $isLinkCardNode as y, BANNER_LABELS as z };