@pixerate/editor 0.24.0 → 0.25.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 (133) hide show
  1. package/README.md +71 -1
  2. package/THIRD_PARTY_NOTICES.md +29 -0
  3. package/dist/agent/index.cjs +10 -9
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +1 -1
  6. package/dist/agent/index.d.ts +1 -1
  7. package/dist/agent/index.js +4 -3
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/canvas/index.cjs +3173 -340
  10. package/dist/canvas/index.cjs.map +1 -1
  11. package/dist/canvas/index.d.cts +617 -4
  12. package/dist/canvas/index.d.ts +617 -4
  13. package/dist/canvas/index.js +3105 -272
  14. package/dist/canvas/index.js.map +1 -1
  15. package/dist/chunk-4BVP64JJ.cjs +1406 -0
  16. package/dist/chunk-4BVP64JJ.cjs.map +1 -0
  17. package/dist/{chunk-XNORY7M6.cjs → chunk-7WISYZFO.cjs} +105 -50
  18. package/dist/chunk-7WISYZFO.cjs.map +1 -0
  19. package/dist/{chunk-OLZJXL4R.cjs → chunk-AL6JMGWW.cjs} +59 -25
  20. package/dist/chunk-AL6JMGWW.cjs.map +1 -0
  21. package/dist/chunk-BVP3TSLH.cjs +69 -0
  22. package/dist/chunk-BVP3TSLH.cjs.map +1 -0
  23. package/dist/{chunk-MCSJFWAO.cjs → chunk-C7P22VIP.cjs} +670 -96
  24. package/dist/chunk-C7P22VIP.cjs.map +1 -0
  25. package/dist/{chunk-PGNZKFQS.js → chunk-ECCCYKGL.js} +76 -21
  26. package/dist/chunk-ECCCYKGL.js.map +1 -0
  27. package/dist/{chunk-3CN2WD3X.js → chunk-FH6F67DN.js} +646 -72
  28. package/dist/chunk-FH6F67DN.js.map +1 -0
  29. package/dist/chunk-HRMEDT2L.js +1406 -0
  30. package/dist/chunk-HRMEDT2L.js.map +1 -0
  31. package/dist/{chunk-ITBSKI26.js → chunk-IXHSYOVY.js} +941 -9
  32. package/dist/chunk-IXHSYOVY.js.map +1 -0
  33. package/dist/{chunk-FODNLX5N.js → chunk-NAXVVS5I.js} +38 -4
  34. package/dist/chunk-NAXVVS5I.js.map +1 -0
  35. package/dist/chunk-O5RYH2PG.js +69 -0
  36. package/dist/chunk-O5RYH2PG.js.map +1 -0
  37. package/dist/{chunk-SBCZ6RN5.cjs → chunk-PO6A6QQH.cjs} +1039 -107
  38. package/dist/chunk-PO6A6QQH.cjs.map +1 -0
  39. package/dist/{chunk-3LV6RRPH.js → chunk-QGYDWPRM.js} +2 -2
  40. package/dist/{chunk-U5KWZU4X.js → chunk-RR7F5GUM.js} +2632 -129
  41. package/dist/chunk-RR7F5GUM.js.map +1 -0
  42. package/dist/{chunk-5S3Z4PPF.js → chunk-UAKDG62Z.js} +232 -25
  43. package/dist/chunk-UAKDG62Z.js.map +1 -0
  44. package/dist/{chunk-SML64VJG.cjs → chunk-V3CHHWOE.cjs} +4 -4
  45. package/dist/{chunk-SML64VJG.cjs.map → chunk-V3CHHWOE.cjs.map} +1 -1
  46. package/dist/chunk-WSQKHXW7.cjs +5726 -0
  47. package/dist/chunk-WSQKHXW7.cjs.map +1 -0
  48. package/dist/{chunk-D27YFC46.cjs → chunk-X4N4WUKB.cjs} +267 -60
  49. package/dist/chunk-X4N4WUKB.cjs.map +1 -0
  50. package/dist/commands/index.cjs +74 -1
  51. package/dist/commands/index.cjs.map +1 -1
  52. package/dist/commands/index.d.cts +34 -1
  53. package/dist/commands/index.d.ts +34 -1
  54. package/dist/commands/index.js +73 -0
  55. package/dist/commands/index.js.map +1 -1
  56. package/dist/compare/index.cjs +3 -3
  57. package/dist/compare/index.d.cts +1 -1
  58. package/dist/compare/index.d.ts +1 -1
  59. package/dist/compare/index.js +2 -2
  60. package/dist/controller-BIJiNoVs.d.cts +1842 -0
  61. package/dist/controller-BIJiNoVs.d.ts +1842 -0
  62. package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
  63. package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
  64. package/dist/extensions/index.cjs +53 -6
  65. package/dist/extensions/index.cjs.map +1 -1
  66. package/dist/extensions/index.d.cts +12 -4
  67. package/dist/extensions/index.d.ts +12 -4
  68. package/dist/extensions/index.js +53 -6
  69. package/dist/generation/index.cjs +1013 -69
  70. package/dist/generation/index.cjs.map +1 -1
  71. package/dist/generation/index.d.cts +474 -13
  72. package/dist/generation/index.d.ts +474 -13
  73. package/dist/generation/index.js +976 -32
  74. package/dist/generation/index.js.map +1 -1
  75. package/dist/image-editor/index.cjs +3385 -1275
  76. package/dist/image-editor/index.cjs.map +1 -1
  77. package/dist/image-editor/index.d.cts +693 -128
  78. package/dist/image-editor/index.d.ts +693 -128
  79. package/dist/image-editor/index.js +3325 -1215
  80. package/dist/image-editor/index.js.map +1 -1
  81. package/dist/{index-fJrlkJCd.d.ts → index-7a6bHTLy.d.ts} +494 -5
  82. package/dist/{index-BhJBSm2d.d.cts → index-Csd5qI8u.d.cts} +494 -5
  83. package/dist/index.cjs +1491 -45
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +110 -19
  86. package/dist/index.d.ts +110 -19
  87. package/dist/index.js +1464 -18
  88. package/dist/index.js.map +1 -1
  89. package/dist/mention-text-CDnCFd3c.d.cts +53 -0
  90. package/dist/mention-text-CDnCFd3c.d.ts +53 -0
  91. package/dist/model-C1zApQM8.d.cts +219 -0
  92. package/dist/model-C1zApQM8.d.ts +219 -0
  93. package/dist/persistence/index.d.cts +3 -120
  94. package/dist/persistence/index.d.ts +3 -120
  95. package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
  96. package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
  97. package/dist/serializers/index.cjs +12 -4
  98. package/dist/serializers/index.cjs.map +1 -1
  99. package/dist/serializers/index.d.cts +39 -12
  100. package/dist/serializers/index.d.ts +39 -12
  101. package/dist/serializers/index.js +12 -4
  102. package/dist/spreadsheet/index.cjs +127 -3
  103. package/dist/spreadsheet/index.cjs.map +1 -1
  104. package/dist/spreadsheet/index.d.cts +431 -11
  105. package/dist/spreadsheet/index.d.ts +431 -11
  106. package/dist/spreadsheet/index.js +127 -3
  107. package/dist/storage-BpFfspHm.d.cts +120 -0
  108. package/dist/storage-BpFfspHm.d.ts +120 -0
  109. package/dist/tokens/index.cjs +115 -0
  110. package/dist/tokens/index.cjs.map +1 -0
  111. package/dist/tokens/index.d.cts +212 -0
  112. package/dist/tokens/index.d.ts +212 -0
  113. package/dist/tokens/index.js +115 -0
  114. package/dist/tokens/index.js.map +1 -0
  115. package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
  116. package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
  117. package/package.json +6 -1
  118. package/dist/chunk-3CN2WD3X.js.map +0 -1
  119. package/dist/chunk-5S3Z4PPF.js.map +0 -1
  120. package/dist/chunk-D27YFC46.cjs.map +0 -1
  121. package/dist/chunk-FODNLX5N.js.map +0 -1
  122. package/dist/chunk-ITBSKI26.js.map +0 -1
  123. package/dist/chunk-MA2F5KHF.cjs +0 -3223
  124. package/dist/chunk-MA2F5KHF.cjs.map +0 -1
  125. package/dist/chunk-MCSJFWAO.cjs.map +0 -1
  126. package/dist/chunk-OLZJXL4R.cjs.map +0 -1
  127. package/dist/chunk-PGNZKFQS.js.map +0 -1
  128. package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
  129. package/dist/chunk-U5KWZU4X.js.map +0 -1
  130. package/dist/chunk-XNORY7M6.cjs.map +0 -1
  131. package/dist/controller-BhLLAoi8.d.cts +0 -1000
  132. package/dist/controller-BhLLAoi8.d.ts +0 -1000
  133. /package/dist/{chunk-3LV6RRPH.js.map → chunk-QGYDWPRM.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  compareDocuments
3
- } from "./chunk-FODNLX5N.js";
3
+ } from "./chunk-NAXVVS5I.js";
4
4
  import {
5
5
  INSTRUCTION_REGEX,
6
6
  TEMPLATE_REGEX,
@@ -16,20 +16,20 @@ import {
16
16
  rtfToPlainText,
17
17
  sanitizeImportedHtml,
18
18
  serializeRichClipboardPayload
19
- } from "./chunk-3LV6RRPH.js";
19
+ } from "./chunk-QGYDWPRM.js";
20
20
  import {
21
21
  escapeHtml,
22
22
  htmlToPlainText,
23
23
  markdownToTipTapHtml,
24
24
  plainTextToSlice
25
- } from "./chunk-PGNZKFQS.js";
25
+ } from "./chunk-ECCCYKGL.js";
26
26
  import {
27
27
  CommentStore,
28
28
  TRACK_CHANGES_SKIP_META,
29
29
  TrackChanges,
30
30
  createReviewId,
31
31
  trackChangesPluginKey
32
- } from "./chunk-5S3Z4PPF.js";
32
+ } from "./chunk-UAKDG62Z.js";
33
33
 
