@pixerate/editor 0.23.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.
- package/README.md +167 -2
- package/THIRD_PARTY_NOTICES.md +80 -0
- package/dist/agent/index.cjs +10 -8
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +4 -3
- package/dist/agent/index.d.ts +4 -3
- package/dist/agent/index.js +4 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/canvas/index.cjs +4437 -411
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +934 -4
- package/dist/canvas/index.d.ts +934 -4
- package/dist/canvas/index.js +4309 -283
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-664LQYFJ.js +115 -0
- package/dist/chunk-664LQYFJ.js.map +1 -0
- package/dist/chunk-7WISYZFO.cjs +740 -0
- package/dist/chunk-7WISYZFO.cjs.map +1 -0
- package/dist/chunk-AL6JMGWW.cjs +783 -0
- package/dist/chunk-AL6JMGWW.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-RIKBJLS2.cjs → chunk-C7P22VIP.cjs} +2001 -279
- package/dist/chunk-C7P22VIP.cjs.map +1 -0
- package/dist/chunk-D6FH2YAX.cjs +131 -0
- package/dist/chunk-D6FH2YAX.cjs.map +1 -0
- package/dist/chunk-ECCCYKGL.js +740 -0
- package/dist/chunk-ECCCYKGL.js.map +1 -0
- package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
- package/dist/chunk-FD4LPFAE.cjs.map +1 -0
- package/dist/{chunk-QBJ22YZR.js → chunk-FH6F67DN.js} +1999 -277
- package/dist/chunk-FH6F67DN.js.map +1 -0
- package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
- package/dist/chunk-FPENXAEJ.js.map +1 -0
- package/dist/chunk-HLIMTC6N.js +1 -0
- package/dist/chunk-HLIMTC6N.js.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-QGHGJ56O.js → chunk-IXHSYOVY.js} +2518 -122
- package/dist/chunk-IXHSYOVY.js.map +1 -0
- package/dist/chunk-MKA3AHLV.cjs +256 -0
- package/dist/chunk-MKA3AHLV.cjs.map +1 -0
- package/dist/chunk-NAXVVS5I.js +783 -0
- package/dist/chunk-NAXVVS5I.js.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/chunk-OXSV64JX.js +689 -0
- package/dist/chunk-OXSV64JX.js.map +1 -0
- package/dist/chunk-P3RQ5UJE.cjs +689 -0
- package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
- package/dist/chunk-P6ODPZ4W.js +256 -0
- package/dist/chunk-P6ODPZ4W.js.map +1 -0
- package/dist/chunk-PL4JB3BF.cjs +1 -0
- package/dist/chunk-PL4JB3BF.cjs.map +1 -0
- package/dist/{chunk-GVSMRPHB.cjs → chunk-PO6A6QQH.cjs} +2607 -211
- package/dist/chunk-PO6A6QQH.cjs.map +1 -0
- package/dist/{chunk-JJDLPEWL.js → chunk-QGYDWPRM.js} +2 -2
- package/dist/chunk-RR7F5GUM.js +5726 -0
- package/dist/chunk-RR7F5GUM.js.map +1 -0
- package/dist/chunk-TNNJXF46.cjs +115 -0
- package/dist/chunk-TNNJXF46.cjs.map +1 -0
- package/dist/chunk-U5HZSFCS.js +131 -0
- package/dist/chunk-U5HZSFCS.js.map +1 -0
- package/dist/{chunk-MJZBNWG7.js → chunk-UAKDG62Z.js} +309 -581
- package/dist/chunk-UAKDG62Z.js.map +1 -0
- package/dist/{chunk-CL37JGFW.cjs → chunk-V3CHHWOE.cjs} +4 -4
- package/dist/{chunk-CL37JGFW.cjs.map → chunk-V3CHHWOE.cjs.map} +1 -1
- package/dist/chunk-WSQKHXW7.cjs +5726 -0
- package/dist/chunk-WSQKHXW7.cjs.map +1 -0
- package/dist/{chunk-AA4ZEVCC.cjs → chunk-X4N4WUKB.cjs} +336 -608
- package/dist/chunk-X4N4WUKB.cjs.map +1 -0
- package/dist/commands/index.cjs +74 -1
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +38 -353
- package/dist/commands/index.d.ts +38 -353
- package/dist/commands/index.js +73 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/comment-store-BEMgVlAA.d.cts +135 -0
- package/dist/comment-store-BEMgVlAA.d.ts +135 -0
- package/dist/compare/index.cjs +15 -0
- package/dist/compare/index.cjs.map +1 -0
- package/dist/compare/index.d.cts +58 -0
- package/dist/compare/index.d.ts +58 -0
- package/dist/compare/index.js +15 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/controller-BIJiNoVs.d.cts +1842 -0
- package/dist/controller-BIJiNoVs.d.ts +1842 -0
- package/dist/documents-CHT1U5Vs.d.ts +86 -0
- package/dist/documents-Gp7wb0Me.d.cts +86 -0
- package/dist/extensions/index.cjs +100 -5
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +16 -4
- package/dist/extensions/index.d.ts +16 -4
- package/dist/extensions/index.js +100 -5
- package/dist/generation/index.cjs +2317 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +1015 -0
- package/dist/generation/index.d.ts +1015 -0
- package/dist/generation/index.js +2317 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/grammar/index.cjs +48 -3
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.d.cts +322 -100
- package/dist/grammar/index.d.ts +322 -100
- package/dist/grammar/index.js +54 -9
- package/dist/image-editor/index.cjs +4392 -481
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +1249 -97
- package/dist/image-editor/index.d.ts +1249 -97
- package/dist/image-editor/index.js +4281 -370
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index-7a6bHTLy.d.ts +1754 -0
- package/dist/index-Csd5qI8u.d.cts +1754 -0
- package/dist/index.cjs +1682 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -21
- package/dist/index.d.ts +118 -21
- package/dist/index.js +1630 -47
- package/dist/index.js.map +1 -1
- package/dist/mention-text-CDnCFd3c.d.cts +53 -0
- package/dist/mention-text-CDnCFd3c.d.ts +53 -0
- package/dist/migrate-iTlm3a8h.d.cts +112 -0
- package/dist/migrate-iTlm3a8h.d.ts +112 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.cjs +676 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +230 -0
- package/dist/persistence/index.d.ts +230 -0
- package/dist/persistence/index.js +676 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/{registry-D5-R90p0.d.cts → registry-BDe-vlv3.d.ts} +2 -40
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/registry-D32LpIc4.d.cts +354 -0
- package/dist/{registry-D5-R90p0.d.ts → registry-DnzpgY27.d.cts} +2 -40
- package/dist/registry-evKTP4ls.d.ts +354 -0
- package/dist/schema-DYDGdYL9.d.cts +41 -0
- package/dist/schema-DYDGdYL9.d.ts +41 -0
- package/dist/serializers/index.cjs +12 -3
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +40 -12
- package/dist/serializers/index.d.ts +40 -12
- package/dist/serializers/index.js +12 -3
- package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
- package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
- package/dist/spreadsheet/index.cjs +161 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +611 -20
- package/dist/spreadsheet/index.d.ts +611 -20
- package/dist/spreadsheet/index.js +168 -10
- package/dist/storage-BpFfspHm.d.cts +120 -0
- package/dist/storage-BpFfspHm.d.ts +120 -0
- package/dist/tokens/index.cjs +115 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +212 -0
- package/dist/tokens/index.d.ts +212 -0
- package/dist/tokens/index.js +115 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
- package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
- package/dist/{track-changes-BSF58RK0.d.cts → track-changes-D_VAPELi.d.cts} +110 -144
- package/dist/{track-changes-BSF58RK0.d.ts → track-changes-Db_fGEvx.d.ts} +110 -144
- package/package.json +21 -1
- package/dist/chunk-4SUNJZWB.cjs.map +0 -1
- package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
- package/dist/chunk-AXV2ZW34.cjs +0 -118
- package/dist/chunk-AXV2ZW34.cjs.map +0 -1
- package/dist/chunk-B2ZRLN4S.js +0 -118
- package/dist/chunk-B2ZRLN4S.js.map +0 -1
- package/dist/chunk-GVSMRPHB.cjs.map +0 -1
- package/dist/chunk-K3R5NG2D.js +0 -3100
- package/dist/chunk-K3R5NG2D.js.map +0 -1
- package/dist/chunk-MJZBNWG7.js.map +0 -1
- package/dist/chunk-OYXSKQFR.js.map +0 -1
- package/dist/chunk-QBJ22YZR.js.map +0 -1
- package/dist/chunk-QGHGJ56O.js.map +0 -1
- package/dist/chunk-RIKBJLS2.cjs.map +0 -1
- package/dist/chunk-XDMXXC4Z.cjs +0 -3100
- package/dist/chunk-XDMXXC4Z.cjs.map +0 -1
- package/dist/controller-pePCJLT1.d.cts +0 -959
- package/dist/controller-pePCJLT1.d.ts +0 -959
- package/dist/index-CCr_z9kF.d.cts +0 -899
- package/dist/index-Ck6ajwXh.d.ts +0 -899
- /package/dist/{chunk-JJDLPEWL.js.map → chunk-QGYDWPRM.js.map} +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareDocuments
|
|
3
|
+
} from "./chunk-NAXVVS5I.js";
|
|
1
4
|
import {
|
|
2
5
|
INSTRUCTION_REGEX,
|
|
3
6
|
TEMPLATE_REGEX,
|
|
4
7
|
VARIABLE_REGEX,
|
|
5
8
|
matchConditionalTags,
|
|
6
9
|
parseVariableBody
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-P6ODPZ4W.js";
|
|
8
11
|
import {
|
|
9
12
|
PIXERATE_DOCUMENT_MIME,
|
|
10
13
|
getClipboardData,
|
|
@@ -13,16 +16,20 @@ import {
|
|
|
13
16
|
rtfToPlainText,
|
|
14
17
|
sanitizeImportedHtml,
|
|
15
18
|
serializeRichClipboardPayload
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-QGYDWPRM.js";
|
|
17
20
|
import {
|
|
18
|
-
CommentStore,
|
|
19
|
-
TrackChanges,
|
|
20
|
-
createReviewId,
|
|
21
21
|
escapeHtml,
|
|
22
22
|
htmlToPlainText,
|
|
23
23
|
markdownToTipTapHtml,
|
|
24
24
|
plainTextToSlice
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-ECCCYKGL.js";
|
|
26
|
+
import {
|
|
27
|
+
CommentStore,
|
|
28
|
+
TRACK_CHANGES_SKIP_META,
|
|
29
|
+
TrackChanges,
|
|
30
|
+
createReviewId,
|
|
31
|
+
trackChangesPluginKey
|
|
32
|
+
} from "./chunk-UAKDG62Z.js";
|
|
26
33
|
|
|
27
34
|
// src/extensions/gradient-text.ts
|
|
28
35
|
import { Mark } from "@tiptap/core";
|
|
@@ -124,11 +131,12 @@ var createGradientDecorations = (doc, options) => {
|
|
|
124
131
|
result.error ? {
|
|
125
132
|
class: "underline decoration-wavy decoration-red-500 text-red-600 dark:text-red-400",
|
|
126
133
|
"data-conditional-error": result.error,
|
|
127
|
-
title: result.error === "unclosed-section" ? `Section "${tag.name}" is never closed with {/${tag.name}}` : `{/${tag.name}} does not close any open section`
|
|
134
|
+
title: result.error === "invalid-condition" ? `Invalid condition: ${result.comparison?.error ?? tag.name}` : result.error === "unclosed-section" ? `Section "${tag.name}" is never closed with {/${tag.name}}` : `{/${tag.name}} does not close any open section`
|
|
128
135
|
} : {
|
|
129
136
|
class: "bg-amber-400/30 text-amber-700 dark:text-amber-300 font-medium px-0.5 rounded-xs",
|
|
130
137
|
"data-conditional": tag.conditional,
|
|
131
|
-
|
|
138
|
+
// A comparison (`{?amount>100}`) names its subject variable.
|
|
139
|
+
"data-variable-condition": result.comparison?.subject ?? tag.name
|
|
132
140
|
}
|
|
133
141
|
)
|
|
134
142
|
);
|
|
@@ -593,10 +601,47 @@ var Mention = BaseMention.extend({
|
|
|
593
601
|
if (!attributes.color) return {};
|
|
594
602
|
return { "data-color": attributes.color };
|
|
595
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
|
+
}
|
|
596
618
|
}
|
|
597
619
|
};
|
|
598
620
|
}
|
|
599
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
|
+
}
|
|
600
645
|
function createMentionExtension(options = {}) {
|
|
601
646
|
const { HTMLAttributes = {}, ...rest } = options;
|
|
602
647
|
return Mention.configure({
|
|
@@ -2028,8 +2073,56 @@ function getCommentAuthor(editor) {
|
|
|
2028
2073
|
return ext ? resolveAuthor(ext.options.author) : null;
|
|
2029
2074
|
}
|
|
2030
2075
|
|
|
2031
|
-
// src/extensions/
|
|
2076
|
+
// src/extensions/document-compare.ts
|
|
2032
2077
|
import { Extension as Extension7 } from "@tiptap/core";
|
|
2078
|
+
function trackChangesAuthor(state) {
|
|
2079
|
+
return trackChangesPluginKey.getState(state)?.author;
|
|
2080
|
+
}
|
|
2081
|
+
function compareInEditor(editor, original, revised, options = {}) {
|
|
2082
|
+
return compareDocuments(original, revised, {
|
|
2083
|
+
...options,
|
|
2084
|
+
author: options.author ?? trackChangesAuthor(editor.state),
|
|
2085
|
+
schema: editor.schema
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
function applyDocumentComparison(editor, original, revised, options = {}) {
|
|
2089
|
+
const result = compareInEditor(editor, original, revised, options);
|
|
2090
|
+
const { state } = editor;
|
|
2091
|
+
const tr = state.tr.replaceWith(0, state.doc.content.size, result.doc.content);
|
|
2092
|
+
tr.setMeta(TRACK_CHANGES_SKIP_META, true);
|
|
2093
|
+
editor.view.dispatch(tr);
|
|
2094
|
+
return result;
|
|
2095
|
+
}
|
|
2096
|
+
var DocumentCompare = Extension7.create({
|
|
2097
|
+
name: "documentCompare",
|
|
2098
|
+
addOptions() {
|
|
2099
|
+
return { compare: {} };
|
|
2100
|
+
},
|
|
2101
|
+
addStorage() {
|
|
2102
|
+
return { lastResult: null };
|
|
2103
|
+
},
|
|
2104
|
+
addCommands() {
|
|
2105
|
+
return {
|
|
2106
|
+
setContentFromComparison: (original, revised, options = {}) => ({ state, tr, dispatch }) => {
|
|
2107
|
+
const merged = { ...this.options.compare, ...options };
|
|
2108
|
+
const result = compareDocuments(original, revised, {
|
|
2109
|
+
...merged,
|
|
2110
|
+
author: merged.author ?? trackChangesAuthor(state),
|
|
2111
|
+
schema: state.schema
|
|
2112
|
+
});
|
|
2113
|
+
if (dispatch) {
|
|
2114
|
+
tr.replaceWith(0, tr.doc.content.size, result.doc.content);
|
|
2115
|
+
tr.setMeta(TRACK_CHANGES_SKIP_META, true);
|
|
2116
|
+
this.storage.lastResult = result;
|
|
2117
|
+
}
|
|
2118
|
+
return true;
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
// src/extensions/file-import.ts
|
|
2125
|
+
import { Extension as Extension8 } from "@tiptap/core";
|
|
2033
2126
|
import { Plugin as Plugin7, PluginKey as PluginKey4 } from "@tiptap/pm/state";
|
|
2034
2127
|
|
|
2035
2128
|
// src/import/registry.ts
|
|
@@ -2193,7 +2286,13 @@ var ImportAdapterRegistry = class {
|
|
|
2193
2286
|
{ fileName }
|
|
2194
2287
|
);
|
|
2195
2288
|
}
|
|
2196
|
-
const context = createImportContext(input, {
|
|
2289
|
+
const context = createImportContext(input, {
|
|
2290
|
+
fileName,
|
|
2291
|
+
mimeType,
|
|
2292
|
+
target: adapter.target,
|
|
2293
|
+
registry: this,
|
|
2294
|
+
options: options.adapterOptions?.[adapter.id]
|
|
2295
|
+
});
|
|
2197
2296
|
try {
|
|
2198
2297
|
const result = await adapter.import(input, context);
|
|
2199
2298
|
return { adapterId: adapter.id, target: adapter.target, fileName, mimeType, result };
|
|
@@ -3292,7 +3391,10 @@ async function importFilesIntoEditor(target, files, options = {}) {
|
|
|
3292
3391
|
let at = options.at;
|
|
3293
3392
|
for (const file of Array.from(files)) {
|
|
3294
3393
|
try {
|
|
3295
|
-
const outcome = await registry.importFile(file, {
|
|
3394
|
+
const outcome = await registry.importFile(file, {
|
|
3395
|
+
target: "document",
|
|
3396
|
+
adapterOptions: options.adapterOptions
|
|
3397
|
+
});
|
|
3296
3398
|
const view = toView(target);
|
|
3297
3399
|
if (view.isDestroyed) break;
|
|
3298
3400
|
at = insertImportedDocument(view, outcome.result, { at, asPlainText: options.asPlainText });
|
|
@@ -3321,11 +3423,12 @@ function dropPosition(view, event) {
|
|
|
3321
3423
|
return void 0;
|
|
3322
3424
|
}
|
|
3323
3425
|
}
|
|
3324
|
-
var FileImport =
|
|
3426
|
+
var FileImport = Extension8.create({
|
|
3325
3427
|
name: "fileImport",
|
|
3326
3428
|
addOptions() {
|
|
3327
3429
|
return {
|
|
3328
3430
|
registry: void 0,
|
|
3431
|
+
adapterOptions: void 0,
|
|
3329
3432
|
asPlainText: false,
|
|
3330
3433
|
enableDrop: true,
|
|
3331
3434
|
enablePaste: true,
|
|
@@ -3341,6 +3444,7 @@ var FileImport = Extension7.create({
|
|
|
3341
3444
|
registry: registry(),
|
|
3342
3445
|
at,
|
|
3343
3446
|
asPlainText: options.asPlainText,
|
|
3447
|
+
adapterOptions: options.adapterOptions,
|
|
3344
3448
|
onImport: options.onImport,
|
|
3345
3449
|
onError: options.onImportError
|
|
3346
3450
|
});
|
|
@@ -3372,7 +3476,7 @@ var FileImport = Extension7.create({
|
|
|
3372
3476
|
});
|
|
3373
3477
|
|
|
3374
3478
|
// src/extensions/rich-clipboard.ts
|
|
3375
|
-
import { Extension as
|
|
3479
|
+
import { Extension as Extension9 } from "@tiptap/core";
|
|
3376
3480
|
import { Plugin as Plugin8, PluginKey as PluginKey5 } from "@tiptap/pm/state";
|
|
3377
3481
|
var richClipboardPluginKey = new PluginKey5("richClipboard");
|
|
3378
3482
|
function writeRichClipboard(view, event, options = {}) {
|
|
@@ -3392,7 +3496,7 @@ function writeRichClipboard(view, event, options = {}) {
|
|
|
3392
3496
|
}
|
|
3393
3497
|
return true;
|
|
3394
3498
|
}
|
|
3395
|
-
var RichClipboard =
|
|
3499
|
+
var RichClipboard = Extension9.create({
|
|
3396
3500
|
name: "richClipboard",
|
|
3397
3501
|
addOptions() {
|
|
3398
3502
|
return {
|
|
@@ -3431,7 +3535,7 @@ var RichClipboard = Extension8.create({
|
|
|
3431
3535
|
});
|
|
3432
3536
|
|
|
3433
3537
|
// src/extensions/autocorrect.ts
|
|
3434
|
-
import { Extension as
|
|
3538
|
+
import { Extension as Extension10 } from "@tiptap/core";
|
|
3435
3539
|
import { closeHistory } from "@tiptap/pm/history";
|
|
3436
3540
|
import { Plugin as Plugin9, PluginKey as PluginKey6 } from "@tiptap/pm/state";
|
|
3437
3541
|
var quoteOpener = /[\s ([{<—–‘“-]/;
|
|
@@ -3590,7 +3694,7 @@ function isInsidePromptToken(textBefore) {
|
|
|
3590
3694
|
const underscores = textBefore.match(/__/g);
|
|
3591
3695
|
return !!underscores && underscores.length % 2 === 1;
|
|
3592
3696
|
}
|
|
3593
|
-
var Autocorrect =
|
|
3697
|
+
var Autocorrect = Extension10.create({
|
|
3594
3698
|
name: "autocorrect",
|
|
3595
3699
|
addOptions() {
|
|
3596
3700
|
return {
|
|
@@ -3738,7 +3842,7 @@ var Autocorrect = Extension9.create({
|
|
|
3738
3842
|
});
|
|
3739
3843
|
|
|
3740
3844
|
// src/extensions/document-structure/outline.ts
|
|
3741
|
-
import { Extension as
|
|
3845
|
+
import { Extension as Extension11 } from "@tiptap/core";
|
|
3742
3846
|
import {
|
|
3743
3847
|
Plugin as Plugin10,
|
|
3744
3848
|
PluginKey as PluginKey7,
|
|
@@ -3974,7 +4078,7 @@ function assignHeadingIds(state, types, generateId) {
|
|
|
3974
4078
|
});
|
|
3975
4079
|
return tr;
|
|
3976
4080
|
}
|
|
3977
|
-
var Outline =
|
|
4081
|
+
var Outline = Extension11.create({
|
|
3978
4082
|
name: "outline",
|
|
3979
4083
|
priority: STRUCTURE_PRIORITY,
|
|
3980
4084
|
addOptions() {
|
|
@@ -5169,117 +5273,2224 @@ function getDocumentStructureCommandItems(editor, query = "") {
|
|
|
5169
5273
|
);
|
|
5170
5274
|
}
|
|
5171
5275
|
|
|
5172
|
-
// src/extensions/
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
var
|
|
5180
|
-
var
|
|
5181
|
-
var
|
|
5182
|
-
var
|
|
5183
|
-
var
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5276
|
+
// src/extensions/find-replace/extension.ts
|
|
5277
|
+
import { Extension as Extension12 } from "@tiptap/core";
|
|
5278
|
+
import { Plugin as Plugin14, PluginKey as PluginKey11, TextSelection as TextSelection7 } from "@tiptap/pm/state";
|
|
5279
|
+
import { Decoration as Decoration4, DecorationSet as DecorationSet4 } from "@tiptap/pm/view";
|
|
5280
|
+
import { closeHistory as closeHistory2 } from "@tiptap/pm/history";
|
|
5281
|
+
|
|
5282
|
+
// src/extensions/find-replace/pattern.ts
|
|
5283
|
+
var FIND_PARAGRAPH_MARK = "\r";
|
|
5284
|
+
var MAX_FIND_PATTERN_LENGTH = 1024;
|
|
5285
|
+
var MAX_COUNT = 1e3;
|
|
5286
|
+
var MAX_PROGRAM_SIZE = 2e4;
|
|
5287
|
+
var now = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
5288
|
+
function createSearchBudget(options = {}) {
|
|
5289
|
+
return {
|
|
5290
|
+
deadline: now() + (options.timeLimitMs ?? 250),
|
|
5291
|
+
steps: options.maxSteps ?? 5e7,
|
|
5292
|
+
exhausted: false
|
|
5293
|
+
};
|
|
5294
|
+
}
|
|
5295
|
+
var PARA = 13;
|
|
5296
|
+
var ANY = (cp) => cp !== PARA;
|
|
5297
|
+
var LETTER = /\p{L}/u;
|
|
5298
|
+
var PUNCT = /\p{P}/u;
|
|
5299
|
+
var WORD = /[\p{L}\p{M}\p{Nd}\p{Pc}]/u;
|
|
5300
|
+
var foldCache = /* @__PURE__ */ new Map();
|
|
5301
|
+
function foldCodePoint(cp) {
|
|
5302
|
+
if (cp < 128) return cp >= 65 && cp <= 90 ? cp + 32 : cp;
|
|
5303
|
+
const cached = foldCache.get(cp);
|
|
5304
|
+
if (cached !== void 0) return cached;
|
|
5305
|
+
const lower = String.fromCodePoint(cp).toLowerCase();
|
|
5306
|
+
const folded = [...lower].length === 1 ? lower.codePointAt(0) : cp;
|
|
5307
|
+
if (foldCache.size < 4096) foldCache.set(cp, folded);
|
|
5308
|
+
return folded;
|
|
5309
|
+
}
|
|
5310
|
+
function isWordChar(cp) {
|
|
5311
|
+
if (cp < 0) return false;
|
|
5312
|
+
if (cp < 128) {
|
|
5313
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 95;
|
|
5314
|
+
}
|
|
5315
|
+
return WORD.test(String.fromCodePoint(cp));
|
|
5316
|
+
}
|
|
5317
|
+
var isLetter = (cp) => cp < 128 ? cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 : LETTER.test(String.fromCodePoint(cp));
|
|
5318
|
+
function isPunct(cp) {
|
|
5319
|
+
if (cp < 128) {
|
|
5320
|
+
return cp >= 33 && cp <= 47 || cp >= 58 && cp <= 64 || cp >= 91 && cp <= 96 || cp >= 123 && cp <= 126;
|
|
5321
|
+
}
|
|
5322
|
+
return cp >= 8208 && cp <= 8231 || cp === 161 || cp === 191 || cp === 171 || cp === 187 || PUNCT.test(String.fromCodePoint(cp));
|
|
5323
|
+
}
|
|
5324
|
+
var isSpace = (cp) => cp === 32 || cp === 9 || cp === 160;
|
|
5325
|
+
function cpBefore(hay, pos) {
|
|
5326
|
+
if (pos <= 0) return -1;
|
|
5327
|
+
const low = hay.charCodeAt(pos - 1);
|
|
5328
|
+
if (low >= 56320 && low <= 57343 && pos >= 2) {
|
|
5329
|
+
const high = hay.charCodeAt(pos - 2);
|
|
5330
|
+
if (high >= 55296 && high <= 56319) return hay.codePointAt(pos - 2);
|
|
5331
|
+
}
|
|
5332
|
+
return low;
|
|
5333
|
+
}
|
|
5334
|
+
var cpAt = (hay, pos) => pos < hay.length ? hay.codePointAt(pos) : -1;
|
|
5335
|
+
var PatternError = class extends Error {
|
|
5336
|
+
constructor(code, message) {
|
|
5337
|
+
super(message);
|
|
5338
|
+
this.code = code;
|
|
5339
|
+
}
|
|
5340
|
+
code;
|
|
5341
|
+
};
|
|
5342
|
+
var charNode = (test) => ({ k: "char", test });
|
|
5343
|
+
var exact = (cp) => charNode((c) => c === cp);
|
|
5344
|
+
var literalNode = (cp, caseInsensitive) => {
|
|
5345
|
+
if (!caseInsensitive) return exact(cp);
|
|
5346
|
+
const f = foldCodePoint(cp);
|
|
5347
|
+
return charNode((_c, folded) => folded === f);
|
|
5348
|
+
};
|
|
5349
|
+
var star = (body, lazy) => ({ k: "rep", body, min: 0, max: -1, lazy });
|
|
5350
|
+
function findCode(c) {
|
|
5351
|
+
switch (c) {
|
|
5352
|
+
case "p":
|
|
5353
|
+
return exact(PARA);
|
|
5354
|
+
case "t":
|
|
5355
|
+
return exact(9);
|
|
5356
|
+
case "?":
|
|
5357
|
+
return charNode(ANY);
|
|
5358
|
+
case "#":
|
|
5359
|
+
return charNode((cp) => cp >= 48 && cp <= 57);
|
|
5360
|
+
case "$":
|
|
5361
|
+
return charNode((cp) => isLetter(cp));
|
|
5362
|
+
case "^":
|
|
5363
|
+
return exact(94);
|
|
5364
|
+
case "l":
|
|
5365
|
+
return exact(10);
|
|
5366
|
+
case "m":
|
|
5367
|
+
return exact(12);
|
|
5368
|
+
case "n":
|
|
5369
|
+
return exact(14);
|
|
5370
|
+
case "+":
|
|
5371
|
+
return exact(8212);
|
|
5372
|
+
case "=":
|
|
5373
|
+
return exact(8211);
|
|
5374
|
+
case "s":
|
|
5375
|
+
return exact(160);
|
|
5376
|
+
case "~":
|
|
5377
|
+
return exact(8209);
|
|
5378
|
+
case "-":
|
|
5379
|
+
return exact(173);
|
|
5380
|
+
case "w":
|
|
5381
|
+
return { k: "rep", body: charNode((cp) => isSpace(cp)), min: 1, max: -1, lazy: false };
|
|
5382
|
+
case "g":
|
|
5383
|
+
return exact(65532);
|
|
5384
|
+
case "%":
|
|
5385
|
+
return exact(167);
|
|
5386
|
+
case "v":
|
|
5387
|
+
return exact(182);
|
|
5388
|
+
default:
|
|
5389
|
+
return null;
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
function replaceCode(c) {
|
|
5393
|
+
switch (c) {
|
|
5394
|
+
case "p":
|
|
5395
|
+
return FIND_PARAGRAPH_MARK;
|
|
5396
|
+
case "t":
|
|
5397
|
+
return " ";
|
|
5398
|
+
case "^":
|
|
5399
|
+
return "^";
|
|
5400
|
+
case "l":
|
|
5401
|
+
return "\n";
|
|
5402
|
+
case "m":
|
|
5403
|
+
return "\f";
|
|
5404
|
+
case "n":
|
|
5405
|
+
return "";
|
|
5406
|
+
case "+":
|
|
5407
|
+
return "\u2014";
|
|
5408
|
+
case "=":
|
|
5409
|
+
return "\u2013";
|
|
5410
|
+
case "s":
|
|
5411
|
+
return "\xA0";
|
|
5412
|
+
case "~":
|
|
5413
|
+
return "\u2011";
|
|
5414
|
+
case "-":
|
|
5415
|
+
return "\xAD";
|
|
5416
|
+
case "%":
|
|
5417
|
+
return "\xA7";
|
|
5418
|
+
case "v":
|
|
5419
|
+
return "\xB6";
|
|
5420
|
+
default:
|
|
5421
|
+
return null;
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
function codeChar(n) {
|
|
5425
|
+
switch (n) {
|
|
5426
|
+
case 13:
|
|
5427
|
+
return PARA;
|
|
5428
|
+
case 11:
|
|
5429
|
+
return 10;
|
|
5430
|
+
case 12:
|
|
5431
|
+
return 12;
|
|
5432
|
+
case 14:
|
|
5433
|
+
return 14;
|
|
5434
|
+
case 30:
|
|
5435
|
+
return 8209;
|
|
5436
|
+
case 31:
|
|
5437
|
+
return 173;
|
|
5438
|
+
}
|
|
5439
|
+
if (n > 1114111 || n >= 55296 && n <= 57343) return null;
|
|
5440
|
+
if (n < 32 && n !== 9 || n >= 127 && n < 160) return null;
|
|
5441
|
+
return n;
|
|
5442
|
+
}
|
|
5443
|
+
function numericCode(chars, i) {
|
|
5444
|
+
const unicode = chars[i] === "u";
|
|
5445
|
+
const start2 = unicode ? i + 1 : i;
|
|
5446
|
+
const max = unicode ? 5 : 4;
|
|
5447
|
+
let digits = "";
|
|
5448
|
+
for (let j = start2; j < chars.length && digits.length < max && /[0-9]/.test(chars[j]); j++) digits += chars[j];
|
|
5449
|
+
if (!digits) return null;
|
|
5450
|
+
const cp = codeChar(parseInt(digits, 10));
|
|
5451
|
+
return cp === null ? null : { cp, used: start2 - i + digits.length };
|
|
5452
|
+
}
|
|
5453
|
+
var toChars = (s) => Array.from(s);
|
|
5454
|
+
var cpOf = (ch) => ch.codePointAt(0);
|
|
5455
|
+
function parseSpecial(query, o, caseInsensitive) {
|
|
5456
|
+
const chars = toChars(query);
|
|
5457
|
+
const atoms = [];
|
|
5458
|
+
let spans = false;
|
|
5459
|
+
let i = 0;
|
|
5460
|
+
while (i < chars.length) {
|
|
5461
|
+
const c = chars[i++];
|
|
5462
|
+
if (c === "^" && !o.literal && i < chars.length) {
|
|
5463
|
+
const n = chars[i];
|
|
5464
|
+
const code = findCode(n);
|
|
5465
|
+
if (code) {
|
|
5466
|
+
if (n === "p") spans = true;
|
|
5467
|
+
atoms.push(code);
|
|
5468
|
+
i++;
|
|
5469
|
+
continue;
|
|
5470
|
+
}
|
|
5471
|
+
const num = numericCode(chars, i);
|
|
5472
|
+
if (num) {
|
|
5473
|
+
if (num.cp === PARA) spans = true;
|
|
5474
|
+
atoms.push(literalNode(num.cp, caseInsensitive));
|
|
5475
|
+
i += num.used;
|
|
5476
|
+
continue;
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
const cp = cpOf(c);
|
|
5480
|
+
if (o.ignorePunctuation && isPunct(cp) || o.ignoreWhitespace && isSpace(cp)) continue;
|
|
5481
|
+
atoms.push(literalNode(cp, caseInsensitive));
|
|
5482
|
+
}
|
|
5483
|
+
const filler = o.ignorePunctuation && o.ignoreWhitespace ? (cp) => isPunct(cp) || isSpace(cp) : o.ignorePunctuation ? (cp) => isPunct(cp) : o.ignoreWhitespace ? (cp) => isSpace(cp) : null;
|
|
5484
|
+
const items = [];
|
|
5485
|
+
atoms.forEach((atom, index) => {
|
|
5486
|
+
if (index > 0 && filler) items.push(star(charNode(filler), false));
|
|
5487
|
+
items.push(atom);
|
|
5488
|
+
});
|
|
5489
|
+
return { node: { k: "cat", items }, spans };
|
|
5490
|
+
}
|
|
5491
|
+
function parseCount(body) {
|
|
5492
|
+
const bad = () => new PatternError("invalid_pattern", `{${body}} is not a valid count`);
|
|
5493
|
+
const num = (s) => {
|
|
5494
|
+
const t = s.trim();
|
|
5495
|
+
if (!t) return null;
|
|
5496
|
+
if (!/^[0-9]+$/.test(t)) throw bad();
|
|
5497
|
+
const n = parseInt(t, 10);
|
|
5498
|
+
if (n > MAX_COUNT) throw new PatternError("invalid_pattern", `counts go up to ${MAX_COUNT}`);
|
|
5499
|
+
return n;
|
|
5500
|
+
};
|
|
5501
|
+
const sep = body.search(/[,;]/);
|
|
5502
|
+
if (sep < 0) {
|
|
5503
|
+
const n = num(body);
|
|
5504
|
+
if (n === null) throw bad();
|
|
5505
|
+
return { min: n, max: n };
|
|
5506
|
+
}
|
|
5507
|
+
const a = num(body.slice(0, sep)) ?? 0;
|
|
5508
|
+
const b = num(body.slice(sep + 1));
|
|
5509
|
+
if (b !== null && b < a) throw bad();
|
|
5510
|
+
return { min: a, max: b ?? -1 };
|
|
5511
|
+
}
|
|
5512
|
+
function parseClass(inner) {
|
|
5513
|
+
let negated = false;
|
|
5514
|
+
if (inner[0] === "!") {
|
|
5515
|
+
negated = true;
|
|
5516
|
+
inner = inner.slice(1);
|
|
5517
|
+
}
|
|
5518
|
+
if (inner.length === 0) throw new PatternError("invalid_pattern", "[ ] needs at least one character");
|
|
5519
|
+
const singles = /* @__PURE__ */ new Set();
|
|
5520
|
+
const ranges = [];
|
|
5521
|
+
let i = 0;
|
|
5522
|
+
while (i < inner.length) {
|
|
5523
|
+
let c = inner[i];
|
|
5524
|
+
if (c === "\\" && i + 1 < inner.length) c = inner[++i];
|
|
5525
|
+
i++;
|
|
5526
|
+
if (inner[i] === "-" && i + 1 < inner.length) {
|
|
5527
|
+
const hi = inner[i + 1];
|
|
5528
|
+
if (cpOf(hi) < cpOf(c)) throw new PatternError("invalid_pattern", `[${c}-${hi}] is not in ascending order`);
|
|
5529
|
+
ranges.push([cpOf(c), cpOf(hi)]);
|
|
5530
|
+
i += 2;
|
|
5531
|
+
} else {
|
|
5532
|
+
singles.add(cpOf(c));
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
const inSet = (cp) => singles.has(cp) || ranges.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
5536
|
+
return negated ? (cp) => cp !== PARA && !inSet(cp) : (cp) => inSet(cp);
|
|
5537
|
+
}
|
|
5538
|
+
function parseWildcards(query) {
|
|
5539
|
+
const chars = toChars(query);
|
|
5540
|
+
const stack = [{ items: [], group: 0 }];
|
|
5541
|
+
let groups = 0;
|
|
5542
|
+
let spans = false;
|
|
5543
|
+
const top = () => stack[stack.length - 1].items;
|
|
5544
|
+
const quantify = (min, max, what) => {
|
|
5545
|
+
const items = top();
|
|
5546
|
+
const last = items[items.length - 1];
|
|
5547
|
+
if (!last || last.k === "assert" || last.k === "rep") {
|
|
5548
|
+
throw new PatternError("invalid_pattern", `${what} needs something to repeat before it`);
|
|
5549
|
+
}
|
|
5550
|
+
items[items.length - 1] = { k: "rep", body: last, min, max, lazy: false };
|
|
5551
|
+
};
|
|
5552
|
+
let i = 0;
|
|
5553
|
+
while (i < chars.length) {
|
|
5554
|
+
const c = chars[i++];
|
|
5555
|
+
switch (c) {
|
|
5556
|
+
case "?":
|
|
5557
|
+
top().push(charNode(ANY));
|
|
5558
|
+
break;
|
|
5559
|
+
case "*":
|
|
5560
|
+
top().push(star(charNode(ANY), true));
|
|
5561
|
+
break;
|
|
5562
|
+
case "@":
|
|
5563
|
+
quantify(1, -1, "@");
|
|
5564
|
+
break;
|
|
5565
|
+
case "<":
|
|
5566
|
+
top().push({ k: "assert", kind: "wordStart" });
|
|
5567
|
+
break;
|
|
5568
|
+
case ">":
|
|
5569
|
+
top().push({ k: "assert", kind: "wordEnd" });
|
|
5570
|
+
break;
|
|
5571
|
+
case "(":
|
|
5572
|
+
stack.push({ items: [], group: ++groups });
|
|
5573
|
+
break;
|
|
5574
|
+
case ")": {
|
|
5575
|
+
if (stack.length === 1) throw new PatternError("invalid_pattern", "a ) has no opening (");
|
|
5576
|
+
const frame = stack.pop();
|
|
5577
|
+
top().push({ k: "group", index: frame.group, body: { k: "cat", items: frame.items } });
|
|
5578
|
+
break;
|
|
5579
|
+
}
|
|
5580
|
+
case "{": {
|
|
5581
|
+
const end = chars.indexOf("}", i);
|
|
5582
|
+
if (end < 0) throw new PatternError("invalid_pattern", "a { needs a closing }");
|
|
5583
|
+
const { min, max } = parseCount(chars.slice(i, end).join(""));
|
|
5584
|
+
quantify(min, max, `{${chars.slice(i, end).join("")}}`);
|
|
5585
|
+
i = end + 1;
|
|
5586
|
+
break;
|
|
5587
|
+
}
|
|
5588
|
+
case "[": {
|
|
5589
|
+
const end = chars.indexOf("]", i);
|
|
5590
|
+
if (end < 0) throw new PatternError("invalid_pattern", "a [ needs a closing ]");
|
|
5591
|
+
top().push(charNode(parseClass(chars.slice(i, end))));
|
|
5592
|
+
i = end + 1;
|
|
5593
|
+
break;
|
|
5594
|
+
}
|
|
5595
|
+
case "\\": {
|
|
5596
|
+
if (i >= chars.length) {
|
|
5597
|
+
top().push(exact(92));
|
|
5598
|
+
} else if (/[0-9]/.test(chars[i])) {
|
|
5599
|
+
throw new PatternError("invalid_pattern", "back-references (\\1) work in Replace with, not in Find what");
|
|
5600
|
+
} else {
|
|
5601
|
+
top().push(exact(cpOf(chars[i++])));
|
|
5201
5602
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5603
|
+
break;
|
|
5604
|
+
}
|
|
5605
|
+
case "^": {
|
|
5606
|
+
if (i >= chars.length) {
|
|
5607
|
+
top().push(exact(94));
|
|
5608
|
+
break;
|
|
5206
5609
|
}
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5610
|
+
const n = chars[i];
|
|
5611
|
+
const code = findCode(n);
|
|
5612
|
+
if (code) {
|
|
5613
|
+
if (n === "p") spans = true;
|
|
5614
|
+
top().push(code);
|
|
5615
|
+
i++;
|
|
5616
|
+
break;
|
|
5617
|
+
}
|
|
5618
|
+
const num = numericCode(chars, i);
|
|
5619
|
+
if (num) {
|
|
5620
|
+
if (num.cp === PARA) spans = true;
|
|
5621
|
+
top().push(exact(num.cp));
|
|
5622
|
+
i += num.used;
|
|
5623
|
+
} else {
|
|
5624
|
+
top().push(exact(94));
|
|
5212
5625
|
}
|
|
5626
|
+
break;
|
|
5213
5627
|
}
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5628
|
+
default:
|
|
5629
|
+
top().push(exact(cpOf(c)));
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
if (stack.length > 1) throw new PatternError("invalid_pattern", "a ( needs a closing )");
|
|
5633
|
+
return { node: { k: "cat", items: stack[0].items }, spans, groups };
|
|
5634
|
+
}
|
|
5635
|
+
function compileProgram(node) {
|
|
5636
|
+
const prog = [];
|
|
5637
|
+
const push = (inst) => {
|
|
5638
|
+
if (prog.length >= MAX_PROGRAM_SIZE) {
|
|
5639
|
+
throw new PatternError("too_complex", "the pattern is too complex (too many repeats)");
|
|
5640
|
+
}
|
|
5641
|
+
prog.push(inst);
|
|
5642
|
+
return prog.length - 1;
|
|
5643
|
+
};
|
|
5644
|
+
const emit = (n) => {
|
|
5645
|
+
switch (n.k) {
|
|
5646
|
+
case "char":
|
|
5647
|
+
push({ op: 0 /* Char */, test: n.test });
|
|
5648
|
+
return;
|
|
5649
|
+
case "cat":
|
|
5650
|
+
n.items.forEach(emit);
|
|
5651
|
+
return;
|
|
5652
|
+
case "group":
|
|
5653
|
+
push({ op: 3 /* Save */, n: 2 * n.index });
|
|
5654
|
+
emit(n.body);
|
|
5655
|
+
push({ op: 3 /* Save */, n: 2 * n.index + 1 });
|
|
5656
|
+
return;
|
|
5657
|
+
case "assert":
|
|
5658
|
+
push({ op: 4 /* Assert */, kind: n.kind });
|
|
5659
|
+
return;
|
|
5660
|
+
case "rep": {
|
|
5661
|
+
for (let k = 0; k < n.min; k++) emit(n.body);
|
|
5662
|
+
if (n.max < 0) {
|
|
5663
|
+
const split = push({ op: 1 /* Split */, x: 0, y: 0 });
|
|
5664
|
+
emit(n.body);
|
|
5665
|
+
push({ op: 2 /* Jmp */, x: split });
|
|
5666
|
+
const exit2 = prog.length;
|
|
5667
|
+
const inst = prog[split];
|
|
5668
|
+
inst.x = n.lazy ? exit2 : split + 1;
|
|
5669
|
+
inst.y = n.lazy ? split + 1 : exit2;
|
|
5670
|
+
return;
|
|
5671
|
+
}
|
|
5672
|
+
const splits = [];
|
|
5673
|
+
for (let k = n.min; k < n.max; k++) {
|
|
5674
|
+
splits.push(push({ op: 1 /* Split */, x: 0, y: 0 }));
|
|
5675
|
+
emit(n.body);
|
|
5676
|
+
}
|
|
5677
|
+
const exit = prog.length;
|
|
5678
|
+
for (const s of splits) {
|
|
5679
|
+
const inst = prog[s];
|
|
5680
|
+
inst.x = n.lazy ? exit : s + 1;
|
|
5681
|
+
inst.y = n.lazy ? s + 1 : exit;
|
|
5682
|
+
}
|
|
5683
|
+
return;
|
|
5234
5684
|
}
|
|
5235
|
-
}
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5685
|
+
}
|
|
5686
|
+
};
|
|
5687
|
+
push({ op: 3 /* Save */, n: 0 });
|
|
5688
|
+
emit(node);
|
|
5689
|
+
push({ op: 3 /* Save */, n: 1 });
|
|
5690
|
+
push({ op: 5 /* Match */ });
|
|
5691
|
+
return prog;
|
|
5692
|
+
}
|
|
5693
|
+
function checkAssert(kind, hay, pos) {
|
|
5694
|
+
const before = isWordChar(cpBefore(hay, pos));
|
|
5695
|
+
const after = isWordChar(cpAt(hay, pos));
|
|
5696
|
+
switch (kind) {
|
|
5697
|
+
case "boundary":
|
|
5698
|
+
return before !== after;
|
|
5699
|
+
case "wordStart":
|
|
5700
|
+
return !before && after;
|
|
5701
|
+
case "wordEnd":
|
|
5702
|
+
return before && !after;
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
function pikeExec(prog, groupCount, caseInsensitive, hay, start2, end, budget) {
|
|
5706
|
+
const ncap = 2 * (groupCount + 1);
|
|
5707
|
+
const mark = new Int32Array(prog.length);
|
|
5708
|
+
let gen = 1;
|
|
5709
|
+
let clistPc = [];
|
|
5710
|
+
let clistCaps = [];
|
|
5711
|
+
let nlistPc = [];
|
|
5712
|
+
let nlistCaps = [];
|
|
5713
|
+
const stackPc = [];
|
|
5714
|
+
const stackCaps = [];
|
|
5715
|
+
let matched = null;
|
|
5716
|
+
const initial = new Int32Array(ncap).fill(-1);
|
|
5717
|
+
const add = (listPc, listCaps, pc0, caps0, pos) => {
|
|
5718
|
+
stackPc.push(pc0);
|
|
5719
|
+
stackCaps.push(caps0);
|
|
5720
|
+
while (stackPc.length) {
|
|
5721
|
+
const pc = stackPc.pop();
|
|
5722
|
+
const caps = stackCaps.pop();
|
|
5723
|
+
if (mark[pc] === gen) continue;
|
|
5724
|
+
mark[pc] = gen;
|
|
5725
|
+
const inst = prog[pc];
|
|
5726
|
+
switch (inst.op) {
|
|
5727
|
+
case 2 /* Jmp */:
|
|
5728
|
+
stackPc.push(inst.x);
|
|
5729
|
+
stackCaps.push(caps);
|
|
5730
|
+
break;
|
|
5731
|
+
case 1 /* Split */:
|
|
5732
|
+
stackPc.push(inst.y, inst.x);
|
|
5733
|
+
stackCaps.push(caps, caps);
|
|
5734
|
+
break;
|
|
5735
|
+
case 3 /* Save */: {
|
|
5736
|
+
const next = caps.slice();
|
|
5737
|
+
next[inst.n] = pos;
|
|
5738
|
+
stackPc.push(pc + 1);
|
|
5739
|
+
stackCaps.push(next);
|
|
5740
|
+
break;
|
|
5741
|
+
}
|
|
5742
|
+
case 4 /* Assert */:
|
|
5743
|
+
if (checkAssert(inst.kind, hay, pos)) {
|
|
5744
|
+
stackPc.push(pc + 1);
|
|
5745
|
+
stackCaps.push(caps);
|
|
5746
|
+
}
|
|
5747
|
+
break;
|
|
5748
|
+
default:
|
|
5749
|
+
listPc.push(pc);
|
|
5750
|
+
listCaps.push(caps);
|
|
5244
5751
|
}
|
|
5245
|
-
}
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5752
|
+
}
|
|
5753
|
+
};
|
|
5754
|
+
let i = start2;
|
|
5755
|
+
if (budget.steps <= 0 || now() > budget.deadline) {
|
|
5756
|
+
budget.exhausted = true;
|
|
5757
|
+
return "budget";
|
|
5758
|
+
}
|
|
5759
|
+
let steps = 0;
|
|
5760
|
+
for (; ; ) {
|
|
5761
|
+
if (!matched) add(clistPc, clistCaps, 0, initial, i);
|
|
5762
|
+
const cp = i < end ? hay.codePointAt(i) : -1;
|
|
5763
|
+
const width = cp > 65535 ? 2 : 1;
|
|
5764
|
+
if (clistPc.length === 0) {
|
|
5765
|
+
if (matched || i >= end) break;
|
|
5766
|
+
gen++;
|
|
5767
|
+
i += width;
|
|
5768
|
+
continue;
|
|
5769
|
+
}
|
|
5770
|
+
const folded = cp >= 0 && caseInsensitive ? foldCodePoint(cp) : cp;
|
|
5771
|
+
gen++;
|
|
5772
|
+
nlistPc = [];
|
|
5773
|
+
nlistCaps = [];
|
|
5774
|
+
for (let t = 0; t < clistPc.length; t++) {
|
|
5775
|
+
if (++steps >= 4096) {
|
|
5776
|
+
budget.steps -= steps;
|
|
5777
|
+
steps = 0;
|
|
5778
|
+
if (budget.steps <= 0 || now() > budget.deadline) {
|
|
5779
|
+
budget.exhausted = true;
|
|
5780
|
+
return "budget";
|
|
5781
|
+
}
|
|
5258
5782
|
}
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5783
|
+
const inst = prog[clistPc[t]];
|
|
5784
|
+
if (inst.op === 0 /* Char */) {
|
|
5785
|
+
if (cp >= 0 && inst.test(cp, folded)) add(nlistPc, nlistCaps, clistPc[t] + 1, clistCaps[t], i + width);
|
|
5786
|
+
} else if (inst.op === 5 /* Match */) {
|
|
5787
|
+
matched = clistCaps[t];
|
|
5788
|
+
break;
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
clistPc = nlistPc;
|
|
5792
|
+
clistCaps = nlistCaps;
|
|
5793
|
+
if (i >= end) break;
|
|
5794
|
+
i += width;
|
|
5795
|
+
}
|
|
5796
|
+
budget.steps -= steps;
|
|
5797
|
+
if (!matched) return null;
|
|
5798
|
+
const groups = [];
|
|
5799
|
+
for (let g = 0; g <= groupCount; g++) {
|
|
5800
|
+
const a = matched[2 * g];
|
|
5801
|
+
const b = matched[2 * g + 1];
|
|
5802
|
+
groups.push(a >= 0 && b >= 0 ? hay.slice(a, b) : void 0);
|
|
5803
|
+
}
|
|
5804
|
+
return { start: matched[0], end: matched[1], groups };
|
|
5805
|
+
}
|
|
5806
|
+
var WORD_CLASS = "[\\p{L}\\p{M}\\p{Nd}\\p{Pc}]";
|
|
5807
|
+
var REGEX_BOUNDARY = `(?:(?<=${WORD_CLASS})(?!${WORD_CLASS})|(?<!${WORD_CLASS})(?=${WORD_CLASS}))`;
|
|
5808
|
+
function hasNestedRepeat(source) {
|
|
5809
|
+
const quantifierAt = (i) => {
|
|
5810
|
+
const c = source[i];
|
|
5811
|
+
if (c === "*" || c === "+") return { repeats: true, many: true };
|
|
5812
|
+
if (c === "{") {
|
|
5813
|
+
const m = /^\{(\d*)(,?)(\d*)\}/.exec(source.slice(i));
|
|
5814
|
+
if (m) {
|
|
5815
|
+
const max = m[2] ? m[3] ? Number(m[3]) : Infinity : Number(m[1]);
|
|
5816
|
+
return { repeats: max > 1, many: max > 1 };
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
return { repeats: false, many: false };
|
|
5820
|
+
};
|
|
5821
|
+
const stack = [];
|
|
5822
|
+
let inClass = false;
|
|
5823
|
+
for (let i = 0; i < source.length; i++) {
|
|
5824
|
+
const c = source[i];
|
|
5825
|
+
if (c === "\\") {
|
|
5826
|
+
i++;
|
|
5827
|
+
continue;
|
|
5828
|
+
}
|
|
5829
|
+
if (inClass) {
|
|
5830
|
+
if (c === "]") inClass = false;
|
|
5831
|
+
continue;
|
|
5832
|
+
}
|
|
5833
|
+
if (c === "[") {
|
|
5834
|
+
inClass = true;
|
|
5835
|
+
} else if (c === "(") {
|
|
5836
|
+
stack.push(false);
|
|
5837
|
+
} else if (c === ")") {
|
|
5838
|
+
const inner = stack.pop() ?? false;
|
|
5839
|
+
const q = quantifierAt(i + 1);
|
|
5840
|
+
if (inner && q.many) return true;
|
|
5841
|
+
if (stack.length && (inner || q.repeats)) stack[stack.length - 1] = true;
|
|
5842
|
+
} else if (stack.length && quantifierAt(i).repeats) {
|
|
5843
|
+
stack[stack.length - 1] = true;
|
|
5844
|
+
}
|
|
5274
5845
|
}
|
|
5275
|
-
|
|
5276
|
-
|
|
5846
|
+
return false;
|
|
5847
|
+
}
|
|
5848
|
+
function compileRegex(query, o) {
|
|
5849
|
+
if (hasNestedRepeat(query)) {
|
|
5850
|
+
throw new PatternError(
|
|
5851
|
+
"too_complex",
|
|
5852
|
+
"a repeated group containing a repeat, like (a+)+, can take exponential time; rewrite it without nesting"
|
|
5853
|
+
);
|
|
5277
5854
|
}
|
|
5278
|
-
|
|
5279
|
-
|
|
5855
|
+
let source = query;
|
|
5856
|
+
if (o.wholeWord || o.matchPrefix && o.matchSuffix) source = `${REGEX_BOUNDARY}(?:${query})${REGEX_BOUNDARY}`;
|
|
5857
|
+
else if (o.matchPrefix) source = `${REGEX_BOUNDARY}(?:${query})`;
|
|
5858
|
+
else if (o.matchSuffix) source = `(?:${query})${REGEX_BOUNDARY}`;
|
|
5859
|
+
let re;
|
|
5860
|
+
try {
|
|
5861
|
+
re = new RegExp(source, o.matchCase ? "gu" : "giu");
|
|
5862
|
+
} catch (error) {
|
|
5863
|
+
throw new PatternError("invalid_pattern", `bad pattern: ${error instanceof Error ? error.message : String(error)}`);
|
|
5280
5864
|
}
|
|
5281
|
-
|
|
5282
|
-
|
|
5865
|
+
let slicedFor = null;
|
|
5866
|
+
return {
|
|
5867
|
+
mode: "regex",
|
|
5868
|
+
spansParagraphs: false,
|
|
5869
|
+
caseInsensitive: !o.matchCase,
|
|
5870
|
+
groupCount: 0,
|
|
5871
|
+
exec(hay, start2, end, budget) {
|
|
5872
|
+
if (now() > budget.deadline) {
|
|
5873
|
+
budget.exhausted = true;
|
|
5874
|
+
return "budget";
|
|
5875
|
+
}
|
|
5876
|
+
let text = hay;
|
|
5877
|
+
if (end < hay.length) {
|
|
5878
|
+
if (!slicedFor || slicedFor.hay !== hay || slicedFor.end !== end) {
|
|
5879
|
+
slicedFor = { hay, end, text: hay.slice(0, end) };
|
|
5880
|
+
}
|
|
5881
|
+
text = slicedFor.text;
|
|
5882
|
+
}
|
|
5883
|
+
re.lastIndex = start2;
|
|
5884
|
+
const m = re.exec(text);
|
|
5885
|
+
if (!m) return null;
|
|
5886
|
+
return { start: m.index, end: m.index + m[0].length, groups: Array.from(m) };
|
|
5887
|
+
}
|
|
5888
|
+
};
|
|
5889
|
+
}
|
|
5890
|
+
function compileFindPattern(query, options = {}) {
|
|
5891
|
+
try {
|
|
5892
|
+
if (Array.from(query).length > MAX_FIND_PATTERN_LENGTH) {
|
|
5893
|
+
throw new PatternError("too_long", `the Find what text is longer than ${MAX_FIND_PATTERN_LENGTH} characters`);
|
|
5894
|
+
}
|
|
5895
|
+
if (options.regex) return { ok: true, pattern: compileRegex(query, options) };
|
|
5896
|
+
const wildcards = !!options.wildcards && !options.literal;
|
|
5897
|
+
const caseInsensitive = !options.matchCase && !wildcards;
|
|
5898
|
+
let node;
|
|
5899
|
+
let spans;
|
|
5900
|
+
let groups = 0;
|
|
5901
|
+
if (wildcards) {
|
|
5902
|
+
({ node, spans, groups } = parseWildcards(query));
|
|
5903
|
+
} else {
|
|
5904
|
+
({ node, spans } = parseSpecial(query, options, caseInsensitive));
|
|
5905
|
+
const before = options.wholeWord || options.matchPrefix;
|
|
5906
|
+
const after = options.wholeWord || options.matchSuffix;
|
|
5907
|
+
if (before || after) {
|
|
5908
|
+
const items = [];
|
|
5909
|
+
if (before) items.push({ k: "assert", kind: "boundary" });
|
|
5910
|
+
items.push(node);
|
|
5911
|
+
if (after) items.push({ k: "assert", kind: "boundary" });
|
|
5912
|
+
node = { k: "cat", items };
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
const prog = compileProgram(node);
|
|
5916
|
+
return {
|
|
5917
|
+
ok: true,
|
|
5918
|
+
pattern: {
|
|
5919
|
+
mode: wildcards ? "wildcards" : "special",
|
|
5920
|
+
spansParagraphs: spans,
|
|
5921
|
+
caseInsensitive,
|
|
5922
|
+
groupCount: groups,
|
|
5923
|
+
exec: (hay, start2, end, budget) => pikeExec(prog, groups, caseInsensitive, hay, start2, end, budget)
|
|
5924
|
+
}
|
|
5925
|
+
};
|
|
5926
|
+
} catch (error) {
|
|
5927
|
+
if (error instanceof PatternError) return { ok: false, error: { code: error.code, message: error.message } };
|
|
5928
|
+
return { ok: false, error: { code: "invalid_pattern", message: error instanceof Error ? error.message : String(error) } };
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
function findAllMatches(pattern, hay, options = {}) {
|
|
5932
|
+
const budget = options.budget ?? createSearchBudget();
|
|
5933
|
+
const maxMatches = options.maxMatches ?? 1e4;
|
|
5934
|
+
const cuts = [...options.cuts ?? [], hay.length].sort((a, b) => a - b);
|
|
5935
|
+
const matches = [];
|
|
5936
|
+
let from = 0;
|
|
5937
|
+
for (const cut of cuts) {
|
|
5938
|
+
if (cut <= from) continue;
|
|
5939
|
+
let pos = from;
|
|
5940
|
+
from = cut;
|
|
5941
|
+
for (; ; ) {
|
|
5942
|
+
const m = pattern.exec(hay, pos, cut, budget);
|
|
5943
|
+
if (m === "budget") return { matches, truncated: true };
|
|
5944
|
+
if (!m) break;
|
|
5945
|
+
const empty = m.end === m.start;
|
|
5946
|
+
const next = empty ? m.end + (cpAt(hay, m.end) > 65535 ? 2 : 1) : m.end;
|
|
5947
|
+
if (next > cut) break;
|
|
5948
|
+
pos = next;
|
|
5949
|
+
if (empty) continue;
|
|
5950
|
+
matches.push(m);
|
|
5951
|
+
if (matches.length >= maxMatches) return { matches, truncated: true };
|
|
5952
|
+
}
|
|
5953
|
+
}
|
|
5954
|
+
return { matches, truncated: false };
|
|
5955
|
+
}
|
|
5956
|
+
function parseReplacement(replacement, options = {}) {
|
|
5957
|
+
if (options.literal || options.regex) return replacement ? [{ type: "text", text: replacement }] : [];
|
|
5958
|
+
const chars = toChars(replacement);
|
|
5959
|
+
const out = [];
|
|
5960
|
+
let cur = "";
|
|
5961
|
+
const flush = () => {
|
|
5962
|
+
if (cur) out.push({ type: "text", text: cur });
|
|
5963
|
+
cur = "";
|
|
5964
|
+
};
|
|
5965
|
+
let i = 0;
|
|
5966
|
+
while (i < chars.length) {
|
|
5967
|
+
const c = chars[i++];
|
|
5968
|
+
if (c === "\\" && options.wildcards && i < chars.length) {
|
|
5969
|
+
const d = chars[i];
|
|
5970
|
+
if (/[1-9]/.test(d)) {
|
|
5971
|
+
flush();
|
|
5972
|
+
out.push({ type: "group", index: Number(d) });
|
|
5973
|
+
} else {
|
|
5974
|
+
cur += d;
|
|
5975
|
+
}
|
|
5976
|
+
i++;
|
|
5977
|
+
continue;
|
|
5978
|
+
}
|
|
5979
|
+
if (c === "^" && i < chars.length) {
|
|
5980
|
+
const n = chars[i];
|
|
5981
|
+
if (n === "&" || n === "c") {
|
|
5982
|
+
flush();
|
|
5983
|
+
out.push(n === "&" ? { type: "found" } : { type: "clipboard" });
|
|
5984
|
+
i++;
|
|
5985
|
+
continue;
|
|
5986
|
+
}
|
|
5987
|
+
const code = replaceCode(n);
|
|
5988
|
+
if (code !== null) {
|
|
5989
|
+
cur += code;
|
|
5990
|
+
i++;
|
|
5991
|
+
continue;
|
|
5992
|
+
}
|
|
5993
|
+
const num = numericCode(chars, i);
|
|
5994
|
+
if (num) {
|
|
5995
|
+
cur += String.fromCodePoint(num.cp);
|
|
5996
|
+
i += num.used;
|
|
5997
|
+
continue;
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
cur += c;
|
|
6001
|
+
}
|
|
6002
|
+
flush();
|
|
6003
|
+
return out;
|
|
6004
|
+
}
|
|
6005
|
+
function expandReplacement(pieces, match, clipboardText = "") {
|
|
6006
|
+
let out = "";
|
|
6007
|
+
for (const piece of pieces) {
|
|
6008
|
+
switch (piece.type) {
|
|
6009
|
+
case "text":
|
|
6010
|
+
out += piece.text;
|
|
6011
|
+
break;
|
|
6012
|
+
case "found":
|
|
6013
|
+
out += match.groups[0] ?? "";
|
|
6014
|
+
break;
|
|
6015
|
+
case "group":
|
|
6016
|
+
out += match.groups[piece.index] ?? "";
|
|
6017
|
+
break;
|
|
6018
|
+
case "clipboard":
|
|
6019
|
+
out += clipboardText.replace(/\r\n/g, "\r").replace(/\n/g, "\r");
|
|
6020
|
+
break;
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
return out;
|
|
6024
|
+
}
|
|
6025
|
+
|
|
6026
|
+
// src/extensions/find-replace/search.ts
|
|
6027
|
+
var DELETION_MARK = "deletion";
|
|
6028
|
+
var DROPPED_REPLACEMENT_MARKS = /* @__PURE__ */ new Set([DELETION_MARK, "formatChange"]);
|
|
6029
|
+
var OBJECT_CHAR = "\uFFFC";
|
|
6030
|
+
function searchDocument(doc, query, options = {}, searchOptions = {}) {
|
|
6031
|
+
if (!query) return { matches: [], truncated: false, error: null };
|
|
6032
|
+
const compiled = compileFindPattern(query, options);
|
|
6033
|
+
if (!compiled.ok) return { matches: [], truncated: false, error: compiled.error };
|
|
6034
|
+
const pattern = compiled.pattern;
|
|
6035
|
+
const pieces = parseReplacement(searchOptions.replacement ?? "", options);
|
|
6036
|
+
const budget = createSearchBudget({ timeLimitMs: searchOptions.timeLimitMs });
|
|
6037
|
+
const maxMatches = searchOptions.maxMatches ?? 1e4;
|
|
6038
|
+
const blocks = [];
|
|
6039
|
+
doc.descendants((node, pos, parent, index) => {
|
|
6040
|
+
if (!node.isTextblock) return true;
|
|
6041
|
+
blocks.push({ node, pos, parent, index });
|
|
6042
|
+
return false;
|
|
6043
|
+
});
|
|
6044
|
+
const chunks = [];
|
|
6045
|
+
blocks.forEach((block, i) => {
|
|
6046
|
+
const prev = blocks[i - 1];
|
|
6047
|
+
const joins = pattern.spansParagraphs && prev && prev.parent === block.parent && prev.index + 1 === block.index;
|
|
6048
|
+
if (joins) chunks[chunks.length - 1].push(block);
|
|
6049
|
+
else chunks.push([block]);
|
|
6050
|
+
});
|
|
6051
|
+
const matches = [];
|
|
6052
|
+
let truncated = false;
|
|
6053
|
+
for (const chunk of chunks) {
|
|
6054
|
+
let hay = "";
|
|
6055
|
+
const posOf = [];
|
|
6056
|
+
const endOf = [];
|
|
6057
|
+
const cuts = [];
|
|
6058
|
+
chunk.forEach((block, k) => {
|
|
6059
|
+
const contentStart = block.pos + 1;
|
|
6060
|
+
block.node.forEach((child, offset) => {
|
|
6061
|
+
const at = contentStart + offset;
|
|
6062
|
+
if (child.marks.some((m) => m.type.name === DELETION_MARK)) {
|
|
6063
|
+
if (cuts[cuts.length - 1] !== hay.length) cuts.push(hay.length);
|
|
6064
|
+
return;
|
|
6065
|
+
}
|
|
6066
|
+
if (child.isText) {
|
|
6067
|
+
const text = child.text ?? "";
|
|
6068
|
+
for (let j = 0; j < text.length; j++) {
|
|
6069
|
+
posOf.push(at + j);
|
|
6070
|
+
endOf.push(at + j + 1);
|
|
6071
|
+
}
|
|
6072
|
+
hay += text;
|
|
6073
|
+
} else {
|
|
6074
|
+
posOf.push(at);
|
|
6075
|
+
endOf.push(at + child.nodeSize);
|
|
6076
|
+
hay += child.type.name === "hardBreak" ? "\n" : OBJECT_CHAR;
|
|
6077
|
+
}
|
|
6078
|
+
});
|
|
6079
|
+
const next = chunk[k + 1];
|
|
6080
|
+
if (next) {
|
|
6081
|
+
posOf.push(contentStart + block.node.content.size);
|
|
6082
|
+
endOf.push(next.pos + 1);
|
|
6083
|
+
hay += FIND_PARAGRAPH_MARK;
|
|
6084
|
+
}
|
|
6085
|
+
});
|
|
6086
|
+
if (!hay) continue;
|
|
6087
|
+
const result = findAllMatches(pattern, hay, {
|
|
6088
|
+
cuts,
|
|
6089
|
+
budget,
|
|
6090
|
+
maxMatches: maxMatches - matches.length
|
|
6091
|
+
});
|
|
6092
|
+
for (const m of result.matches) {
|
|
6093
|
+
matches.push({
|
|
6094
|
+
from: posOf[m.start],
|
|
6095
|
+
to: endOf[m.end - 1],
|
|
6096
|
+
text: m.groups[0] ?? hay.slice(m.start, m.end),
|
|
6097
|
+
replacement: expandReplacement(pieces, m, searchOptions.clipboardText ?? "")
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
if (result.truncated) {
|
|
6101
|
+
truncated = true;
|
|
6102
|
+
break;
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
return { matches, truncated, error: null };
|
|
6106
|
+
}
|
|
6107
|
+
function replacementMarks(doc, from, to) {
|
|
6108
|
+
const $from = doc.resolve(from);
|
|
6109
|
+
const node = from < to ? $from.nodeAfter : null;
|
|
6110
|
+
const marks = node ? node.marks : $from.marks();
|
|
6111
|
+
return marks.filter((m) => !DROPPED_REPLACEMENT_MARKS.has(m.type.name));
|
|
6112
|
+
}
|
|
6113
|
+
function inlineContent(schema, text, marks) {
|
|
6114
|
+
const nodes = [];
|
|
6115
|
+
const hardBreak = schema.nodes.hardBreak;
|
|
6116
|
+
const parts = hardBreak ? text.split("\n") : [text];
|
|
6117
|
+
parts.forEach((part, i) => {
|
|
6118
|
+
if (i > 0) nodes.push(hardBreak.create(null, null, marks));
|
|
6119
|
+
if (part) nodes.push(schema.text(part, marks));
|
|
6120
|
+
});
|
|
6121
|
+
return nodes;
|
|
6122
|
+
}
|
|
6123
|
+
function applyReplacements(tr, matches) {
|
|
6124
|
+
const ends = new Array(matches.length);
|
|
6125
|
+
const schema = tr.doc.type.schema;
|
|
6126
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
6127
|
+
const { from, to, replacement } = matches[i];
|
|
6128
|
+
const marks = replacementMarks(tr.doc, from, to);
|
|
6129
|
+
const parts = replacement.split(/[\r\u2029]/);
|
|
6130
|
+
const stepsBefore = tr.steps.length;
|
|
6131
|
+
const first = inlineContent(schema, parts[0], marks);
|
|
6132
|
+
if (first.length) tr.replaceWith(from, to, first);
|
|
6133
|
+
else if (to > from) tr.delete(from, to);
|
|
6134
|
+
let pos = tr.mapping.slice(stepsBefore).map(to, 1);
|
|
6135
|
+
for (let k = 1; k < parts.length; k++) {
|
|
6136
|
+
tr.split(pos);
|
|
6137
|
+
pos += 2;
|
|
6138
|
+
const content = inlineContent(schema, parts[k], marks);
|
|
6139
|
+
if (content.length) {
|
|
6140
|
+
tr.insert(pos, content);
|
|
6141
|
+
pos += content.reduce((size, node) => size + node.nodeSize, 0);
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
const map = tr.mapping.slice(stepsBefore);
|
|
6145
|
+
for (let j = i + 1; j < matches.length; j++) ends[j] = map.map(ends[j], 1);
|
|
6146
|
+
ends[i] = pos;
|
|
6147
|
+
}
|
|
6148
|
+
return ends;
|
|
6149
|
+
}
|
|
6150
|
+
|
|
6151
|
+
// src/extensions/find-replace/extension.ts
|
|
6152
|
+
var EMPTY_OPTIONS = Object.freeze({});
|
|
6153
|
+
var EMPTY_FIND_REPLACE_SNAPSHOT = Object.freeze({
|
|
6154
|
+
open: false,
|
|
6155
|
+
mode: "find",
|
|
6156
|
+
query: "",
|
|
6157
|
+
replacement: "",
|
|
6158
|
+
options: EMPTY_OPTIONS,
|
|
6159
|
+
matches: Object.freeze([]),
|
|
6160
|
+
currentIndex: -1,
|
|
6161
|
+
error: null,
|
|
6162
|
+
truncated: false,
|
|
6163
|
+
lastReplaceCount: null,
|
|
6164
|
+
focusRequest: 0
|
|
6165
|
+
});
|
|
6166
|
+
var FindReplaceStore = class {
|
|
6167
|
+
snapshot = EMPTY_FIND_REPLACE_SNAPSHOT;
|
|
6168
|
+
listeners = /* @__PURE__ */ new Set();
|
|
6169
|
+
subscribe = (listener) => {
|
|
6170
|
+
this.listeners.add(listener);
|
|
6171
|
+
listener(this.snapshot);
|
|
6172
|
+
return () => {
|
|
6173
|
+
this.listeners.delete(listener);
|
|
6174
|
+
};
|
|
6175
|
+
};
|
|
6176
|
+
getSnapshot = () => this.snapshot;
|
|
6177
|
+
/** @internal Called by the extension's plugin view. */
|
|
6178
|
+
set(snapshot) {
|
|
6179
|
+
if (snapshot === this.snapshot) return;
|
|
6180
|
+
this.snapshot = snapshot;
|
|
6181
|
+
this.listeners.forEach((listener) => {
|
|
6182
|
+
try {
|
|
6183
|
+
listener(snapshot);
|
|
6184
|
+
} catch (err) {
|
|
6185
|
+
console.error("[FindReplaceStore] Error in listener:", err);
|
|
6186
|
+
}
|
|
6187
|
+
});
|
|
6188
|
+
}
|
|
6189
|
+
};
|
|
6190
|
+
var findReplacePluginKey = new PluginKey11("findReplace");
|
|
6191
|
+
function getFindReplaceState(source) {
|
|
6192
|
+
const state = source?.state ?? source;
|
|
6193
|
+
if (!state || typeof state.plugins === "undefined") return null;
|
|
6194
|
+
const plugin = findReplacePluginKey.getState(state);
|
|
6195
|
+
return plugin ? toSnapshot(plugin) : null;
|
|
6196
|
+
}
|
|
6197
|
+
function getFindReplaceStore(editor) {
|
|
6198
|
+
return editor?.storage?.findReplace?.store ?? null;
|
|
6199
|
+
}
|
|
6200
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
6201
|
+
function toSnapshot(s) {
|
|
6202
|
+
let snapshot = snapshotCache.get(s);
|
|
6203
|
+
if (!snapshot) {
|
|
6204
|
+
snapshot = Object.freeze({
|
|
6205
|
+
open: s.open,
|
|
6206
|
+
mode: s.mode,
|
|
6207
|
+
query: s.query,
|
|
6208
|
+
replacement: s.replacement,
|
|
6209
|
+
options: s.options,
|
|
6210
|
+
matches: s.active ? s.matches : EMPTY_FIND_REPLACE_SNAPSHOT.matches,
|
|
6211
|
+
currentIndex: s.active ? s.currentIndex : -1,
|
|
6212
|
+
error: s.error,
|
|
6213
|
+
truncated: s.truncated,
|
|
6214
|
+
lastReplaceCount: s.lastReplaceCount,
|
|
6215
|
+
focusRequest: s.focusRequest
|
|
6216
|
+
});
|
|
6217
|
+
snapshotCache.set(s, snapshot);
|
|
6218
|
+
}
|
|
6219
|
+
return snapshot;
|
|
6220
|
+
}
|
|
6221
|
+
var FIND_OPTION_TOGGLES = Object.freeze([
|
|
6222
|
+
{ key: "matchCase", label: "Match case", short: "Aa", advanced: false },
|
|
6223
|
+
{ key: "wholeWord", label: "Whole words only", short: "ab", advanced: false },
|
|
6224
|
+
{ key: "wildcards", label: "Use wildcards", short: "*?", advanced: false },
|
|
6225
|
+
{ key: "regex", label: "Regular expression", short: ".*", advanced: false },
|
|
6226
|
+
{ key: "matchPrefix", label: "Match prefix", short: "ab-", advanced: true },
|
|
6227
|
+
{ key: "matchSuffix", label: "Match suffix", short: "-ab", advanced: true },
|
|
6228
|
+
{ key: "ignorePunctuation", label: "Ignore punctuation", short: ".,", advanced: true },
|
|
6229
|
+
{ key: "ignoreWhitespace", label: "Ignore white space", short: "_", advanced: true },
|
|
6230
|
+
{ key: "literal", label: "No special characters (^)", short: "^", advanced: true }
|
|
6231
|
+
]);
|
|
6232
|
+
function formatFindStatus(s) {
|
|
6233
|
+
if (!s.query) return "";
|
|
6234
|
+
if (s.error) return s.error.message;
|
|
6235
|
+
const n = s.matches.length;
|
|
6236
|
+
if (n === 0) return "No results";
|
|
6237
|
+
const total = s.truncated ? `${n}+` : `${n}`;
|
|
6238
|
+
return s.currentIndex >= 0 ? `${s.currentIndex + 1} of ${total}` : `${total} results`;
|
|
6239
|
+
}
|
|
6240
|
+
function indexFrom(matches, pos) {
|
|
6241
|
+
if (!matches.length) return -1;
|
|
6242
|
+
const i = matches.findIndex((m) => m.from >= pos);
|
|
6243
|
+
return i < 0 ? 0 : i;
|
|
6244
|
+
}
|
|
6245
|
+
function selectedQuery(state, maxLength) {
|
|
6246
|
+
const { from, to, $from, $to } = state.selection;
|
|
6247
|
+
if (from === to || !$from.sameParent($to)) return void 0;
|
|
6248
|
+
const text = state.doc.textBetween(from, to, "", "");
|
|
6249
|
+
return text && text.length <= maxLength && !/[\r\n]/.test(text) ? text : void 0;
|
|
6250
|
+
}
|
|
6251
|
+
var FindReplace = Extension12.create({
|
|
6252
|
+
name: "findReplace",
|
|
6253
|
+
addOptions() {
|
|
6254
|
+
return {
|
|
6255
|
+
shortcuts: true,
|
|
6256
|
+
matchClass: "pixerate-find-match",
|
|
6257
|
+
currentMatchClass: "pixerate-find-match-current",
|
|
6258
|
+
timeLimitMs: 250,
|
|
6259
|
+
maxMatches: 1e4,
|
|
6260
|
+
getClipboardText: () => "",
|
|
6261
|
+
maxPrefillLength: 256
|
|
6262
|
+
};
|
|
6263
|
+
},
|
|
6264
|
+
addStorage() {
|
|
6265
|
+
return { store: new FindReplaceStore() };
|
|
6266
|
+
},
|
|
6267
|
+
addCommands() {
|
|
6268
|
+
const meta = (tr, value) => tr.setMeta(findReplacePluginKey, value);
|
|
6269
|
+
const current = (state) => findReplacePluginKey.getState(state);
|
|
6270
|
+
const maxPrefill = () => this.options.maxPrefillLength;
|
|
6271
|
+
const select = (tr, match) => {
|
|
6272
|
+
tr.setSelection(TextSelection7.create(tr.doc, match.from, match.to)).scrollIntoView();
|
|
6273
|
+
};
|
|
6274
|
+
return {
|
|
6275
|
+
setSearch: (params) => ({ tr, state, dispatch }) => {
|
|
6276
|
+
if (!current(state)) return false;
|
|
6277
|
+
if (dispatch) meta(tr, { type: "set", params, anchor: state.selection.from });
|
|
6278
|
+
return true;
|
|
6279
|
+
},
|
|
6280
|
+
openFindReplace: (params = {}) => ({ tr, state, dispatch }) => {
|
|
6281
|
+
if (!current(state)) return false;
|
|
6282
|
+
if (dispatch) {
|
|
6283
|
+
meta(tr, {
|
|
6284
|
+
type: "open",
|
|
6285
|
+
mode: params.mode ?? "find",
|
|
6286
|
+
query: params.query ?? selectedQuery(state, maxPrefill()),
|
|
6287
|
+
anchor: state.selection.from
|
|
6288
|
+
});
|
|
6289
|
+
}
|
|
6290
|
+
return true;
|
|
6291
|
+
},
|
|
6292
|
+
closeFindReplace: () => ({ tr, state, dispatch }) => {
|
|
6293
|
+
if (!current(state)) return false;
|
|
6294
|
+
if (dispatch) meta(tr, { type: "close" });
|
|
6295
|
+
return true;
|
|
6296
|
+
},
|
|
6297
|
+
clearSearch: () => ({ tr, state, dispatch }) => {
|
|
6298
|
+
if (!current(state)) return false;
|
|
6299
|
+
if (dispatch) meta(tr, { type: "clear" });
|
|
6300
|
+
return true;
|
|
6301
|
+
},
|
|
6302
|
+
findNext: () => ({ tr, state, dispatch }) => {
|
|
6303
|
+
const s = current(state);
|
|
6304
|
+
if (!s || !s.active || !s.matches.length) return false;
|
|
6305
|
+
const { from, to } = state.selection;
|
|
6306
|
+
const cur = s.matches[s.currentIndex];
|
|
6307
|
+
const onCurrent = cur && cur.from === from && cur.to === to;
|
|
6308
|
+
const index = onCurrent ? (s.currentIndex + 1) % s.matches.length : indexFrom(s.matches, to);
|
|
6309
|
+
if (dispatch) {
|
|
6310
|
+
meta(tr, { type: "current", index });
|
|
6311
|
+
select(tr, s.matches[index]);
|
|
6312
|
+
}
|
|
6313
|
+
return true;
|
|
6314
|
+
},
|
|
6315
|
+
findPrevious: () => ({ tr, state, dispatch }) => {
|
|
6316
|
+
const s = current(state);
|
|
6317
|
+
if (!s || !s.active || !s.matches.length) return false;
|
|
6318
|
+
const { from, to } = state.selection;
|
|
6319
|
+
const n = s.matches.length;
|
|
6320
|
+
const cur = s.matches[s.currentIndex];
|
|
6321
|
+
const onCurrent = cur && cur.from === from && cur.to === to;
|
|
6322
|
+
let index;
|
|
6323
|
+
if (onCurrent) {
|
|
6324
|
+
index = (s.currentIndex - 1 + n) % n;
|
|
6325
|
+
} else {
|
|
6326
|
+
index = -1;
|
|
6327
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
6328
|
+
if (s.matches[i].to <= from) {
|
|
6329
|
+
index = i;
|
|
6330
|
+
break;
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
if (index < 0) index = n - 1;
|
|
6334
|
+
}
|
|
6335
|
+
if (dispatch) {
|
|
6336
|
+
meta(tr, { type: "current", index });
|
|
6337
|
+
select(tr, s.matches[index]);
|
|
6338
|
+
}
|
|
6339
|
+
return true;
|
|
6340
|
+
},
|
|
6341
|
+
replaceCurrent: () => ({ tr, state, dispatch }) => {
|
|
6342
|
+
const s = current(state);
|
|
6343
|
+
if (!s || !s.active || s.currentIndex < 0 || !s.matches[s.currentIndex]) return false;
|
|
6344
|
+
if (dispatch) {
|
|
6345
|
+
closeHistory2(tr);
|
|
6346
|
+
const [end] = applyReplacements(tr, [s.matches[s.currentIndex]]);
|
|
6347
|
+
tr.setSelection(TextSelection7.create(tr.doc, end)).scrollIntoView();
|
|
6348
|
+
meta(tr, { type: "replaced", anchor: end, count: 1 });
|
|
6349
|
+
}
|
|
6350
|
+
return true;
|
|
6351
|
+
},
|
|
6352
|
+
replaceAll: () => ({ tr, state, dispatch }) => {
|
|
6353
|
+
const s = current(state);
|
|
6354
|
+
if (!s || !s.active || !s.matches.length) return false;
|
|
6355
|
+
if (dispatch) {
|
|
6356
|
+
closeHistory2(tr);
|
|
6357
|
+
const ends = applyReplacements(tr, s.matches);
|
|
6358
|
+
meta(tr, { type: "replaced", anchor: ends[ends.length - 1], count: s.matches.length });
|
|
6359
|
+
}
|
|
6360
|
+
return true;
|
|
6361
|
+
}
|
|
6362
|
+
};
|
|
6363
|
+
},
|
|
6364
|
+
addKeyboardShortcuts() {
|
|
6365
|
+
if (!this.options.shortcuts) return {};
|
|
6366
|
+
const open = (mode) => () => this.editor.commands.openFindReplace({ mode });
|
|
6367
|
+
const step = (forward) => () => {
|
|
6368
|
+
const s = findReplacePluginKey.getState(this.editor.state);
|
|
6369
|
+
if (!s?.active || !s.matches.length) return false;
|
|
6370
|
+
return forward ? this.editor.commands.findNext() : this.editor.commands.findPrevious();
|
|
6371
|
+
};
|
|
6372
|
+
return {
|
|
6373
|
+
"Mod-f": open("find"),
|
|
6374
|
+
"Mod-h": open("replace"),
|
|
6375
|
+
"Mod-Shift-h": open("replace"),
|
|
6376
|
+
F3: step(true),
|
|
6377
|
+
"Shift-F3": step(false),
|
|
6378
|
+
"Mod-g": step(true),
|
|
6379
|
+
"Mod-Shift-g": step(false)
|
|
6380
|
+
};
|
|
6381
|
+
},
|
|
6382
|
+
addProseMirrorPlugins() {
|
|
6383
|
+
const options = this.options;
|
|
6384
|
+
const store = this.storage.store;
|
|
6385
|
+
const search = (s, doc) => {
|
|
6386
|
+
if (!s.active || !s.query) {
|
|
6387
|
+
return { ...s, matches: [], currentIndex: -1, error: null, truncated: false };
|
|
6388
|
+
}
|
|
6389
|
+
const result = searchDocument(doc, s.query, s.options, {
|
|
6390
|
+
replacement: s.replacement,
|
|
6391
|
+
clipboardText: safeClipboard(options.getClipboardText),
|
|
6392
|
+
timeLimitMs: options.timeLimitMs,
|
|
6393
|
+
maxMatches: options.maxMatches
|
|
6394
|
+
});
|
|
6395
|
+
return {
|
|
6396
|
+
...s,
|
|
6397
|
+
matches: result.matches,
|
|
6398
|
+
currentIndex: indexFrom(result.matches, s.anchor),
|
|
6399
|
+
error: result.error,
|
|
6400
|
+
truncated: result.truncated
|
|
6401
|
+
};
|
|
6402
|
+
};
|
|
6403
|
+
const decorate = (s, doc) => {
|
|
6404
|
+
if (!s.active || !s.matches.length) return { ...s, decorations: DecorationSet4.empty };
|
|
6405
|
+
const decorations = s.matches.map(
|
|
6406
|
+
(m, i) => Decoration4.inline(m.from, m.to, {
|
|
6407
|
+
class: i === s.currentIndex ? `${options.matchClass} ${options.currentMatchClass}` : options.matchClass
|
|
6408
|
+
})
|
|
6409
|
+
);
|
|
6410
|
+
return { ...s, decorations: DecorationSet4.create(doc, decorations) };
|
|
6411
|
+
};
|
|
6412
|
+
return [
|
|
6413
|
+
new Plugin14({
|
|
6414
|
+
key: findReplacePluginKey,
|
|
6415
|
+
state: {
|
|
6416
|
+
init: () => ({
|
|
6417
|
+
active: false,
|
|
6418
|
+
open: false,
|
|
6419
|
+
mode: "find",
|
|
6420
|
+
query: "",
|
|
6421
|
+
replacement: "",
|
|
6422
|
+
options: EMPTY_OPTIONS,
|
|
6423
|
+
matches: [],
|
|
6424
|
+
currentIndex: -1,
|
|
6425
|
+
anchor: 0,
|
|
6426
|
+
error: null,
|
|
6427
|
+
truncated: false,
|
|
6428
|
+
lastReplaceCount: null,
|
|
6429
|
+
focusRequest: 0,
|
|
6430
|
+
decorations: DecorationSet4.empty
|
|
6431
|
+
}),
|
|
6432
|
+
apply: (tr, prev, _oldState, newState) => {
|
|
6433
|
+
const m = tr.getMeta(findReplacePluginKey);
|
|
6434
|
+
if (!m && !tr.docChanged) return prev;
|
|
6435
|
+
let s = prev;
|
|
6436
|
+
let research = false;
|
|
6437
|
+
let redecorate = false;
|
|
6438
|
+
if (tr.docChanged) {
|
|
6439
|
+
s = { ...s, anchor: tr.mapping.map(s.anchor) };
|
|
6440
|
+
research = s.active;
|
|
6441
|
+
}
|
|
6442
|
+
if (m) {
|
|
6443
|
+
switch (m.type) {
|
|
6444
|
+
case "set": {
|
|
6445
|
+
const p = m.params;
|
|
6446
|
+
s = {
|
|
6447
|
+
...s,
|
|
6448
|
+
active: true,
|
|
6449
|
+
query: p.query ?? s.query,
|
|
6450
|
+
replacement: p.replacement ?? s.replacement,
|
|
6451
|
+
options: p.options ? Object.freeze({ ...s.options, ...p.options }) : s.options,
|
|
6452
|
+
anchor: m.anchor,
|
|
6453
|
+
lastReplaceCount: null
|
|
6454
|
+
};
|
|
6455
|
+
research = true;
|
|
6456
|
+
break;
|
|
6457
|
+
}
|
|
6458
|
+
case "open":
|
|
6459
|
+
s = {
|
|
6460
|
+
...s,
|
|
6461
|
+
active: true,
|
|
6462
|
+
open: true,
|
|
6463
|
+
mode: m.mode,
|
|
6464
|
+
query: m.query ?? s.query,
|
|
6465
|
+
anchor: m.anchor,
|
|
6466
|
+
focusRequest: s.focusRequest + 1
|
|
6467
|
+
};
|
|
6468
|
+
research = true;
|
|
6469
|
+
break;
|
|
6470
|
+
case "close":
|
|
6471
|
+
s = { ...s, open: false, active: false };
|
|
6472
|
+
research = true;
|
|
6473
|
+
break;
|
|
6474
|
+
case "clear":
|
|
6475
|
+
s = { ...s, active: false, query: "", lastReplaceCount: null };
|
|
6476
|
+
research = true;
|
|
6477
|
+
break;
|
|
6478
|
+
case "current":
|
|
6479
|
+
s = {
|
|
6480
|
+
...s,
|
|
6481
|
+
currentIndex: m.index,
|
|
6482
|
+
anchor: s.matches[m.index]?.from ?? s.anchor
|
|
6483
|
+
};
|
|
6484
|
+
redecorate = true;
|
|
6485
|
+
break;
|
|
6486
|
+
case "replaced":
|
|
6487
|
+
s = { ...s, anchor: m.anchor, lastReplaceCount: m.count };
|
|
6488
|
+
research = true;
|
|
6489
|
+
break;
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
if (research) s = search(s, newState.doc);
|
|
6493
|
+
if (research || redecorate) s = decorate(s, newState.doc);
|
|
6494
|
+
return s;
|
|
6495
|
+
}
|
|
6496
|
+
},
|
|
6497
|
+
props: {
|
|
6498
|
+
decorations: (state) => findReplacePluginKey.getState(state)?.decorations ?? DecorationSet4.empty
|
|
6499
|
+
},
|
|
6500
|
+
view: (view) => {
|
|
6501
|
+
const sync = () => {
|
|
6502
|
+
const s = findReplacePluginKey.getState(view.state);
|
|
6503
|
+
if (s) store.set(toSnapshot(s));
|
|
6504
|
+
};
|
|
6505
|
+
sync();
|
|
6506
|
+
return { update: sync };
|
|
6507
|
+
}
|
|
6508
|
+
})
|
|
6509
|
+
];
|
|
6510
|
+
}
|
|
6511
|
+
});
|
|
6512
|
+
function safeClipboard(get) {
|
|
6513
|
+
try {
|
|
6514
|
+
return String(get() ?? "");
|
|
6515
|
+
} catch {
|
|
6516
|
+
return "";
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
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
|
+
|
|
7383
|
+
// src/extensions/rich-text-preset.ts
|
|
7384
|
+
var resolveExt3 = (mod, name) => mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;
|
|
7385
|
+
var StarterKit = resolveExt3(_StarterKit, "StarterKit");
|
|
7386
|
+
var Color = resolveExt3(_Color, "Color");
|
|
7387
|
+
var Highlight = resolveExt3(_Highlight, "Highlight");
|
|
7388
|
+
var Link2 = resolveExt3(_Link, "Link");
|
|
7389
|
+
var TaskList2 = resolveExt3(_TaskList, "TaskList");
|
|
7390
|
+
var TaskItem2 = resolveExt3(_TaskItem, "TaskItem");
|
|
7391
|
+
var TextAlign = resolveExt3(_TextAlign, "TextAlign");
|
|
7392
|
+
var TextStyle = resolveExt3(_TextStyle, "TextStyle");
|
|
7393
|
+
var Typography = resolveExt3(_Typography, "Typography");
|
|
7394
|
+
var Underline = resolveExt3(_Underline, "Underline");
|
|
7395
|
+
var Placeholder = resolveExt3(_Placeholder, "Placeholder");
|
|
7396
|
+
var CharacterCount = resolveExt3(_CharacterCount, "CharacterCount");
|
|
7397
|
+
var optionObject = (value) => typeof value === "object" && value !== null ? value : {};
|
|
7398
|
+
function createRichTextPreset(options = {}) {
|
|
7399
|
+
const {
|
|
7400
|
+
placeholder = "Start typing...",
|
|
7401
|
+
characterLimit,
|
|
7402
|
+
openLinksOnClick = false,
|
|
7403
|
+
enableSmilies = true,
|
|
7404
|
+
enableColorHighlighter = true,
|
|
7405
|
+
headingLevels: headingLevels2 = [1, 2, 3, 4]
|
|
7406
|
+
} = options;
|
|
7407
|
+
const extensions2 = [
|
|
7408
|
+
StarterKit.configure({
|
|
7409
|
+
orderedList: {
|
|
7410
|
+
HTMLAttributes: {
|
|
7411
|
+
class: "list-decimal pl-4"
|
|
7412
|
+
}
|
|
7413
|
+
},
|
|
7414
|
+
bulletList: {
|
|
7415
|
+
HTMLAttributes: {
|
|
7416
|
+
class: "list-disc pl-4"
|
|
7417
|
+
}
|
|
7418
|
+
},
|
|
7419
|
+
heading: {
|
|
7420
|
+
levels: headingLevels2,
|
|
7421
|
+
HTMLAttributes: {
|
|
7422
|
+
class: "font-bold tracking-tight"
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
}),
|
|
7426
|
+
TextStyle,
|
|
7427
|
+
Color,
|
|
7428
|
+
Highlight.configure({ multicolor: true }),
|
|
7429
|
+
Underline,
|
|
7430
|
+
...options.autocorrect ? [] : [Typography],
|
|
7431
|
+
TextAlign.configure({
|
|
7432
|
+
types: ["heading", "paragraph"]
|
|
7433
|
+
}),
|
|
7434
|
+
Link2.extend({
|
|
7435
|
+
addInputRules() {
|
|
7436
|
+
return [
|
|
7437
|
+
markInputRule({
|
|
7438
|
+
find: /(?:^|\s)\[([^\]]+)\]\(([^)]+)\)$/,
|
|
7439
|
+
type: this.type,
|
|
7440
|
+
getAttributes: (match) => ({
|
|
7441
|
+
href: match[2]
|
|
7442
|
+
})
|
|
7443
|
+
})
|
|
7444
|
+
];
|
|
7445
|
+
}
|
|
7446
|
+
}).configure({
|
|
7447
|
+
openOnClick: openLinksOnClick,
|
|
7448
|
+
autolink: true,
|
|
7449
|
+
defaultProtocol: "https",
|
|
7450
|
+
protocols: ["file", "vscode", "cursor"],
|
|
7451
|
+
HTMLAttributes: {
|
|
7452
|
+
target: "_blank",
|
|
7453
|
+
rel: "noopener noreferrer",
|
|
7454
|
+
class: "text-blue-500 underline underline-offset-2"
|
|
7455
|
+
}
|
|
7456
|
+
}),
|
|
7457
|
+
TaskList2,
|
|
7458
|
+
TaskItem2.extend({
|
|
7459
|
+
addInputRules() {
|
|
7460
|
+
return [
|
|
7461
|
+
wrappingInputRule({
|
|
7462
|
+
find: /^\s*(\[([ |x])?\])\s$/i,
|
|
7463
|
+
type: this.type,
|
|
7464
|
+
getAttributes: (match) => ({
|
|
7465
|
+
checked: match[2]?.toLowerCase() === "x"
|
|
7466
|
+
})
|
|
7467
|
+
})
|
|
7468
|
+
];
|
|
7469
|
+
}
|
|
7470
|
+
}).configure({
|
|
7471
|
+
nested: true
|
|
7472
|
+
}),
|
|
7473
|
+
FontSize,
|
|
7474
|
+
Placeholder.configure({
|
|
7475
|
+
emptyEditorClass: "is-empty",
|
|
7476
|
+
placeholder: typeof placeholder === "function" ? placeholder : () => placeholder
|
|
7477
|
+
})
|
|
7478
|
+
];
|
|
7479
|
+
if (characterLimit) {
|
|
7480
|
+
extensions2.push(
|
|
7481
|
+
CharacterCount.configure({
|
|
7482
|
+
limit: characterLimit
|
|
7483
|
+
})
|
|
7484
|
+
);
|
|
7485
|
+
}
|
|
7486
|
+
if (enableSmilies) {
|
|
7487
|
+
extensions2.push(SmilieReplacer);
|
|
7488
|
+
}
|
|
7489
|
+
if (enableColorHighlighter) {
|
|
7490
|
+
extensions2.push(ColorHighlighter);
|
|
7491
|
+
}
|
|
7492
|
+
if (options.mention) {
|
|
7493
|
+
const mentionConfig = typeof options.mention === "object" ? options.mention : {};
|
|
5283
7494
|
extensions2.push(
|
|
5284
7495
|
Mention.configure({
|
|
5285
7496
|
HTMLAttributes: {
|
|
@@ -5325,7 +7536,8 @@ function createRichTextPreset(options = {}) {
|
|
|
5325
7536
|
}
|
|
5326
7537
|
if (options.trackChanges) {
|
|
5327
7538
|
extensions2.push(
|
|
5328
|
-
typeof options.trackChanges === "object" ? TrackChanges.configure(options.trackChanges) : TrackChanges
|
|
7539
|
+
typeof options.trackChanges === "object" ? TrackChanges.configure(options.trackChanges) : TrackChanges,
|
|
7540
|
+
DocumentCompare
|
|
5329
7541
|
);
|
|
5330
7542
|
}
|
|
5331
7543
|
if (options.fileImport) {
|
|
@@ -5364,9 +7576,152 @@ function createRichTextPreset(options = {}) {
|
|
|
5364
7576
|
if (options.crossReferences) {
|
|
5365
7577
|
extensions2.push(CrossReference.configure(optionObject(options.crossReferences)));
|
|
5366
7578
|
}
|
|
7579
|
+
if (options.findReplace) {
|
|
7580
|
+
extensions2.push(FindReplace.configure(optionObject(options.findReplace)));
|
|
7581
|
+
}
|
|
7582
|
+
if (options.paragraphStyles) {
|
|
7583
|
+
extensions2.push(ParagraphStyles.configure(optionObject(options.paragraphStyles)));
|
|
7584
|
+
}
|
|
5367
7585
|
return extensions2;
|
|
5368
7586
|
}
|
|
5369
7587
|
|
|
7588
|
+
// src/extensions/find-replace/commands.ts
|
|
7589
|
+
var OPTION_KEYS = [
|
|
7590
|
+
"matchCase",
|
|
7591
|
+
"wholeWord",
|
|
7592
|
+
"matchPrefix",
|
|
7593
|
+
"matchSuffix",
|
|
7594
|
+
"ignorePunctuation",
|
|
7595
|
+
"ignoreWhitespace",
|
|
7596
|
+
"wildcards",
|
|
7597
|
+
"regex",
|
|
7598
|
+
"literal"
|
|
7599
|
+
];
|
|
7600
|
+
var searchParams = (withReplacement) => ({
|
|
7601
|
+
type: "object",
|
|
7602
|
+
properties: {
|
|
7603
|
+
query: { type: "string", description: "Find what. Word ^ codes (^p, ^t, ^#) unless literal." },
|
|
7604
|
+
...withReplacement ? {
|
|
7605
|
+
replacement: {
|
|
7606
|
+
type: "string",
|
|
7607
|
+
description: "Replace with. ^& is the found text; with wildcards, \\1-\\9 are groups."
|
|
7608
|
+
}
|
|
7609
|
+
} : {},
|
|
7610
|
+
...Object.fromEntries(OPTION_KEYS.map((key) => [key, { type: "boolean" }]))
|
|
7611
|
+
},
|
|
7612
|
+
additionalProperties: false
|
|
7613
|
+
});
|
|
7614
|
+
function splitParams(params) {
|
|
7615
|
+
if (!params) return null;
|
|
7616
|
+
const options = {};
|
|
7617
|
+
for (const key of OPTION_KEYS) if (params[key] !== void 0) options[key] = params[key];
|
|
7618
|
+
return {
|
|
7619
|
+
...params.query !== void 0 ? { query: params.query } : {},
|
|
7620
|
+
...params.replacement !== void 0 ? { replacement: params.replacement } : {},
|
|
7621
|
+
options
|
|
7622
|
+
};
|
|
7623
|
+
}
|
|
7624
|
+
var usable = (editor) => !editor.isDestroyed && findReplacePluginKey.getState(editor.state) ? true : "Find and Replace is not available.";
|
|
7625
|
+
function createFindReplaceCommands(editor, options = {}) {
|
|
7626
|
+
const prefix = options.prefix ?? "findReplace.";
|
|
7627
|
+
const group = options.group ?? "Edit";
|
|
7628
|
+
const scope = options.scope ? { scope: options.scope } : {};
|
|
7629
|
+
const state = () => getFindReplaceState(editor);
|
|
7630
|
+
const hasMatches = () => {
|
|
7631
|
+
const ok = usable(editor);
|
|
7632
|
+
if (ok !== true) return ok;
|
|
7633
|
+
return state()?.matches.length ? true : "No matches.";
|
|
7634
|
+
};
|
|
7635
|
+
const apply = (params) => {
|
|
7636
|
+
const split = splitParams(params);
|
|
7637
|
+
if (split) editor.commands.setSearch(split);
|
|
7638
|
+
};
|
|
7639
|
+
const failIfInvalid = () => {
|
|
7640
|
+
const s = state();
|
|
7641
|
+
if (s?.error) throw new Error(s.error.message);
|
|
7642
|
+
};
|
|
7643
|
+
return [
|
|
7644
|
+
{
|
|
7645
|
+
id: `${prefix}open`,
|
|
7646
|
+
label: "Find",
|
|
7647
|
+
group,
|
|
7648
|
+
keywords: ["search", "find"],
|
|
7649
|
+
description: "Shows the find bar.",
|
|
7650
|
+
...options.shortcuts ? { shortcut: "Mod+F" } : {},
|
|
7651
|
+
...scope,
|
|
7652
|
+
enabled: () => usable(editor),
|
|
7653
|
+
run: () => editor.commands.openFindReplace({ mode: "find" })
|
|
7654
|
+
},
|
|
7655
|
+
{
|
|
7656
|
+
id: `${prefix}openReplace`,
|
|
7657
|
+
label: "Replace",
|
|
7658
|
+
group,
|
|
7659
|
+
keywords: ["search", "substitute"],
|
|
7660
|
+
description: "Shows the find bar with the replace field.",
|
|
7661
|
+
...options.shortcuts ? { shortcut: "Mod+H" } : {},
|
|
7662
|
+
...scope,
|
|
7663
|
+
enabled: () => usable(editor),
|
|
7664
|
+
run: () => editor.commands.openFindReplace({ mode: "replace" })
|
|
7665
|
+
},
|
|
7666
|
+
{
|
|
7667
|
+
id: `${prefix}find`,
|
|
7668
|
+
label: "Find text",
|
|
7669
|
+
group,
|
|
7670
|
+
description: "Searches the document and returns the number of matches.",
|
|
7671
|
+
params: searchParams(false),
|
|
7672
|
+
readOnly: true,
|
|
7673
|
+
...scope,
|
|
7674
|
+
enabled: () => usable(editor),
|
|
7675
|
+
run: (_ctx, params) => {
|
|
7676
|
+
apply(params);
|
|
7677
|
+
failIfInvalid();
|
|
7678
|
+
const s = state();
|
|
7679
|
+
return {
|
|
7680
|
+
count: s?.matches.length ?? 0,
|
|
7681
|
+
truncated: s?.truncated ?? false,
|
|
7682
|
+
matches: (s?.matches ?? []).slice(0, 200).map(({ from, to, text }) => ({ from, to, text }))
|
|
7683
|
+
};
|
|
7684
|
+
}
|
|
7685
|
+
},
|
|
7686
|
+
{
|
|
7687
|
+
id: `${prefix}next`,
|
|
7688
|
+
label: "Find next",
|
|
7689
|
+
group,
|
|
7690
|
+
...scope,
|
|
7691
|
+
enabled: hasMatches,
|
|
7692
|
+
run: () => editor.commands.findNext()
|
|
7693
|
+
},
|
|
7694
|
+
{
|
|
7695
|
+
id: `${prefix}previous`,
|
|
7696
|
+
label: "Find previous",
|
|
7697
|
+
group,
|
|
7698
|
+
...scope,
|
|
7699
|
+
enabled: hasMatches,
|
|
7700
|
+
run: () => editor.commands.findPrevious()
|
|
7701
|
+
},
|
|
7702
|
+
{
|
|
7703
|
+
id: `${prefix}replaceAll`,
|
|
7704
|
+
label: "Replace all",
|
|
7705
|
+
group,
|
|
7706
|
+
description: "Replaces every match (one undo step). Tracked when Track Changes is on.",
|
|
7707
|
+
params: searchParams(true),
|
|
7708
|
+
...scope,
|
|
7709
|
+
enabled: (_ctx, params) => params?.query !== void 0 ? usable(editor) : hasMatches(),
|
|
7710
|
+
run: (_ctx, params) => {
|
|
7711
|
+
apply(params);
|
|
7712
|
+
failIfInvalid();
|
|
7713
|
+
const count = state()?.matches.length ?? 0;
|
|
7714
|
+
if (count) editor.commands.replaceAll();
|
|
7715
|
+
return { replaced: count };
|
|
7716
|
+
}
|
|
7717
|
+
}
|
|
7718
|
+
];
|
|
7719
|
+
}
|
|
7720
|
+
function validateFindPattern(query, options = {}) {
|
|
7721
|
+
const result = compileFindPattern(query, options);
|
|
7722
|
+
return result.ok ? null : result.error;
|
|
7723
|
+
}
|
|
7724
|
+
|
|
5370
7725
|
export {
|
|
5371
7726
|
GradientText,
|
|
5372
7727
|
SlashCommands,
|
|
@@ -5379,6 +7734,8 @@ export {
|
|
|
5379
7734
|
ColorHighlighter,
|
|
5380
7735
|
FontSize,
|
|
5381
7736
|
Mention,
|
|
7737
|
+
getMentionNodes,
|
|
7738
|
+
setMentionRoleAt,
|
|
5382
7739
|
createMentionExtension,
|
|
5383
7740
|
Markdown2 as Markdown,
|
|
5384
7741
|
imagePastePluginKey,
|
|
@@ -5395,6 +7752,9 @@ export {
|
|
|
5395
7752
|
getCommentStore,
|
|
5396
7753
|
Comments,
|
|
5397
7754
|
getCommentAuthor,
|
|
7755
|
+
compareInEditor,
|
|
7756
|
+
applyDocumentComparison,
|
|
7757
|
+
DocumentCompare,
|
|
5398
7758
|
ImportError,
|
|
5399
7759
|
createImportContext,
|
|
5400
7760
|
ImportAdapterRegistry,
|
|
@@ -5467,6 +7827,42 @@ export {
|
|
|
5467
7827
|
CrossReference,
|
|
5468
7828
|
documentStructureCommandItems,
|
|
5469
7829
|
getDocumentStructureCommandItems,
|
|
5470
|
-
|
|
7830
|
+
FIND_PARAGRAPH_MARK,
|
|
7831
|
+
MAX_FIND_PATTERN_LENGTH,
|
|
7832
|
+
createSearchBudget,
|
|
7833
|
+
foldCodePoint,
|
|
7834
|
+
compileFindPattern,
|
|
7835
|
+
findAllMatches,
|
|
7836
|
+
parseReplacement,
|
|
7837
|
+
expandReplacement,
|
|
7838
|
+
searchDocument,
|
|
7839
|
+
applyReplacements,
|
|
7840
|
+
EMPTY_FIND_REPLACE_SNAPSHOT,
|
|
7841
|
+
FindReplaceStore,
|
|
7842
|
+
findReplacePluginKey,
|
|
7843
|
+
getFindReplaceState,
|
|
7844
|
+
getFindReplaceStore,
|
|
7845
|
+
FIND_OPTION_TOGGLES,
|
|
7846
|
+
formatFindStatus,
|
|
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,
|
|
7864
|
+
createRichTextPreset,
|
|
7865
|
+
createFindReplaceCommands,
|
|
7866
|
+
validateFindPattern
|
|
5471
7867
|
};
|
|
5472
|
-
//# sourceMappingURL=chunk-
|
|
7868
|
+
//# sourceMappingURL=chunk-IXHSYOVY.js.map
|