34
34
  // src/extensions/gradient-text.ts
35
35
  import { Mark } from "@tiptap/core";
@@ -601,10 +601,47 @@ var Mention = BaseMention.extend({
601
601
  if (!attributes.color) return {};
602
602
  return { "data-color": attributes.color };
603
603
  }
604
+ },
605
+ // Role of an asset mention (`reference`, `style`, `start_frame`, …). The
606
+ // node attribute is `assetRole` so entities that already carry a `role`
607
+ // (e.g. a user's job title) passed as suggestion props do not set it.
608
+ assetRole: {
609
+ default: null,
610
+ parseHTML: (element) => {
611
+ const role = element.getAttribute("data-role");
612
+ return role && MENTION_ROLE_PATTERN.test(role) ? role : null;
613
+ },
614
+ renderHTML: (attributes) => {
615
+ if (!attributes.assetRole) return {};
616
+ return { "data-role": attributes.assetRole };
617
+ }
604
618
  }
605
619
  };
606
620
  }
607
621
  });
622
+ var MENTION_ROLE_PATTERN = /^[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9])?$/;
623
+ function getMentionNodes(editor) {
624
+ const result = [];
625
+ editor.state.doc.descendants((node, pos) => {
626
+ if (node.type?.name !== "mention") return;
627
+ result.push({
628
+ pos,
629
+ id: node.attrs.id ?? "",
630
+ label: node.attrs.label ?? "",
631
+ ...node.attrs.type ? { type: node.attrs.type } : {},
632
+ ...node.attrs.assetRole ? { role: node.attrs.assetRole } : {}
633
+ });
634
+ });
635
+ return result;
636
+ }
637
+ function setMentionRoleAt(editor, pos, role) {
638
+ const node = editor?.state?.doc?.nodeAt(pos);
639
+ if (!node || node.type.name !== "mention") return false;
640
+ if (role !== null && !MENTION_ROLE_PATTERN.test(role)) return false;
641
+ const tr = editor.state.tr.setNodeMarkup(pos, void 0, { ...node.attrs, assetRole: role });
642
+ editor.view.dispatch(tr);
643
+ return true;
644
+ }
608
645
  function createMentionExtension(options = {}) {
609
646
  const { HTMLAttributes = {}, ...rest } = options;
610
647
  return Mention.configure({
@@ -2249,7 +2286,13 @@ var ImportAdapterRegistry = class {
2249
2286
  { fileName }
2250
2287
  );
2251
2288
  }
2252
- const context = createImportContext(input, { fileName, mimeType, target: adapter.target, registry: this });
2289
+ const context = createImportContext(input, {
2290
+ fileName,
2291
+ mimeType,
2292
+ target: adapter.target,
2293
+ registry: this,
2294
+ options: options.adapterOptions?.[adapter.id]
2295
+ });
2253
2296
  try {
2254
2297
  const result = await adapter.import(input, context);
2255
2298
  return { adapterId: adapter.id, target: adapter.target, fileName, mimeType, result };
@@ -3348,7 +3391,10 @@ async function importFilesIntoEditor(target, files, options = {}) {
3348
3391
  let at = options.at;
3349
3392
  for (const file of Array.from(files)) {
3350
3393
  try {
3351
- const outcome = await registry.importFile(file, { target: "document" });
3394
+ const outcome = await registry.importFile(file, {
3395
+ target: "document",
3396
+ adapterOptions: options.adapterOptions
3397
+ });
3352
3398
  const view = toView(target);
3353
3399
  if (view.isDestroyed) break;
3354
3400
  at = insertImportedDocument(view, outcome.result, { at, asPlainText: options.asPlainText });
@@ -3382,6 +3428,7 @@ var FileImport = Extension8.create({
3382
3428
  addOptions() {
3383
3429
  return {
3384
3430
  registry: void 0,
3431
+ adapterOptions: void 0,
3385
3432
  asPlainText: false,
3386
3433
  enableDrop: true,
3387
3434
  enablePaste: true,
@@ -3397,6 +3444,7 @@ var FileImport = Extension8.create({
3397
3444
  registry: registry(),
3398
3445
  at,
3399
3446
  asPlainText: options.asPlainText,
3447
+ adapterOptions: options.adapterOptions,
3400
3448
  onImport: options.onImport,
3401
3449
  onError: options.onImportError
3402
3450
  });
@@ -6469,6 +6517,869 @@ function safeClipboard(get) {
6469
6517
  }
6470
6518
  }
6471
6519
 
6520
+ // src/extensions/paragraph-styles/extension.ts
6521
+ import { Extension as Extension13, Mark as Mark5, mergeAttributes as mergeAttributes7 } from "@tiptap/core";
6522
+ import { Plugin as Plugin15, PluginKey as PluginKey12 } from "@tiptap/pm/state";
6523
+
6524
+ // src/extensions/paragraph-styles/registry.ts
6525
+ var MAX_CHAIN = 32;
6526
+ var STYLE_ID = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
6527
+ var UNSAFE_CSS = /[;{}<>\\]|\/\*|url\s*\(|expression\s*\(|@import|javascript:/i;
6528
+ var NORMAL_STYLE_ID = "Normal";
6529
+ var PREVIEW_PROPERTIES = /* @__PURE__ */ new Set([
6530
+ "font-family",
6531
+ "font-weight",
6532
+ "font-style",
6533
+ "color",
6534
+ "text-decoration-line",
6535
+ "text-transform",
6536
+ "letter-spacing",
6537
+ "font-variant-caps"
6538
+ ]);
6539
+ function isValidStyleId(id) {
6540
+ return typeof id === "string" && STYLE_ID.test(id);
6541
+ }
6542
+ var MUTED = "var(--pixerate-style-muted, #6b7280)";
6543
+ var HEADING_SIZES = ["2em", "1.5em", "1.25em", "1.1em", "1em", "0.9em"];
6544
+ function builtinStyles() {
6545
+ const styles = [
6546
+ {
6547
+ id: NORMAL_STYLE_ID,
6548
+ name: "Normal",
6549
+ kind: "paragraph",
6550
+ block: { type: "paragraph" },
6551
+ priority: 0,
6552
+ quick: true,
6553
+ builtin: true
6554
+ },
6555
+ {
6556
+ id: "Title",
6557
+ name: "Title",
6558
+ kind: "paragraph",
6559
+ basedOn: NORMAL_STYLE_ID,
6560
+ next: NORMAL_STYLE_ID,
6561
+ linked: "TitleChar",
6562
+ paragraph: { spaceAfter: "0.25em", lineHeight: "1.15" },
6563
+ character: { fontSize: "2.5em", bold: true, letterSpacing: "-0.01em" },
6564
+ priority: 10,
6565
+ quick: true,
6566
+ builtin: true
6567
+ },
6568
+ {
6569
+ id: "Subtitle",
6570
+ name: "Subtitle",
6571
+ kind: "paragraph",
6572
+ basedOn: NORMAL_STYLE_ID,
6573
+ next: NORMAL_STYLE_ID,
6574
+ linked: "SubtitleChar",
6575
+ paragraph: { spaceAfter: "0.75em" },
6576
+ character: { fontSize: "1.25em", color: MUTED },
6577
+ priority: 11,
6578
+ quick: true,
6579
+ builtin: true
6580
+ }
6581
+ ];
6582
+ HEADING_SIZES.forEach((fontSize, i) => {
6583
+ const level = i + 1;
6584
+ styles.push({
6585
+ id: `Heading${level}`,
6586
+ name: `Heading ${level}`,
6587
+ kind: "paragraph",
6588
+ basedOn: NORMAL_STYLE_ID,
6589
+ next: NORMAL_STYLE_ID,
6590
+ linked: `Heading${level}Char`,
6591
+ block: { type: "heading", attrs: { level } },
6592
+ paragraph: { spaceBefore: level <= 2 ? "1em" : "0.75em", spaceAfter: "0.25em" },
6593
+ character: { fontSize, bold: true },
6594
+ priority: 11 + level,
6595
+ quick: level <= 3,
6596
+ builtin: true
6597
+ });
6598
+ });
6599
+ styles.push(
6600
+ {
6601
+ id: "Quote",
6602
+ name: "Quote",
6603
+ kind: "paragraph",
6604
+ basedOn: NORMAL_STYLE_ID,
6605
+ next: NORMAL_STYLE_ID,
6606
+ linked: "QuoteChar",
6607
+ paragraph: { indentLeft: "1em", paddingLeft: "0.75em", borderLeft: "3px solid currentColor" },
6608
+ character: { italic: true, color: MUTED },
6609
+ priority: 29,
6610
+ quick: true,
6611
+ builtin: true
6612
+ },
6613
+ {
6614
+ id: "Code",
6615
+ name: "Code",
6616
+ kind: "paragraph",
6617
+ basedOn: NORMAL_STYLE_ID,
6618
+ next: NORMAL_STYLE_ID,
6619
+ block: { type: "codeBlock" },
6620
+ character: {
6621
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
6622
+ fontSize: "0.9em"
6623
+ },
6624
+ priority: 30,
6625
+ quick: true,
6626
+ builtin: true
6627
+ },
6628
+ {
6629
+ id: "ListParagraph",
6630
+ name: "List Paragraph",
6631
+ kind: "paragraph",
6632
+ basedOn: NORMAL_STYLE_ID,
6633
+ paragraph: { indentLeft: "2em" },
6634
+ priority: 34,
6635
+ quick: true,
6636
+ builtin: true
6637
+ }
6638
+ );
6639
+ const linked = ["Title", "Subtitle", "Quote", ...HEADING_SIZES.map((_, i) => `Heading${i + 1}`)];
6640
+ linked.forEach((paragraphId) => {
6641
+ const name = styles.find((s) => s.id === paragraphId).name;
6642
+ styles.push({
6643
+ id: `${paragraphId}Char`,
6644
+ name: `${name} Char`,
6645
+ kind: "character",
6646
+ linked: paragraphId,
6647
+ priority: 99,
6648
+ hidden: true,
6649
+ builtin: true
6650
+ });
6651
+ });
6652
+ styles.push(
6653
+ {
6654
+ id: "Emphasis",
6655
+ name: "Emphasis",
6656
+ kind: "character",
6657
+ character: { italic: true },
6658
+ priority: 20,
6659
+ quick: true,
6660
+ builtin: true
6661
+ },
6662
+ {
6663
+ id: "Strong",
6664
+ name: "Strong",
6665
+ kind: "character",
6666
+ character: { bold: true },
6667
+ priority: 22,
6668
+ quick: true,
6669
+ builtin: true
6670
+ }
6671
+ );
6672
+ return styles;
6673
+ }
6674
+ var DEFAULT_STYLES = Object.freeze(builtinStyles());
6675
+ var clone = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
6676
+ function overlay(base, patch) {
6677
+ if (!patch) return base;
6678
+ const out = { ...base };
6679
+ Object.entries(patch).forEach(([key, value]) => {
6680
+ if (value !== void 0 && value !== null) out[key] = value;
6681
+ });
6682
+ return out;
6683
+ }
6684
+ function sameBlock(a, type, attrs) {
6685
+ if (a.type !== type) return false;
6686
+ return Object.entries(a.attrs ?? {}).every(([key, value]) => attrs[key] === value);
6687
+ }
6688
+ var StyleRegistry = class {
6689
+ styles = /* @__PURE__ */ new Map();
6690
+ defaults;
6691
+ version = 0;
6692
+ snapshot = null;
6693
+ listeners = /* @__PURE__ */ new Set();
6694
+ constructor(options = {}) {
6695
+ this.defaults = clone(options.defaults ?? {});
6696
+ if (options.builtins !== false) DEFAULT_STYLES.forEach((style) => this.styles.set(style.id, clone(style)));
6697
+ (options.styles ?? []).forEach((style) => this.put(style));
6698
+ }
6699
+ // -- Reading --------------------------------------------------------------
6700
+ get(id) {
6701
+ return id ? this.styles.get(id) : void 0;
6702
+ }
6703
+ /** Finds a style by id or display name (case-insensitive). */
6704
+ find(nameOrId) {
6705
+ const direct = this.styles.get(nameOrId);
6706
+ if (direct) return direct;
6707
+ const lower = nameOrId.toLowerCase();
6708
+ return this.list().find((style) => style.name.toLowerCase() === lower);
6709
+ }
6710
+ /** All styles (optionally of one kind) in gallery order. */
6711
+ list(kind) {
6712
+ return Array.from(this.styles.values()).filter((style) => !kind || style.kind === kind).sort((a, b) => (a.priority ?? 99) - (b.priority ?? 99) || a.name.localeCompare(b.name));
6713
+ }
6714
+ /** Styles shown in the gallery (`quick`, not hidden), paragraph styles by default. */
6715
+ gallery(kind = "paragraph") {
6716
+ return this.list(kind).filter((style) => style.quick && !style.hidden);
6717
+ }
6718
+ /** The `basedOn` chain from the root style down to `id` (cycle-safe, bounded). */
6719
+ chain(id) {
6720
+ const out = [];
6721
+ let current = this.styles.get(id);
6722
+ while (current && out.length < MAX_CHAIN && !out.includes(current)) {
6723
+ out.push(current);
6724
+ current = current.basedOn ? this.styles.get(current.basedOn) : void 0;
6725
+ }
6726
+ return out.reverse();
6727
+ }
6728
+ /**
6729
+ * Resolves a paragraph style: defaults, then each style of the chain (base
6730
+ * first). Unknown ids resolve to the defaults in a `paragraph` block.
6731
+ */
6732
+ resolve(id) {
6733
+ let paragraph = overlay({}, this.defaults.paragraph);
6734
+ let character = overlay({}, this.defaults.character);
6735
+ let block = { type: "paragraph" };
6736
+ this.chain(id).forEach((style) => {
6737
+ if (style.kind !== "paragraph") return;
6738
+ paragraph = overlay(paragraph, style.paragraph);
6739
+ character = overlay(character, style.character);
6740
+ if (style.block) block = clone(style.block);
6741
+ });
6742
+ return { id, paragraph, character, block, next: this.nextStyleId(id) };
6743
+ }
6744
+ /** Character formatting a paragraph style's chain sets (without defaults). */
6745
+ paragraphCharacter(id) {
6746
+ let character = {};
6747
+ this.chain(id).forEach((style) => {
6748
+ if (style.kind === "paragraph") character = overlay(character, style.character);
6749
+ });
6750
+ return character;
6751
+ }
6752
+ /**
6753
+ * Character formatting of a character style's chain. A style linked to a
6754
+ * paragraph style takes that style's character formatting first.
6755
+ */
6756
+ resolveCharacterStyle(id) {
6757
+ let character = {};
6758
+ this.chain(id).forEach((style) => {
6759
+ if (style.kind === "character" && style.linked && this.get(style.linked)?.kind === "paragraph") {
6760
+ character = overlay(character, this.paragraphCharacter(style.linked));
6761
+ }
6762
+ character = overlay(character, style.character);
6763
+ });
6764
+ return character;
6765
+ }
6766
+ /**
6767
+ * Character formatting of text: defaults → paragraph style chain →
6768
+ * character style chain → direct formatting.
6769
+ */
6770
+ resolveText(input) {
6771
+ let character = this.resolve(input.paragraphStyle || NORMAL_STYLE_ID).character;
6772
+ if (input.characterStyle) character = overlay(character, this.resolveCharacterStyle(input.characterStyle));
6773
+ return overlay(character, input.direct);
6774
+ }
6775
+ /** The style after Enter at the end of a paragraph of `id` (itself when unset or unknown). */
6776
+ nextStyleId(id) {
6777
+ const next = this.styles.get(id)?.next;
6778
+ return next && this.styles.get(next)?.kind === "paragraph" ? next : id;
6779
+ }
6780
+ /** The linked character style of a paragraph style, if any. */
6781
+ linkedCharacterStyle(id) {
6782
+ const style = this.styles.get(id);
6783
+ const linked = style?.linked ? this.styles.get(style.linked) : void 0;
6784
+ return style?.kind === "paragraph" && linked?.kind === "character" ? linked : void 0;
6785
+ }
6786
+ /**
6787
+ * The style a block uses when it names none: Normal for paragraphs,
6788
+ * Heading N for headings of level N, Code for code blocks (the built-in
6789
+ * style whose own `block` matches).
6790
+ */
6791
+ defaultStyleFor(type, attrs = {}) {
6792
+ if (type === "paragraph" && this.styles.get(NORMAL_STYLE_ID)) return NORMAL_STYLE_ID;
6793
+ const candidates = this.list("paragraph").filter(
6794
+ (style) => style.block && sameBlock(style.block, type, attrs)
6795
+ );
6796
+ return (candidates.find((s) => s.builtin) ?? candidates[0])?.id ?? null;
6797
+ }
6798
+ /** The style of a block: its `styleId` when known, otherwise the block's default style. */
6799
+ styleOfBlock(type, attrs = {}) {
6800
+ const id = attrs.styleId;
6801
+ if (id && this.styles.get(id)?.kind === "paragraph") return id;
6802
+ return this.defaultStyleFor(type, attrs);
6803
+ }
6804
+ getSnapshot = () => {
6805
+ if (!this.snapshot) {
6806
+ this.snapshot = Object.freeze({
6807
+ styles: Object.freeze(this.list()),
6808
+ defaults: this.defaults,
6809
+ version: this.version
6810
+ });
6811
+ }
6812
+ return this.snapshot;
6813
+ };
6814
+ subscribe = (listener) => {
6815
+ this.listeners.add(listener);
6816
+ listener(this.getSnapshot());
6817
+ return () => {
6818
+ this.listeners.delete(listener);
6819
+ };
6820
+ };
6821
+ toJSON() {
6822
+ return { styles: clone(Array.from(this.styles.values())), defaults: clone(this.defaults) };
6823
+ }
6824
+ // -- Writing --------------------------------------------------------------
6825
+ /** Adds or replaces a style. Throws on an invalid id. */
6826
+ upsert(style) {
6827
+ const stored = this.put(style);
6828
+ this.changed();
6829
+ return stored;
6830
+ }
6831
+ /** Merges `patch` into a style (formats are merged key by key). Returns `undefined` for unknown ids. */
6832
+ update(id, patch) {
6833
+ const current = this.styles.get(id);
6834
+ if (!current) return void 0;
6835
+ const next = {
6836
+ ...current,
6837
+ ...patch,
6838
+ id,
6839
+ paragraph: patch.paragraph ? { ...current.paragraph, ...patch.paragraph } : current.paragraph,
6840
+ character: patch.character ? { ...current.character, ...patch.character } : current.character
6841
+ };
6842
+ return this.upsert(next);
6843
+ }
6844
+ /**
6845
+ * Removes a style and returns it. Built-in styles cannot be removed. Styles
6846
+ * based on it are rebased onto its own base, and `next`/`linked`
6847
+ * references to it are dropped.
6848
+ */
6849
+ remove(id) {
6850
+ const gone = this.styles.get(id);
6851
+ if (!gone || gone.builtin) return void 0;
6852
+ this.styles.delete(id);
6853
+ this.styles.forEach((style, key) => {
6854
+ let changed = style;
6855
+ if (style.basedOn === id) {
6856
+ changed = { ...changed, basedOn: gone.basedOn && gone.basedOn !== key ? gone.basedOn : null };
6857
+ }
6858
+ if (style.next === id) changed = { ...changed, next: null };
6859
+ if (style.linked === id) changed = { ...changed, linked: null };
6860
+ if (changed !== style) this.styles.set(key, changed);
6861
+ });
6862
+ this.changed();
6863
+ return gone;
6864
+ }
6865
+ /** Replaces the document defaults. */
6866
+ setDefaults(defaults) {
6867
+ this.defaults = clone(defaults);
6868
+ this.changed();
6869
+ }
6870
+ /** An unused id derived from a display name (`My Style` → `MyStyle`, then `MyStyle1`, …). */
6871
+ newId(name) {
6872
+ let base = name.replace(/[^A-Za-z0-9_-]+/g, "").replace(/^[^A-Za-z]+/, "").slice(0, 56);
6873
+ if (!base) base = "Style";
6874
+ if (!this.styles.has(base)) return base;
6875
+ for (let i = 1; i < 1e5; i++) {
6876
+ const id = `${base}${i}`;
6877
+ if (!this.styles.has(id)) return id;
6878
+ }
6879
+ return `${base}${Date.now()}`;
6880
+ }
6881
+ // -- CSS --------------------------------------------------------------------
6882
+ /** CSS declarations (`property: value`) of a resolved paragraph or character style. */
6883
+ cssDeclarations(id) {
6884
+ const style = this.styles.get(id);
6885
+ if (style?.kind === "character") return characterCSS(this.resolveCharacterStyle(id));
6886
+ const resolved = this.resolve(id);
6887
+ return { ...paragraphCSS(resolved.paragraph), ...characterCSS(resolved.character) };
6888
+ }
6889
+ /**
6890
+ * Declarations for previewing a style in a menu or gallery: font family,
6891
+ * weight, style, color, decoration, case and letter spacing (no sizes,
6892
+ * margins or borders).
6893
+ */
6894
+ previewDeclarations(id) {
6895
+ return Object.fromEntries(
6896
+ Object.entries(this.cssDeclarations(id)).filter(([property]) => PREVIEW_PROPERTIES.has(property))
6897
+ );
6898
+ }
6899
+ /**
6900
+ * A stylesheet for the styles: `[data-style="Id"]` rules for paragraph
6901
+ * styles (plus the plain element of blocks that use their default style,
6902
+ * e.g. `h2:not([data-style])`), `[data-char-style="Id"]` for character
6903
+ * styles and the defaults on the scope element.
6904
+ */
6905
+ toCSS(options = {}) {
6906
+ const scope = options.scope ?? ".ProseMirror";
6907
+ const tagFor = options.tagFor ?? defaultTagFor;
6908
+ const rules = [];
6909
+ const rule = (selectors, declarations) => {
6910
+ const body = Object.entries(declarations).map(([property, value]) => `${property}: ${value};`).join(" ");
6911
+ if (body) rules.push(`${selectors.join(", ")} { ${body} }`);
6912
+ };
6913
+ rule([scope], {
6914
+ ...paragraphCSS(this.defaults.paragraph ?? {}),
6915
+ ...characterCSS(this.defaults.character ?? {})
6916
+ });
6917
+ this.list().forEach((style) => {
6918
+ if (style.kind === "character") {
6919
+ rule([`${scope} [data-char-style="${style.id}"]`], this.cssDeclarations(style.id));
6920
+ return;
6921
+ }
6922
+ const selectors = [`${scope} [data-style="${style.id}"]`];
6923
+ const block = this.resolve(style.id).block;
6924
+ if (this.defaultStyleFor(block.type, block.attrs ?? {}) === style.id) {
6925
+ const tag = tagFor(block);
6926
+ if (tag) selectors.push(`${scope} ${tag}:not([data-style])`);
6927
+ }
6928
+ const resolved = this.resolve(style.id);
6929
+ const own = {
6930
+ ...paragraphCSS(withoutDefaults(resolved.paragraph, this.defaults.paragraph)),
6931
+ ...characterCSS(withoutDefaults(resolved.character, this.defaults.character))
6932
+ };
6933
+ rule(selectors, own);
6934
+ });
6935
+ return rules.join("\n");
6936
+ }
6937
+ // -- Internals --------------------------------------------------------------
6938
+ put(style) {
6939
+ if (!isValidStyleId(style.id)) throw new Error(`Invalid style id: ${String(style.id)}`);
6940
+ if (style.kind !== "paragraph" && style.kind !== "character") {
6941
+ throw new Error(`Invalid style kind: ${String(style.kind)}`);
6942
+ }
6943
+ const stored = clone({ ...style, name: style.name || style.id });
6944
+ this.styles.set(style.id, stored);
6945
+ return stored;
6946
+ }
6947
+ changed() {
6948
+ this.version++;
6949
+ this.snapshot = null;
6950
+ const snapshot = this.getSnapshot();
6951
+ this.listeners.forEach((listener) => {
6952
+ try {
6953
+ listener(snapshot);
6954
+ } catch (err) {
6955
+ console.error("[StyleRegistry] Error in listener:", err);
6956
+ }
6957
+ });
6958
+ }
6959
+ };
6960
+ function withoutDefaults(format, defaults) {
6961
+ if (!defaults) return format;
6962
+ const out = {};
6963
+ Object.entries(format).forEach(([key, value]) => {
6964
+ if (defaults[key] !== value) out[key] = value;
6965
+ });
6966
+ return out;
6967
+ }
6968
+ function defaultTagFor(block) {
6969
+ if (block.type === "paragraph") return "p";
6970
+ if (block.type === "heading") {
6971
+ const level = Number(block.attrs?.level);
6972
+ return level >= 1 && level <= 6 ? `h${level}` : null;
6973
+ }
6974
+ if (block.type === "codeBlock") return "pre";
6975
+ return null;
6976
+ }
6977
+ function safeCSSValue(value) {
6978
+ if (typeof value !== "string") return void 0;
6979
+ const trimmed = value.trim();
6980
+ if (!trimmed || trimmed.length > 200 || UNSAFE_CSS.test(trimmed)) return void 0;
6981
+ return trimmed;
6982
+ }
6983
+ function paragraphCSS(format) {
6984
+ const out = {};
6985
+ const set = (property, value) => {
6986
+ const safe = safeCSSValue(value);
6987
+ if (safe) out[property] = safe;
6988
+ };
6989
+ set("text-align", format.textAlign);
6990
+ set("line-height", format.lineHeight);
6991
+ set("margin-top", format.spaceBefore);
6992
+ set("margin-bottom", format.spaceAfter);
6993
+ set("margin-left", format.indentLeft);
6994
+ set("margin-right", format.indentRight);
6995
+ set("text-indent", format.indentFirstLine);
6996
+ set("border-left", format.borderLeft);
6997
+ set("padding-left", format.paddingLeft);
6998
+ set("background-color", format.backgroundColor);
6999
+ return out;
7000
+ }
7001
+ function characterCSS(format) {
7002
+ const out = {};
7003
+ const set = (property, value) => {
7004
+ const safe = safeCSSValue(value);
7005
+ if (safe) out[property] = safe;
7006
+ };
7007
+ set("font-family", format.fontFamily);
7008
+ set("font-size", format.fontSize);
7009
+ if (format.bold !== void 0) out["font-weight"] = format.bold ? "700" : "400";
7010
+ if (format.italic !== void 0) out["font-style"] = format.italic ? "italic" : "normal";
7011
+ const lines = [format.underline && "underline", format.strike && "line-through"].filter(Boolean);
7012
+ if (format.underline !== void 0 || format.strike !== void 0) {
7013
+ out["text-decoration-line"] = lines.length ? lines.join(" ") : "none";
7014
+ }
7015
+ set("color", format.color);
7016
+ set("background-color", format.backgroundColor);
7017
+ set("letter-spacing", format.letterSpacing);
7018
+ set("text-transform", format.textTransform);
7019
+ if (format.smallCaps !== void 0) out["font-variant-caps"] = format.smallCaps ? "small-caps" : "normal";
7020
+ return out;
7021
+ }
7022
+ function createStyleRegistry(options = {}) {
7023
+ return new StyleRegistry(options);
7024
+ }
7025
+
7026
+ // src/extensions/paragraph-styles/extension.ts
7027
+ var STYLE_ID_ATTRIBUTE = "styleId";
7028
+ var CHARACTER_STYLE_MARK_NAME = "characterStyle";
7029
+ var paragraphStylesPluginKey = new PluginKey12("paragraphStyles");
7030
+ function getStyleRegistry(editor) {
7031
+ return editor?.storage?.paragraphStyles?.registry ?? null;
7032
+ }
7033
+ function headingLevels(editor) {
7034
+ const heading = editor?.extensionManager?.extensions.find((e) => e.name === "heading");
7035
+ const levels = heading?.options?.levels;
7036
+ return Array.isArray(levels) ? levels : null;
7037
+ }
7038
+ function isParagraphStyleAvailable(editor, registry, id) {
7039
+ const style = registry.get(id);
7040
+ if (!style || style.kind !== "paragraph") return false;
7041
+ const block = registry.resolve(id).block;
7042
+ if (!editor.schema.nodes[block.type]) return false;
7043
+ if (block.type === "heading") {
7044
+ const levels = headingLevels(editor);
7045
+ if (levels && !levels.includes(Number(block.attrs?.level))) return false;
7046
+ }
7047
+ return true;
7048
+ }
7049
+ function getParagraphStyleAt(state, registry, pos = state.selection.from) {
7050
+ const $pos = state.doc.resolve(Math.max(0, Math.min(pos, state.doc.content.size)));
7051
+ const block = $pos.parent;
7052
+ if (!block.isTextblock) return null;
7053
+ return registry.styleOfBlock(block.type.name, block.attrs);
7054
+ }
7055
+ var MARK_FORMAT = {
7056
+ bold: "bold",
7057
+ italic: "italic",
7058
+ underline: "underline",
7059
+ strike: "strike"
7060
+ };
7061
+ function selectionMarks(state) {
7062
+ const { from, to, empty, $from } = state.selection;
7063
+ if (empty) return state.storedMarks ?? $from.marks();
7064
+ let common = null;
7065
+ state.doc.nodesBetween(from, to, (node) => {
7066
+ if (!node.isText) return true;
7067
+ common = common === null ? node.marks : common.filter((m) => m.isInSet(node.marks));
7068
+ return false;
7069
+ });
7070
+ return common ?? $from.marks();
7071
+ }
7072
+ function captureSelectionFormat(state) {
7073
+ const character = {};
7074
+ selectionMarks(state).forEach((mark) => {
7075
+ const name = mark.type.name;
7076
+ if (MARK_FORMAT[name]) character[MARK_FORMAT[name]] = true;
7077
+ if (name === "textStyle") {
7078
+ if (mark.attrs.fontFamily) character.fontFamily = mark.attrs.fontFamily;
7079
+ if (mark.attrs.fontSize) character.fontSize = mark.attrs.fontSize;
7080
+ if (mark.attrs.color) character.color = mark.attrs.color;
7081
+ }
7082
+ if (name === "highlight") character.backgroundColor = mark.attrs.color || "#fef08a";
7083
+ });
7084
+ const paragraph = {};
7085
+ const align = state.selection.$from.parent.attrs?.textAlign;
7086
+ if (align && align !== "left") paragraph.textAlign = align;
7087
+ return { paragraph, character };
7088
+ }
7089
+ function carriedAttributes(node, type) {
7090
+ const spec = type.spec.attrs ?? {};
7091
+ return Object.fromEntries(Object.entries(node.attrs).filter(([key]) => key in spec));
7092
+ }
7093
+ function applyStyleToBlock(tr, pos, id, registry) {
7094
+ const node = tr.doc.nodeAt(pos);
7095
+ if (!node || !node.isTextblock) return false;
7096
+ const resolved = registry.resolve(id);
7097
+ const type = tr.doc.type.schema.nodes[resolved.block.type];
7098
+ if (!type) return false;
7099
+ const blockAttrs = resolved.block.attrs ?? {};
7100
+ const isDefault = registry.defaultStyleFor(resolved.block.type, blockAttrs) === id;
7101
+ const attrs = { ...carriedAttributes(node, type), ...blockAttrs };
7102
+ if (STYLE_ID_ATTRIBUTE in (type.spec.attrs ?? {})) attrs[STYLE_ID_ATTRIBUTE] = isDefault ? null : id;
7103
+ if ("textAlign" in (type.spec.attrs ?? {})) {
7104
+ const fallback = type.spec.attrs.textAlign.default ?? null;
7105
+ attrs.textAlign = fallback === null ? null : resolved.paragraph.textAlign ?? fallback;
7106
+ }
7107
+ if (node.type === type && Object.entries(attrs).every(([k, v]) => node.attrs[k] === v)) return false;
7108
+ try {
7109
+ tr.setNodeMarkup(pos, type, attrs);
7110
+ } catch {
7111
+ try {
7112
+ tr.setBlockType(pos + 1, pos + 1, type, attrs);
7113
+ } catch {
7114
+ return false;
7115
+ }
7116
+ }
7117
+ return true;
7118
+ }
7119
+ function selectedBlocks(state) {
7120
+ const { from, to } = state.selection;
7121
+ const positions = [];
7122
+ state.doc.nodesBetween(from, to, (node, pos) => {
7123
+ if (node.isTextblock) {
7124
+ positions.push(pos);
7125
+ return false;
7126
+ }
7127
+ return true;
7128
+ });
7129
+ if (positions.length === 0 && state.selection.$from.parent.isTextblock) {
7130
+ positions.push(state.selection.$from.before());
7131
+ }
7132
+ return positions;
7133
+ }
7134
+ function setParagraphStyle(tr, state, id, registry) {
7135
+ const start2 = tr.steps.length;
7136
+ selectedBlocks(state).forEach((pos) => {
7137
+ applyStyleToBlock(tr, tr.mapping.slice(start2).map(pos), id, registry);
7138
+ });
7139
+ }
7140
+ function nextStyleTransaction(transactions, oldState, newState, registry) {
7141
+ if (transactions.length !== 1) return null;
7142
+ const [tr] = transactions;
7143
+ if (!tr.docChanged || tr.getMeta("uiEvent") || tr.getMeta("history$") || tr.getMeta("y-sync$")) {
7144
+ return null;
7145
+ }
7146
+ const old = oldState.selection;
7147
+ const $old = old.$from;
7148
+ if (!old.empty || !$old.parent.isTextblock || $old.parent.type.spec.code) return null;
7149
+ if ($old.parentOffset !== $old.parent.content.size) return null;
7150
+ if (newState.doc.content.size !== oldState.doc.content.size + 2) return null;
7151
+ const $new = newState.selection.$from;
7152
+ if (!newState.selection.empty || !$new.parent.isTextblock || $new.parent.content.size !== 0) return null;
7153
+ const depth = $new.depth;
7154
+ const index = $new.index(depth - 1);
7155
+ if (index === 0 || $new.depth !== $old.depth) return null;
7156
+ const previous = $new.node(depth - 1).child(index - 1);
7157
+ if (!previous.isTextblock || !previous.eq($old.parent)) return null;
7158
+ const style = registry.styleOfBlock($old.parent.type.name, $old.parent.attrs);
7159
+ if (!style) return null;
7160
+ const next = registry.nextStyleId(style);
7161
+ const current = registry.styleOfBlock($new.parent.type.name, $new.parent.attrs);
7162
+ const target = registry.resolve(next).block;
7163
+ const typeMatches = $new.parent.type.name === target.type && Object.entries(target.attrs ?? {}).every(([k, v]) => $new.parent.attrs[k] === v);
7164
+ if (current === next && typeMatches) return null;
7165
+ const out = newState.tr;
7166
+ if (!applyStyleToBlock(out, $new.before(), next, registry)) return null;
7167
+ return out;
7168
+ }
7169
+ var CharacterStyle = Mark5.create({
7170
+ name: CHARACTER_STYLE_MARK_NAME,
7171
+ addAttributes() {
7172
+ return {
7173
+ [STYLE_ID_ATTRIBUTE]: {
7174
+ default: null,
7175
+ parseHTML: (el) => {
7176
+ const id = el.getAttribute("data-char-style");
7177
+ return isValidStyleId(id) ? id : null;
7178
+ },
7179
+ renderHTML: (attrs) => attrs[STYLE_ID_ATTRIBUTE] ? { "data-char-style": attrs[STYLE_ID_ATTRIBUTE] } : {}
7180
+ }
7181
+ };
7182
+ },
7183
+ parseHTML() {
7184
+ return [{ tag: "span[data-char-style]" }];
7185
+ },
7186
+ renderHTML({ HTMLAttributes }) {
7187
+ return ["span", mergeAttributes7(HTMLAttributes), 0];
7188
+ }
7189
+ });
7190
+ var ParagraphStyles = Extension13.create({
7191
+ name: "paragraphStyles",
7192
+ addOptions() {
7193
+ return {
7194
+ registry: null,
7195
+ types: ["paragraph", "heading", "codeBlock"],
7196
+ injectCSS: true,
7197
+ cssScope: ".ProseMirror",
7198
+ cssNonce: void 0,
7199
+ linkedCharacterStyles: true
7200
+ };
7201
+ },
7202
+ addStorage() {
7203
+ return { registry: this.options.registry ?? createStyleRegistry() };
7204
+ },
7205
+ addExtensions() {
7206
+ return [CharacterStyle];
7207
+ },
7208
+ addGlobalAttributes() {
7209
+ return [
7210
+ {
7211
+ types: this.options.types,
7212
+ attributes: {
7213
+ [STYLE_ID_ATTRIBUTE]: {
7214
+ default: null,
7215
+ keepOnSplit: true,
7216
+ parseHTML: (el) => {
7217
+ const id = el.getAttribute("data-style");
7218
+ return isValidStyleId(id) ? id : null;
7219
+ },
7220
+ renderHTML: (attrs) => {
7221
+ const id = attrs[STYLE_ID_ATTRIBUTE];
7222
+ return isValidStyleId(id) ? { "data-style": id, class: `pixerate-style-${id}` } : {};
7223
+ }
7224
+ }
7225
+ }
7226
+ }
7227
+ ];
7228
+ },
7229
+ addCommands() {
7230
+ const registry = () => this.storage.registry;
7231
+ const editor = () => this.editor;
7232
+ return {
7233
+ setParagraphStyle: (id) => ({ state, tr, dispatch }) => {
7234
+ const reg = registry();
7235
+ if (!isParagraphStyleAvailable(editor(), reg, id)) return false;
7236
+ const { $from, $to, empty } = state.selection;
7237
+ const linked = this.options.linkedCharacterStyles ? reg.linkedCharacterStyle(id) : void 0;
7238
+ const partial = !empty && $from.sameParent($to) && $from.parent.isTextblock && ($from.parentOffset > 0 || $to.parentOffset < $from.parent.content.size);
7239
+ const markType = state.schema.marks[CHARACTER_STYLE_MARK_NAME];
7240
+ if (partial && linked && markType) {
7241
+ if (dispatch) {
7242
+ tr.addMark($from.pos, $to.pos, markType.create({ [STYLE_ID_ATTRIBUTE]: linked.id }));
7243
+ }
7244
+ return true;
7245
+ }
7246
+ if (dispatch) setParagraphStyle(tr, state, id, reg);
7247
+ return true;
7248
+ },
7249
+ clearParagraphStyle: () => ({ commands }) => commands.setParagraphStyle(NORMAL_STYLE_ID),
7250
+ setCharacterStyle: (id) => ({ commands }) => {
7251
+ if (registry().get(id)?.kind !== "character") return false;
7252
+ return commands.setMark(CHARACTER_STYLE_MARK_NAME, { [STYLE_ID_ATTRIBUTE]: id });
7253
+ },
7254
+ unsetCharacterStyle: () => ({ commands }) => commands.unsetMark(CHARACTER_STYLE_MARK_NAME),
7255
+ updateParagraphStyleFromSelection: (id) => ({ state, dispatch }) => {
7256
+ const reg = registry();
7257
+ const target = id ?? getParagraphStyleAt(state, reg);
7258
+ const style = target ? reg.get(target) : void 0;
7259
+ if (!style || style.kind !== "paragraph") return false;
7260
+ if (dispatch) {
7261
+ const captured = captureSelectionFormat(state);
7262
+ reg.update(style.id, captured);
7263
+ }
7264
+ return true;
7265
+ },
7266
+ createParagraphStyleFromSelection: (name, options = {}) => ({ state, tr, dispatch }) => {
7267
+ const reg = registry();
7268
+ const trimmed = name.trim();
7269
+ if (!trimmed) return false;
7270
+ const id = options.id ?? reg.newId(trimmed);
7271
+ if (!isValidStyleId(id) || reg.get(id)) return false;
7272
+ const basedOn = options.basedOn === void 0 ? getParagraphStyleAt(state, reg) : options.basedOn;
7273
+ if (dispatch) {
7274
+ const captured = captureSelectionFormat(state);
7275
+ const definition = {
7276
+ id,
7277
+ name: trimmed,
7278
+ kind: "paragraph",
7279
+ basedOn: basedOn ?? NORMAL_STYLE_ID,
7280
+ paragraph: captured.paragraph,
7281
+ character: captured.character,
7282
+ priority: 50,
7283
+ quick: true
7284
+ };
7285
+ reg.upsert(definition);
7286
+ if (options.apply !== false) setParagraphStyle(tr, state, id, reg);
7287
+ }
7288
+ return true;
7289
+ }
7290
+ };
7291
+ },
7292
+ addProseMirrorPlugins() {
7293
+ const registry = this.storage.registry;
7294
+ return [
7295
+ new Plugin15({
7296
+ key: paragraphStylesPluginKey,
7297
+ appendTransaction: (transactions, oldState, newState) => nextStyleTransaction(transactions, oldState, newState, registry)
7298
+ })
7299
+ ];
7300
+ },
7301
+ onBeforeCreate() {
7302
+ if (!this.options.injectCSS || typeof document === "undefined") return;
7303
+ const element = document.createElement("style");
7304
+ element.setAttribute("data-pixerate-paragraph-styles", "");
7305
+ if (this.options.cssNonce) element.nonce = this.options.cssNonce;
7306
+ document.head.appendChild(element);
7307
+ const scope = this.options.cssScope;
7308
+ const unsubscribe = this.storage.registry.subscribe(() => {
7309
+ element.textContent = this.storage.registry.toCSS({ scope });
7310
+ });
7311
+ this.storage.cleanup = () => {
7312
+ unsubscribe();
7313
+ element.remove();
7314
+ };
7315
+ },
7316
+ onDestroy() {
7317
+ this.storage.cleanup?.();
7318
+ }
7319
+ });
7320
+ var EMPTY_STYLES = Object.freeze({
7321
+ gallery: [],
7322
+ styles: [],
7323
+ characterStyles: [],
7324
+ currentStyleId: null,
7325
+ version: -1
7326
+ });
7327
+ function createParagraphStylesStore(editor) {
7328
+ const registry = getStyleRegistry(editor);
7329
+ const listeners = /* @__PURE__ */ new Set();
7330
+ let snapshot = EMPTY_STYLES;
7331
+ let unsubscribeRegistry = null;
7332
+ const compute = () => {
7333
+ if (!editor || editor.isDestroyed || !registry) return EMPTY_STYLES;
7334
+ const { version } = registry.getSnapshot();
7335
+ const currentStyleId = getParagraphStyleAt(editor.state, registry);
7336
+ if (snapshot.version === version && snapshot.currentStyleId === currentStyleId) return snapshot;
7337
+ if (snapshot.version === version) return { ...snapshot, currentStyleId };
7338
+ const styles = registry.list("paragraph").filter((style) => !style.hidden && isParagraphStyleAvailable(editor, registry, style.id));
7339
+ return {
7340
+ gallery: styles.filter((style) => style.quick),
7341
+ styles,
7342
+ characterStyles: registry.list("character").filter((style) => !style.hidden),
7343
+ currentStyleId,
7344
+ version
7345
+ };
7346
+ };
7347
+ const update = () => {
7348
+ const next = compute();
7349
+ if (next !== snapshot) {
7350
+ snapshot = next;
7351
+ listeners.forEach((listener) => listener());
7352
+ }
7353
+ };
7354
+ const detach = () => {
7355
+ editor?.off("transaction", update);
7356
+ unsubscribeRegistry?.();
7357
+ unsubscribeRegistry = null;
7358
+ };
7359
+ return {
7360
+ getSnapshot: () => {
7361
+ if (!listeners.size) snapshot = compute();
7362
+ return snapshot;
7363
+ },
7364
+ subscribe(listener) {
7365
+ if (!listeners.size) {
7366
+ snapshot = compute();
7367
+ editor?.on("transaction", update);
7368
+ unsubscribeRegistry = registry ? registry.subscribe(update) : null;
7369
+ }
7370
+ listeners.add(listener);
7371
+ return () => {
7372
+ listeners.delete(listener);
7373
+ if (!listeners.size) detach();
7374
+ };
7375
+ },
7376
+ destroy() {
7377
+ detach();
7378
+ listeners.clear();
7379
+ }
7380
+ };
7381
+ }
7382
+
6472
7383
  // src/extensions/rich-text-preset.ts
6473
7384
  var resolveExt3 = (mod, name) => mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;
6474
7385
  var StarterKit = resolveExt3(_StarterKit, "StarterKit");
@@ -6491,7 +7402,7 @@ function createRichTextPreset(options = {}) {
6491
7402
  openLinksOnClick = false,
6492
7403
  enableSmilies = true,
6493
7404
  enableColorHighlighter = true,
6494
- headingLevels = [1, 2, 3, 4]
7405
+ headingLevels: headingLevels2 = [1, 2, 3, 4]
6495
7406
  } = options;
6496
7407
  const extensions2 = [
6497
7408
  StarterKit.configure({
@@ -6506,7 +7417,7 @@ function createRichTextPreset(options = {}) {
6506
7417
  }
6507
7418
  },
6508
7419
  heading: {
6509
- levels: headingLevels,
7420
+ levels: headingLevels2,
6510
7421
  HTMLAttributes: {
6511
7422
  class: "font-bold tracking-tight"
6512
7423
  }
@@ -6668,6 +7579,9 @@ function createRichTextPreset(options = {}) {
6668
7579
  if (options.findReplace) {
6669
7580
  extensions2.push(FindReplace.configure(optionObject(options.findReplace)));
6670
7581
  }
7582
+ if (options.paragraphStyles) {
7583
+ extensions2.push(ParagraphStyles.configure(optionObject(options.paragraphStyles)));
7584
+ }
6671
7585
  return extensions2;
6672
7586
  }
6673
7587
 
@@ -6820,6 +7734,8 @@ export {
6820
7734
  ColorHighlighter,
6821
7735
  FontSize,
6822
7736
  Mention,
7737
+ getMentionNodes,
7738
+ setMentionRoleAt,
6823
7739
  createMentionExtension,
6824
7740
  Markdown2 as Markdown,
6825
7741
  imagePastePluginKey,
@@ -6929,8 +7845,24 @@ export {
6929
7845
  FIND_OPTION_TOGGLES,
6930
7846
  formatFindStatus,
6931
7847
  FindReplace,
7848
+ NORMAL_STYLE_ID,
7849
+ isValidStyleId,
7850
+ DEFAULT_STYLES,
7851
+ StyleRegistry,
7852
+ safeCSSValue,
7853
+ createStyleRegistry,
7854
+ STYLE_ID_ATTRIBUTE,
7855
+ CHARACTER_STYLE_MARK_NAME,
7856
+ paragraphStylesPluginKey,
7857
+ getStyleRegistry,
7858
+ isParagraphStyleAvailable,
7859
+ getParagraphStyleAt,
7860
+ captureSelectionFormat,
7861
+ CharacterStyle,
7862
+ ParagraphStyles,
7863
+ createParagraphStylesStore,
6932
7864
  createRichTextPreset,
6933
7865
  createFindReplaceCommands,
6934
7866
  validateFindPattern
6935
7867
  };
6936
- //# sourceMappingURL=chunk-ITBSKI26.js.map
7868
+ //# sourceMappingURL=chunk-IXHSYOVY.js.map