chaeditor 0.1.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/LICENSE.txt +21 -0
- package/README.ko.md +250 -0
- package/README.md +242 -0
- package/dist/core.cjs +1034 -0
- package/dist/core.d.mts +347 -0
- package/dist/core.d.ts +347 -0
- package/dist/core.mjs +988 -0
- package/dist/default-host.cjs +243 -0
- package/dist/default-host.d.mts +52 -0
- package/dist/default-host.d.ts +52 -0
- package/dist/default-host.mjs +239 -0
- package/dist/default-markdown-primitive-registry-B3PGEkqs.d.mts +12 -0
- package/dist/default-markdown-primitive-registry-CqzwhHj2.d.ts +12 -0
- package/dist/image-upload-kind-BJqItE_C.d.mts +18 -0
- package/dist/image-upload-kind-BJqItE_C.d.ts +18 -0
- package/dist/index.cjs +8736 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +8668 -0
- package/dist/markdown-editor-B1qvE40Z.d.mts +460 -0
- package/dist/markdown-editor-Ce6DpnQk.d.ts +460 -0
- package/dist/markdown-primitive-contract-BXsqbKwY.d.mts +124 -0
- package/dist/markdown-primitive-contract-BXsqbKwY.d.ts +124 -0
- package/dist/panda-primitives.cjs +1299 -0
- package/dist/panda-primitives.d.mts +21127 -0
- package/dist/panda-primitives.d.ts +21127 -0
- package/dist/panda-primitives.mjs +1285 -0
- package/dist/react.cjs +8558 -0
- package/dist/react.d.mts +35 -0
- package/dist/react.d.ts +35 -0
- package/dist/react.mjs +8531 -0
- package/dist/toolbar-preset-B9ttTEol.d.ts +236 -0
- package/dist/toolbar-preset-DIsQN390.d.mts +236 -0
- package/package.json +151 -0
- package/recipes/host-presets/README.md +16 -0
- package/recipes/host-presets/emotion-host-preset.tsx.template +109 -0
- package/recipes/host-presets/styled-components-host-preset.tsx.template +102 -0
- package/recipes/host-presets/tailwind-host-preset.tsx.template +116 -0
- package/recipes/host-presets/vanilla-extract-host-preset.tsx.template +116 -0
- package/styled-system/styles.css +3370 -0
package/dist/core.mjs
ADDED
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
// src/entities/editor-core/model/editor-attachment-policy.ts
|
|
2
|
+
var MB = 1024 * 1024;
|
|
3
|
+
var EDITOR_ATTACHMENT_MAX_FILE_SIZE = 50 * MB;
|
|
4
|
+
var EDITOR_ATTACHMENT_ALLOWED_EXTENSIONS = [
|
|
5
|
+
"csv",
|
|
6
|
+
"doc",
|
|
7
|
+
"docx",
|
|
8
|
+
"hwp",
|
|
9
|
+
"hwpx",
|
|
10
|
+
"md",
|
|
11
|
+
"pdf",
|
|
12
|
+
"ppt",
|
|
13
|
+
"pptx",
|
|
14
|
+
"txt",
|
|
15
|
+
"xls",
|
|
16
|
+
"xlsx",
|
|
17
|
+
"zip"
|
|
18
|
+
];
|
|
19
|
+
var EDITOR_ATTACHMENT_ALLOWED_MIME_TYPES = [
|
|
20
|
+
"application/msword",
|
|
21
|
+
"application/hwp+zip",
|
|
22
|
+
"application/pdf",
|
|
23
|
+
"application/vnd.hancom.hwpx",
|
|
24
|
+
"application/x-hwp+zip",
|
|
25
|
+
"application/vnd.ms-excel",
|
|
26
|
+
"application/vnd.ms-powerpoint",
|
|
27
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
28
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
29
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
30
|
+
"application/x-hwp",
|
|
31
|
+
"application/x-zip-compressed",
|
|
32
|
+
"application/zip",
|
|
33
|
+
"text/csv",
|
|
34
|
+
"text/markdown",
|
|
35
|
+
"text/plain"
|
|
36
|
+
];
|
|
37
|
+
var EDITOR_ATTACHMENT_FILE_INPUT_ACCEPT = EDITOR_ATTACHMENT_ALLOWED_EXTENSIONS.map(
|
|
38
|
+
(extension) => `.${extension}`
|
|
39
|
+
).join(",");
|
|
40
|
+
var isAllowedEditorAttachmentExtension = (fileName) => {
|
|
41
|
+
const baseName = fileName.trim().split("/").pop()?.split("\\").pop()?.trim() ?? "";
|
|
42
|
+
const lastDotIndex = baseName.lastIndexOf(".");
|
|
43
|
+
if (lastDotIndex <= 0 || lastDotIndex === baseName.length - 1) return false;
|
|
44
|
+
const extension = baseName.slice(lastDotIndex + 1).toLowerCase();
|
|
45
|
+
return EDITOR_ATTACHMENT_ALLOWED_EXTENSIONS.includes(
|
|
46
|
+
extension
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
var isAllowedEditorAttachmentFile = (file) => {
|
|
50
|
+
if (!file.name.trim() || file.size <= 0 || file.size > EDITOR_ATTACHMENT_MAX_FILE_SIZE) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (!isAllowedEditorAttachmentExtension(file.name)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (!file.type) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return EDITOR_ATTACHMENT_ALLOWED_MIME_TYPES.includes(
|
|
60
|
+
file.type
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/entities/editor-core/model/editor-video-policy.ts
|
|
65
|
+
var MB2 = 1024 * 1024;
|
|
66
|
+
var EDITOR_VIDEO_MAX_FILE_SIZE = 500 * MB2;
|
|
67
|
+
var EDITOR_VIDEO_ALLOWED_EXTENSIONS = ["m4v", "mov", "mp4", "webm"];
|
|
68
|
+
var EDITOR_VIDEO_ALLOWED_MIME_TYPES = [
|
|
69
|
+
"video/mp4",
|
|
70
|
+
"video/quicktime",
|
|
71
|
+
"video/webm",
|
|
72
|
+
"video/x-m4v",
|
|
73
|
+
"application/octet-stream"
|
|
74
|
+
];
|
|
75
|
+
var EDITOR_VIDEO_FILE_INPUT_ACCEPT = [
|
|
76
|
+
...EDITOR_VIDEO_ALLOWED_EXTENSIONS.map((extension) => `.${extension}`),
|
|
77
|
+
...EDITOR_VIDEO_ALLOWED_MIME_TYPES
|
|
78
|
+
].join(",");
|
|
79
|
+
var isAllowedEditorVideoExtension = (fileName) => {
|
|
80
|
+
const baseName = fileName.trim().split("/").pop()?.split("\\").pop()?.trim() ?? "";
|
|
81
|
+
const lastDotIndex = baseName.lastIndexOf(".");
|
|
82
|
+
if (lastDotIndex <= 0 || lastDotIndex === baseName.length - 1) return false;
|
|
83
|
+
const extension = baseName.slice(lastDotIndex + 1).toLowerCase();
|
|
84
|
+
return EDITOR_VIDEO_ALLOWED_EXTENSIONS.includes(
|
|
85
|
+
extension
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
var isAllowedEditorVideoFile = (file) => {
|
|
89
|
+
if (!file.name.trim() || file.size <= 0 || file.size > EDITOR_VIDEO_MAX_FILE_SIZE) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (!isAllowedEditorVideoExtension(file.name)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (!file.type) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return EDITOR_VIDEO_ALLOWED_MIME_TYPES.includes(
|
|
99
|
+
file.type
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/shared/lib/markdown/rich-markdown-inline.ts
|
|
104
|
+
var htmlLineBreakPattern = /<br\s*\/?>/gi;
|
|
105
|
+
var htmlHorizontalRulePattern = /<hr\s*\/?>/gi;
|
|
106
|
+
var markdownLineBreakPlaceholder = "__MD_LINE_BREAK__";
|
|
107
|
+
var markdownHorizontalRulePlaceholder = "__MD_HORIZONTAL_RULE__";
|
|
108
|
+
var inlineStyledSpanPattern = /<span style="([^"]+)">([\s\S]*?)<\/span>/g;
|
|
109
|
+
var inlineUnderlinePattern = /<u>([\s\S]*?)<\/u>/g;
|
|
110
|
+
var inlineSpoilerPattern = /\|\|([^|]+?)\|\|/g;
|
|
111
|
+
var inlineMathPattern = /<Math>([\s\S]*?)<\/Math>/g;
|
|
112
|
+
var fenceBoundaryPattern = /^\s*(`{3,}|~{3,})/;
|
|
113
|
+
var getFenceBoundary = (line, activeFence) => {
|
|
114
|
+
const match = line.match(fenceBoundaryPattern);
|
|
115
|
+
if (!match) return null;
|
|
116
|
+
const delimiter = match[1][0];
|
|
117
|
+
const size = match[1].length;
|
|
118
|
+
if (!activeFence) {
|
|
119
|
+
return {
|
|
120
|
+
delimiter,
|
|
121
|
+
size
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (activeFence.delimiter !== delimiter || size < activeFence.size) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
delimiter,
|
|
129
|
+
size
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
var transformMarkdownOutsideCode = (markdown, transform) => {
|
|
133
|
+
const lines = markdown.split("\n");
|
|
134
|
+
let activeFence = null;
|
|
135
|
+
return lines.map((line) => {
|
|
136
|
+
const fenceBoundary = getFenceBoundary(line, activeFence);
|
|
137
|
+
if (fenceBoundary) {
|
|
138
|
+
activeFence = activeFence ? null : fenceBoundary;
|
|
139
|
+
return line;
|
|
140
|
+
}
|
|
141
|
+
if (activeFence) {
|
|
142
|
+
return line;
|
|
143
|
+
}
|
|
144
|
+
let transformedLine = "";
|
|
145
|
+
for (let cursor = 0; cursor < line.length; ) {
|
|
146
|
+
if (line[cursor] !== "`") {
|
|
147
|
+
const nextBacktickIndex = line.indexOf("`", cursor);
|
|
148
|
+
const proseSegment = line.slice(
|
|
149
|
+
cursor,
|
|
150
|
+
nextBacktickIndex === -1 ? line.length : nextBacktickIndex
|
|
151
|
+
);
|
|
152
|
+
transformedLine += transform(proseSegment);
|
|
153
|
+
cursor = nextBacktickIndex === -1 ? line.length : nextBacktickIndex;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
let tickCount = 1;
|
|
157
|
+
while (line[cursor + tickCount] === "`") {
|
|
158
|
+
tickCount += 1;
|
|
159
|
+
}
|
|
160
|
+
const delimiter = "`".repeat(tickCount);
|
|
161
|
+
const codeStart = cursor;
|
|
162
|
+
const codeEnd = line.indexOf(delimiter, cursor + tickCount);
|
|
163
|
+
if (codeEnd === -1) {
|
|
164
|
+
transformedLine += transform(line.slice(cursor));
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
transformedLine += line.slice(codeStart, codeEnd + tickCount);
|
|
168
|
+
cursor = codeEnd + tickCount;
|
|
169
|
+
}
|
|
170
|
+
return transformedLine;
|
|
171
|
+
}).join("\n");
|
|
172
|
+
};
|
|
173
|
+
var escapeMarkdownLinkLabel = (value) => value.replace(/\\/g, "\\\\").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
|
|
174
|
+
var parseInlineStyle = (style) => {
|
|
175
|
+
const declarations = style.split(";").map((entry) => entry.trim()).filter(Boolean);
|
|
176
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
177
|
+
declarations.forEach((entry) => {
|
|
178
|
+
const [property, rawValue] = entry.split(":");
|
|
179
|
+
if (!property || !rawValue) return;
|
|
180
|
+
styleMap.set(property.trim().toLowerCase(), rawValue.trim());
|
|
181
|
+
});
|
|
182
|
+
const color = styleMap.get("color");
|
|
183
|
+
const background = styleMap.get("background-color");
|
|
184
|
+
const colorHex = color?.match(/^#[0-9A-Fa-f]{6}$/)?.[0];
|
|
185
|
+
const backgroundHex = background?.match(/^#[0-9A-Fa-f]{6}$/)?.[0];
|
|
186
|
+
return {
|
|
187
|
+
backgroundHex,
|
|
188
|
+
colorHex
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
var preprocessMarkdownInlineSyntax = (markdown) => transformMarkdownOutsideCode(
|
|
192
|
+
markdown,
|
|
193
|
+
(value) => value.replace(inlineStyledSpanPattern, (_, rawStyle, text) => {
|
|
194
|
+
const escapedText = escapeMarkdownLinkLabel(text.trim() || "Text");
|
|
195
|
+
const { backgroundHex, colorHex } = parseInlineStyle(rawStyle);
|
|
196
|
+
if (backgroundHex && colorHex) {
|
|
197
|
+
return `[${escapedText}](#md-style:color=${colorHex};background=${backgroundHex})`;
|
|
198
|
+
}
|
|
199
|
+
if (backgroundHex) {
|
|
200
|
+
return `[${escapedText}](#md-bg:${backgroundHex})`;
|
|
201
|
+
}
|
|
202
|
+
if (colorHex) {
|
|
203
|
+
return `[${escapedText}](#md-color:${colorHex})`;
|
|
204
|
+
}
|
|
205
|
+
return text;
|
|
206
|
+
}).replace(inlineUnderlinePattern, (_, text) => {
|
|
207
|
+
const escapedText = escapeMarkdownLinkLabel(text.trim() || "Text");
|
|
208
|
+
return `[${escapedText}](#md-underline:)`;
|
|
209
|
+
}).replace(inlineSpoilerPattern, (_, text) => {
|
|
210
|
+
const escapedText = escapeMarkdownLinkLabel(text.trim() || "Spoiler");
|
|
211
|
+
return `[${escapedText}](#md-spoiler:)`;
|
|
212
|
+
}).replace(inlineMathPattern, (_, formula) => {
|
|
213
|
+
const normalizedFormula = formula.trim();
|
|
214
|
+
const encodedFormula = encodeURIComponent(normalizedFormula);
|
|
215
|
+
const escapedLabel = escapeMarkdownLinkLabel(normalizedFormula || "math");
|
|
216
|
+
return `[${escapedLabel}](#md-math:${encodedFormula})`;
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
var normalizeMarkdownHtmlAliases = (markdown) => transformMarkdownOutsideCode(
|
|
220
|
+
markdown,
|
|
221
|
+
(value) => value.replace(htmlHorizontalRulePattern, markdownHorizontalRulePlaceholder).replace(htmlLineBreakPattern, markdownLineBreakPlaceholder)
|
|
222
|
+
).replace(new RegExp(markdownLineBreakPlaceholder, "g"), " \n").replace(
|
|
223
|
+
new RegExp(`(^|
|
|
224
|
+
)${markdownHorizontalRulePlaceholder}(?=
|
|
225
|
+
|$)`, "g"),
|
|
226
|
+
(_, prefix) => `${prefix}---`
|
|
227
|
+
).replace(new RegExp(markdownHorizontalRulePlaceholder, "g"), "\n---\n");
|
|
228
|
+
|
|
229
|
+
// src/shared/lib/url/normalize-http-url.ts
|
|
230
|
+
var normalizeHttpUrl = (rawUrl) => {
|
|
231
|
+
const trimmed = rawUrl?.trim();
|
|
232
|
+
if (!trimmed) return null;
|
|
233
|
+
try {
|
|
234
|
+
const parsed = new URL(trimmed);
|
|
235
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return parsed.toString();
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/entities/editor-core/model/markdown-link.ts
|
|
245
|
+
var createMarkdownLink = (label, url, title) => {
|
|
246
|
+
const normalizedUrl = normalizeHttpUrl(url);
|
|
247
|
+
const normalizedLabel = label.trim();
|
|
248
|
+
if (!normalizedUrl) {
|
|
249
|
+
return normalizedLabel ? label : url.trim();
|
|
250
|
+
}
|
|
251
|
+
const resolvedLabel = normalizedLabel ? label : normalizedUrl;
|
|
252
|
+
const serializedTitle = title ? ` "${title}"` : "";
|
|
253
|
+
return `[${resolvedLabel}](${normalizedUrl}${serializedTitle})`;
|
|
254
|
+
};
|
|
255
|
+
var createMarkdownLinkByMode = ({ label, mode, url }) => {
|
|
256
|
+
if (mode === "embed") {
|
|
257
|
+
const normalizedUrl = normalizeHttpUrl(url);
|
|
258
|
+
if (!normalizedUrl) {
|
|
259
|
+
return label.trim() || url.trim();
|
|
260
|
+
}
|
|
261
|
+
return `[embed](${normalizedUrl})`;
|
|
262
|
+
}
|
|
263
|
+
if (mode === "preview" || mode === "card") {
|
|
264
|
+
return createMarkdownLink(label, url, mode);
|
|
265
|
+
}
|
|
266
|
+
return createMarkdownLink(label, url);
|
|
267
|
+
};
|
|
268
|
+
var extractLabelAndUrl = (clipboardText) => {
|
|
269
|
+
const match = clipboardText.trim().match(/^(?<label>.+?)\s+(?<url>https?:\/\/\S+)$/su);
|
|
270
|
+
const label = match?.groups?.label?.trim() ?? "";
|
|
271
|
+
const url = normalizeHttpUrl(match?.groups?.url);
|
|
272
|
+
if (!label || !url) return null;
|
|
273
|
+
return { label, url };
|
|
274
|
+
};
|
|
275
|
+
var buildEditorLinkInsertion = ({
|
|
276
|
+
clipboardText,
|
|
277
|
+
selectedText
|
|
278
|
+
}) => {
|
|
279
|
+
const normalizedUrl = normalizeHttpUrl(clipboardText);
|
|
280
|
+
const hasSelectedText = selectedText.trim().length > 0;
|
|
281
|
+
if (normalizedUrl && hasSelectedText) {
|
|
282
|
+
return {
|
|
283
|
+
text: createMarkdownLink(selectedText, normalizedUrl),
|
|
284
|
+
type: "link"
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (normalizedUrl) {
|
|
288
|
+
return {
|
|
289
|
+
text: createMarkdownLink(normalizedUrl, normalizedUrl),
|
|
290
|
+
type: "link"
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const extracted = extractLabelAndUrl(clipboardText);
|
|
294
|
+
if (!extracted) return null;
|
|
295
|
+
return {
|
|
296
|
+
text: createMarkdownLink(extracted.label, extracted.url),
|
|
297
|
+
type: "link"
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/shared/lib/markdown/collect-markdown-images.ts
|
|
302
|
+
var markdownImagePattern = /!\[(?<alt>[^\]]*)\]\((?<src>[^)\s]+)(?:\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\)/g;
|
|
303
|
+
var galleryStartPattern = /^:::gallery\s*$/;
|
|
304
|
+
var galleryEndPattern = /^:::\s*$/;
|
|
305
|
+
var collectMarkdownImages = (markdown) => {
|
|
306
|
+
const items = [];
|
|
307
|
+
let imageIndex = 0;
|
|
308
|
+
let isInsideGallery = false;
|
|
309
|
+
for (const line of markdown.split("\n")) {
|
|
310
|
+
if (galleryStartPattern.test(line)) {
|
|
311
|
+
isInsideGallery = true;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (isInsideGallery && galleryEndPattern.test(line)) {
|
|
315
|
+
isInsideGallery = false;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (isInsideGallery) continue;
|
|
319
|
+
for (const matched of line.matchAll(markdownImagePattern)) {
|
|
320
|
+
const alt = matched.groups?.alt?.trim() ?? "";
|
|
321
|
+
const src = matched.groups?.src?.trim() ?? "";
|
|
322
|
+
if (!src) continue;
|
|
323
|
+
items.push({
|
|
324
|
+
alt,
|
|
325
|
+
src,
|
|
326
|
+
viewerId: `markdown-image-${imageIndex}`
|
|
327
|
+
});
|
|
328
|
+
imageIndex += 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return items;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/shared/lib/markdown/rich-markdown-segments.ts
|
|
335
|
+
var toggleStartPrefix = ":::toggle ";
|
|
336
|
+
var galleryStartPattern2 = /^:::gallery\s*$/;
|
|
337
|
+
var alignStartPattern = /^:::align (left|center|right)\s*$/;
|
|
338
|
+
var legacyYoutubePattern = /^<YouTube id="([^"]+)" \/>$/;
|
|
339
|
+
var videoPattern = /^<Video provider="([^"]+)"(?: id="([^"]+)")?(?: src="([^"]+)")? \/>$/;
|
|
340
|
+
var attachmentPattern = /^<Attachment href="([^"]+)" name="([^"]+)"(?: size="(\d+)")?(?: type="([^"]+)")? \/>$/;
|
|
341
|
+
var mathPattern = /^<Math(?: block="(true)")?>([\s\S]+?)<\/Math>$/;
|
|
342
|
+
var subtextPrefix = "-# ";
|
|
343
|
+
var fenceBoundaryPattern2 = /^\s*(`{3,}|~{3,})/;
|
|
344
|
+
var decodeHtmlAttributeEntities = (value) => value.replaceAll(""", '"').replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
345
|
+
var getFenceBoundary2 = (line, activeFence) => {
|
|
346
|
+
const match = line.match(fenceBoundaryPattern2);
|
|
347
|
+
if (!match) return null;
|
|
348
|
+
const delimiter = match[1][0];
|
|
349
|
+
const size = match[1].length;
|
|
350
|
+
if (!activeFence) {
|
|
351
|
+
return {
|
|
352
|
+
delimiter,
|
|
353
|
+
size
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (activeFence.delimiter !== delimiter || size < activeFence.size) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
delimiter,
|
|
361
|
+
size
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
var parseToggleTitle = (rawTitle) => {
|
|
365
|
+
const trimmedTitle = rawTitle.trim();
|
|
366
|
+
const headingMatch = trimmedTitle.match(/^(#{1,4})(?:\s+(.*))?$/);
|
|
367
|
+
const fallbackTitle = "Untitled toggle";
|
|
368
|
+
if (!trimmedTitle) {
|
|
369
|
+
return {
|
|
370
|
+
headingLevel: null,
|
|
371
|
+
title: fallbackTitle
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (!headingMatch) {
|
|
375
|
+
return {
|
|
376
|
+
headingLevel: null,
|
|
377
|
+
title: trimmedTitle || fallbackTitle
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
headingLevel: headingMatch[1].length,
|
|
382
|
+
title: headingMatch[2]?.trim() || fallbackTitle
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
var consumeCustomBlockBody = (lines, startIndex) => {
|
|
386
|
+
const bodyLines = [];
|
|
387
|
+
let activeFence = null;
|
|
388
|
+
let cursor = startIndex;
|
|
389
|
+
while (cursor < lines.length) {
|
|
390
|
+
const line = lines[cursor];
|
|
391
|
+
const fenceBoundary = getFenceBoundary2(line, activeFence);
|
|
392
|
+
if (fenceBoundary) {
|
|
393
|
+
bodyLines.push(line);
|
|
394
|
+
activeFence = activeFence ? null : fenceBoundary;
|
|
395
|
+
cursor += 1;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (!activeFence && line === ":::") {
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
bodyLines.push(line);
|
|
402
|
+
cursor += 1;
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
bodyLines,
|
|
406
|
+
cursor
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
var parseRichMarkdownSegments = (markdown) => {
|
|
410
|
+
const lines = markdown.split("\n");
|
|
411
|
+
const segments = [];
|
|
412
|
+
const currentMarkdownLines = [];
|
|
413
|
+
let activeFence = null;
|
|
414
|
+
const flushMarkdown = () => {
|
|
415
|
+
if (currentMarkdownLines.length === 0) return;
|
|
416
|
+
segments.push({
|
|
417
|
+
markdown: currentMarkdownLines.join("\n"),
|
|
418
|
+
type: "markdown"
|
|
419
|
+
});
|
|
420
|
+
currentMarkdownLines.length = 0;
|
|
421
|
+
};
|
|
422
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
423
|
+
const line = lines[index];
|
|
424
|
+
const fenceBoundary = getFenceBoundary2(line, activeFence);
|
|
425
|
+
if (fenceBoundary) {
|
|
426
|
+
currentMarkdownLines.push(line);
|
|
427
|
+
activeFence = activeFence ? null : fenceBoundary;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (activeFence) {
|
|
431
|
+
currentMarkdownLines.push(line);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (line.startsWith(toggleStartPrefix)) {
|
|
435
|
+
flushMarkdown();
|
|
436
|
+
const { bodyLines, cursor } = consumeCustomBlockBody(lines, index + 1);
|
|
437
|
+
const { headingLevel, title } = parseToggleTitle(line.slice(toggleStartPrefix.length));
|
|
438
|
+
segments.push({
|
|
439
|
+
content: bodyLines.join("\n"),
|
|
440
|
+
headingLevel,
|
|
441
|
+
title,
|
|
442
|
+
type: "toggle"
|
|
443
|
+
});
|
|
444
|
+
index = cursor;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const alignMatch = line.match(alignStartPattern);
|
|
448
|
+
if (alignMatch) {
|
|
449
|
+
flushMarkdown();
|
|
450
|
+
const { bodyLines, cursor } = consumeCustomBlockBody(lines, index + 1);
|
|
451
|
+
segments.push({
|
|
452
|
+
align: alignMatch[1],
|
|
453
|
+
content: bodyLines.join("\n"),
|
|
454
|
+
type: "align"
|
|
455
|
+
});
|
|
456
|
+
index = cursor;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (galleryStartPattern2.test(line)) {
|
|
460
|
+
flushMarkdown();
|
|
461
|
+
const { bodyLines, cursor } = consumeCustomBlockBody(lines, index + 1);
|
|
462
|
+
segments.push({
|
|
463
|
+
items: collectMarkdownImages(bodyLines.join("\n")),
|
|
464
|
+
type: "gallery"
|
|
465
|
+
});
|
|
466
|
+
index = cursor;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const videoMatch = line.match(videoPattern);
|
|
470
|
+
if (videoMatch && (videoMatch[1] === "youtube" || videoMatch[1] === "upload")) {
|
|
471
|
+
flushMarkdown();
|
|
472
|
+
segments.push({
|
|
473
|
+
provider: videoMatch[1],
|
|
474
|
+
src: videoMatch[3] ? decodeHtmlAttributeEntities(videoMatch[3]) : void 0,
|
|
475
|
+
type: "video",
|
|
476
|
+
videoId: videoMatch[2] ? decodeHtmlAttributeEntities(videoMatch[2]) : void 0
|
|
477
|
+
});
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const legacyYoutubeMatch = line.match(legacyYoutubePattern);
|
|
481
|
+
if (legacyYoutubeMatch) {
|
|
482
|
+
flushMarkdown();
|
|
483
|
+
segments.push({
|
|
484
|
+
provider: "youtube",
|
|
485
|
+
type: "video",
|
|
486
|
+
videoId: decodeHtmlAttributeEntities(legacyYoutubeMatch[1])
|
|
487
|
+
});
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const attachmentMatch = line.match(attachmentPattern);
|
|
491
|
+
if (attachmentMatch) {
|
|
492
|
+
flushMarkdown();
|
|
493
|
+
segments.push({
|
|
494
|
+
contentType: attachmentMatch[4] ? decodeHtmlAttributeEntities(attachmentMatch[4]) : void 0,
|
|
495
|
+
fileName: decodeHtmlAttributeEntities(attachmentMatch[2]),
|
|
496
|
+
fileSize: attachmentMatch[3] ? Number(attachmentMatch[3]) : void 0,
|
|
497
|
+
href: decodeHtmlAttributeEntities(attachmentMatch[1]),
|
|
498
|
+
type: "attachment"
|
|
499
|
+
});
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const mathMatch = line.match(mathPattern);
|
|
503
|
+
if (mathMatch) {
|
|
504
|
+
flushMarkdown();
|
|
505
|
+
segments.push({
|
|
506
|
+
formula: mathMatch[2],
|
|
507
|
+
isBlock: mathMatch[1] === "true",
|
|
508
|
+
type: "math"
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (line.startsWith(subtextPrefix)) {
|
|
513
|
+
flushMarkdown();
|
|
514
|
+
const subtextLines = [line.slice(subtextPrefix.length)];
|
|
515
|
+
let cursor = index + 1;
|
|
516
|
+
while (cursor < lines.length && lines[cursor].startsWith(subtextPrefix)) {
|
|
517
|
+
subtextLines.push(lines[cursor].slice(subtextPrefix.length));
|
|
518
|
+
cursor += 1;
|
|
519
|
+
}
|
|
520
|
+
segments.push({
|
|
521
|
+
content: subtextLines.join("\n"),
|
|
522
|
+
type: "subtext"
|
|
523
|
+
});
|
|
524
|
+
index = cursor - 1;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
currentMarkdownLines.push(line);
|
|
528
|
+
}
|
|
529
|
+
flushMarkdown();
|
|
530
|
+
return segments;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/entities/editor-core/model/video-embed.ts
|
|
534
|
+
var escapeJsxAttribute = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
535
|
+
var getFirstPathSegment = (pathname) => pathname.split("/").find((segment) => segment.length > 0) ?? null;
|
|
536
|
+
var extractVideoEmbedReference = (value) => {
|
|
537
|
+
const trimmedValue = value.trim();
|
|
538
|
+
if (!trimmedValue) return null;
|
|
539
|
+
try {
|
|
540
|
+
const url = new URL(trimmedValue);
|
|
541
|
+
const isYoutubeDomain = url.hostname === "youtube.com" || url.hostname.endsWith(".youtube.com");
|
|
542
|
+
if (url.hostname === "youtu.be") {
|
|
543
|
+
const videoId = getFirstPathSegment(url.pathname);
|
|
544
|
+
return videoId ? {
|
|
545
|
+
provider: "youtube",
|
|
546
|
+
videoId
|
|
547
|
+
} : null;
|
|
548
|
+
}
|
|
549
|
+
if (isYoutubeDomain) {
|
|
550
|
+
if (url.pathname === "/watch") {
|
|
551
|
+
const videoId = url.searchParams.get("v");
|
|
552
|
+
return videoId ? {
|
|
553
|
+
provider: "youtube",
|
|
554
|
+
videoId
|
|
555
|
+
} : null;
|
|
556
|
+
}
|
|
557
|
+
const [, firstSegment, secondSegment] = url.pathname.split("/");
|
|
558
|
+
if (firstSegment === "shorts" && secondSegment) {
|
|
559
|
+
return {
|
|
560
|
+
provider: "youtube",
|
|
561
|
+
videoId: secondSegment
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
if (firstSegment === "embed" && secondSegment) {
|
|
565
|
+
return {
|
|
566
|
+
provider: "youtube",
|
|
567
|
+
videoId: secondSegment
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} catch {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
return null;
|
|
575
|
+
};
|
|
576
|
+
var extractYoutubeId = (value) => {
|
|
577
|
+
const reference = extractVideoEmbedReference(value);
|
|
578
|
+
return reference?.provider === "youtube" ? reference.videoId : null;
|
|
579
|
+
};
|
|
580
|
+
var createVideoEmbedMarkdown = (reference) => {
|
|
581
|
+
if (reference.provider === "upload") {
|
|
582
|
+
return `<Video provider="upload" src="${escapeJsxAttribute(reference.src)}" />`;
|
|
583
|
+
}
|
|
584
|
+
return `<Video provider="${reference.provider}" id="${escapeJsxAttribute(reference.videoId)}" />`;
|
|
585
|
+
};
|
|
586
|
+
var createYoutubeEmbedMarkdown = (videoId) => createVideoEmbedMarkdown({
|
|
587
|
+
provider: "youtube",
|
|
588
|
+
videoId
|
|
589
|
+
});
|
|
590
|
+
var createUploadedVideoEmbedMarkdown = (src) => createVideoEmbedMarkdown({
|
|
591
|
+
provider: "upload",
|
|
592
|
+
src
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// src/features/edit-markdown/toolbar/contracts/markdown-toolbar-templates.ts
|
|
596
|
+
var escapeMarkdownAltText = (value) => value.replaceAll("]", "\\]");
|
|
597
|
+
var escapeMarkdownLinkDestination = (value) => value.replaceAll("\\", "\\\\").replaceAll("(", "\\(").replaceAll(")", "\\)").replaceAll("<", "%3C").replaceAll(">", "%3E");
|
|
598
|
+
var normalizeMathFormula = (value) => value.trim().replaceAll(/\s*\n+\s*/g, " ");
|
|
599
|
+
var escapeJsxAttribute2 = (value) => value.replaceAll("&", "&").replaceAll('"', """);
|
|
600
|
+
var createImageEmbedMarkdown = (altText, url) => `})`;
|
|
601
|
+
var createImageEmbedMarkdownGroup = (items) => items.map((item) => createImageEmbedMarkdown(item.altText, item.url)).join("\n\n");
|
|
602
|
+
var createImageGalleryMarkdown = (items) => [
|
|
603
|
+
":::gallery",
|
|
604
|
+
...items.map((item) => createImageEmbedMarkdown(item.altText, item.url)),
|
|
605
|
+
":::"
|
|
606
|
+
].join("\n");
|
|
607
|
+
var createAttachmentEmbedMarkdown = ({
|
|
608
|
+
contentType,
|
|
609
|
+
fileName,
|
|
610
|
+
fileSize,
|
|
611
|
+
url
|
|
612
|
+
}) => `<Attachment href="${escapeJsxAttribute2(url)}" name="${escapeJsxAttribute2(fileName)}" size="${String(fileSize)}" type="${escapeJsxAttribute2(contentType)}" />`;
|
|
613
|
+
var createMathEmbedMarkdown = ({
|
|
614
|
+
formula,
|
|
615
|
+
isBlock
|
|
616
|
+
}) => {
|
|
617
|
+
const normalizedFormula = normalizeMathFormula(formula);
|
|
618
|
+
if (isBlock) {
|
|
619
|
+
return `
|
|
620
|
+
<Math block="true">${normalizedFormula}</Math>
|
|
621
|
+
`;
|
|
622
|
+
}
|
|
623
|
+
return `<Math>${normalizedFormula}</Math>`;
|
|
624
|
+
};
|
|
625
|
+
var createAlignBlockMarkdown = (align) => {
|
|
626
|
+
const before = `:::align ${align}
|
|
627
|
+
`;
|
|
628
|
+
return {
|
|
629
|
+
cursorOffset: before.length,
|
|
630
|
+
text: `${before}Text
|
|
631
|
+
:::`
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
var createToggleBlockMarkdown = (level, selectedText) => {
|
|
635
|
+
const headingPrefix = "#".repeat(level);
|
|
636
|
+
if (!selectedText) {
|
|
637
|
+
const prefix = `:::toggle ${headingPrefix} `;
|
|
638
|
+
return {
|
|
639
|
+
cursorOffset: prefix.length,
|
|
640
|
+
text: `${prefix}
|
|
641
|
+
:::`
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
cursorOffset: `:::toggle ${headingPrefix} `.length + selectedText.length,
|
|
646
|
+
text: `:::toggle ${headingPrefix} ${selectedText}
|
|
647
|
+
Content
|
|
648
|
+
:::`
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
// src/entities/editor-core/model/selection-utils.ts
|
|
653
|
+
var pendingSelectionStartKey = "selectionUtilsPendingStart";
|
|
654
|
+
var pendingSelectionEndKey = "selectionUtilsPendingEnd";
|
|
655
|
+
var setPendingSelection = (textarea, start, end) => {
|
|
656
|
+
textarea.dataset[pendingSelectionStartKey] = String(start);
|
|
657
|
+
textarea.dataset[pendingSelectionEndKey] = String(end);
|
|
658
|
+
};
|
|
659
|
+
var getSelectedLineRange = (textarea) => {
|
|
660
|
+
const { selectionEnd, selectionStart, value } = textarea;
|
|
661
|
+
const lineStart = selectionStart === 0 ? 0 : value.lastIndexOf("\n", selectionStart - 1) + 1;
|
|
662
|
+
const nextLineBreakIndex = value.indexOf("\n", selectionEnd);
|
|
663
|
+
const lineEnd = nextLineBreakIndex === -1 ? value.length : nextLineBreakIndex;
|
|
664
|
+
return {
|
|
665
|
+
lineEnd,
|
|
666
|
+
lineStart,
|
|
667
|
+
text: value.slice(lineStart, lineEnd)
|
|
668
|
+
};
|
|
669
|
+
};
|
|
670
|
+
var headingPrefixPattern = /^(#{1,6})\s+/;
|
|
671
|
+
var unorderedListPattern = /^(\s*)([-*])\s+(.*)$/;
|
|
672
|
+
var unorderedListMarkerOnlyPattern = /^(\s*)([-*])\s*$/;
|
|
673
|
+
var orderedListPattern = /^(\s*)(\d+)\.\s+(.*)$/;
|
|
674
|
+
var orderedListMarkerOnlyPattern = /^(\s*)(\d+)\.\s*$/;
|
|
675
|
+
var listItemPattern = /^(\s*)((?:[-*])|(?:\d+\.))\s+/;
|
|
676
|
+
var replaceSelectedLineRange = (textarea, nextBlock, lineStart, lineEnd) => {
|
|
677
|
+
const nextValue = [
|
|
678
|
+
textarea.value.slice(0, lineStart),
|
|
679
|
+
nextBlock,
|
|
680
|
+
textarea.value.slice(lineEnd)
|
|
681
|
+
].join("");
|
|
682
|
+
setPendingSelection(textarea, lineStart, lineStart + nextBlock.length);
|
|
683
|
+
return nextValue;
|
|
684
|
+
};
|
|
685
|
+
var wrapSelection = (textarea, before, after, placeholder = "Text") => {
|
|
686
|
+
const { selectionEnd, selectionStart, value } = textarea;
|
|
687
|
+
const selectedText = value.slice(selectionStart, selectionEnd);
|
|
688
|
+
const nextSelectionText = selectedText || placeholder;
|
|
689
|
+
const nextValue = [
|
|
690
|
+
value.slice(0, selectionStart),
|
|
691
|
+
before,
|
|
692
|
+
nextSelectionText,
|
|
693
|
+
after,
|
|
694
|
+
value.slice(selectionEnd)
|
|
695
|
+
].join("");
|
|
696
|
+
const nextStart = selectionStart + before.length;
|
|
697
|
+
const nextEnd = nextStart + nextSelectionText.length;
|
|
698
|
+
setPendingSelection(textarea, nextStart, nextEnd);
|
|
699
|
+
return nextValue;
|
|
700
|
+
};
|
|
701
|
+
var prefixLine = (textarea, prefix) => {
|
|
702
|
+
const { lineEnd, lineStart, text } = getSelectedLineRange(textarea);
|
|
703
|
+
const lines = text.split("\n");
|
|
704
|
+
const nonEmptyLines = lines.filter((line) => line.length > 0);
|
|
705
|
+
const shouldRemovePrefix = nonEmptyLines.length > 0 && nonEmptyLines.every((line) => line.startsWith(prefix));
|
|
706
|
+
const nextLines = lines.map((line) => {
|
|
707
|
+
if (line.length === 0) return line;
|
|
708
|
+
if (shouldRemovePrefix) {
|
|
709
|
+
return line.startsWith(prefix) ? line.slice(prefix.length) : line;
|
|
710
|
+
}
|
|
711
|
+
return line.startsWith(prefix) ? line : `${prefix}${line}`;
|
|
712
|
+
});
|
|
713
|
+
const nextBlock = nextLines.join("\n");
|
|
714
|
+
const nextValue = [
|
|
715
|
+
textarea.value.slice(0, lineStart),
|
|
716
|
+
nextBlock,
|
|
717
|
+
textarea.value.slice(lineEnd)
|
|
718
|
+
].join("");
|
|
719
|
+
setPendingSelection(textarea, lineStart, lineStart + nextBlock.length);
|
|
720
|
+
return nextValue;
|
|
721
|
+
};
|
|
722
|
+
var insertTemplate = (textarea, template, cursorOffset = template.length) => {
|
|
723
|
+
const { selectionEnd, selectionStart, value } = textarea;
|
|
724
|
+
const nextValue = [value.slice(0, selectionStart), template, value.slice(selectionEnd)].join("");
|
|
725
|
+
const nextCursor = selectionStart + cursorOffset;
|
|
726
|
+
setPendingSelection(textarea, nextCursor, nextCursor);
|
|
727
|
+
return nextValue;
|
|
728
|
+
};
|
|
729
|
+
var restoreCursor = (textarea, start, end) => {
|
|
730
|
+
const maxIndex = textarea.value.length;
|
|
731
|
+
const nextStart = Math.max(0, Math.min(start, maxIndex));
|
|
732
|
+
const nextEnd = Math.max(nextStart, Math.min(end, maxIndex));
|
|
733
|
+
textarea.setSelectionRange(nextStart, nextEnd);
|
|
734
|
+
delete textarea.dataset[pendingSelectionStartKey];
|
|
735
|
+
delete textarea.dataset[pendingSelectionEndKey];
|
|
736
|
+
};
|
|
737
|
+
var focusTextarea = (textarea) => {
|
|
738
|
+
textarea.focus({ preventScroll: true });
|
|
739
|
+
};
|
|
740
|
+
var getPendingSelection = (textarea) => {
|
|
741
|
+
const start = Number(textarea.dataset[pendingSelectionStartKey]);
|
|
742
|
+
const end = Number(textarea.dataset[pendingSelectionEndKey]);
|
|
743
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
744
|
+
return {
|
|
745
|
+
end: textarea.selectionEnd,
|
|
746
|
+
start: textarea.selectionStart
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
return { end, start };
|
|
750
|
+
};
|
|
751
|
+
var applyTextareaTransform = (textarea, onChange, transform) => {
|
|
752
|
+
const nextValue = transform(textarea);
|
|
753
|
+
onChange(nextValue);
|
|
754
|
+
queueMicrotask(() => {
|
|
755
|
+
const pendingSelection = getPendingSelection(textarea);
|
|
756
|
+
focusTextarea(textarea);
|
|
757
|
+
restoreCursor(textarea, pendingSelection.start, pendingSelection.end);
|
|
758
|
+
});
|
|
759
|
+
return nextValue;
|
|
760
|
+
};
|
|
761
|
+
var toggleHeadingLine = (textarea, level) => {
|
|
762
|
+
const { lineEnd, lineStart, text } = getSelectedLineRange(textarea);
|
|
763
|
+
const lines = text.split("\n");
|
|
764
|
+
const targetPrefix = `${"#".repeat(level)} `;
|
|
765
|
+
if (lines.length === 1 && lines[0].trim().length === 0) {
|
|
766
|
+
const nextValue = [
|
|
767
|
+
textarea.value.slice(0, lineStart),
|
|
768
|
+
targetPrefix,
|
|
769
|
+
textarea.value.slice(lineEnd)
|
|
770
|
+
].join("");
|
|
771
|
+
setPendingSelection(textarea, lineStart + targetPrefix.length, lineStart + targetPrefix.length);
|
|
772
|
+
return nextValue;
|
|
773
|
+
}
|
|
774
|
+
const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
|
|
775
|
+
const shouldRemoveTargetPrefix = nonEmptyLines.length > 0 && nonEmptyLines.every((line) => line.startsWith(targetPrefix));
|
|
776
|
+
const nextLines = lines.map((line) => {
|
|
777
|
+
if (line.trim().length === 0) return line;
|
|
778
|
+
if (shouldRemoveTargetPrefix) {
|
|
779
|
+
return line.startsWith(targetPrefix) ? line.slice(targetPrefix.length) : line;
|
|
780
|
+
}
|
|
781
|
+
return line.replace(headingPrefixPattern, "") === line ? `${targetPrefix}${line}` : line.replace(headingPrefixPattern, targetPrefix);
|
|
782
|
+
});
|
|
783
|
+
return replaceSelectedLineRange(textarea, nextLines.join("\n"), lineStart, lineEnd);
|
|
784
|
+
};
|
|
785
|
+
var continueMarkdownList = (textarea) => {
|
|
786
|
+
const { selectionEnd, selectionStart, value } = textarea;
|
|
787
|
+
if (selectionStart !== selectionEnd) return null;
|
|
788
|
+
const lineStart = value.lastIndexOf("\n", selectionStart - 1) + 1;
|
|
789
|
+
const lineEnd = value.indexOf("\n", selectionStart);
|
|
790
|
+
const resolvedLineEnd = lineEnd === -1 ? value.length : lineEnd;
|
|
791
|
+
const currentLine = value.slice(lineStart, resolvedLineEnd);
|
|
792
|
+
const unorderedMatch = currentLine.match(unorderedListPattern);
|
|
793
|
+
if (unorderedMatch) {
|
|
794
|
+
const [, indent, marker, content] = unorderedMatch;
|
|
795
|
+
const nextLine = content.trim().length === 0 ? "" : `
|
|
796
|
+
${indent}${marker} `;
|
|
797
|
+
const nextValue = `${value.slice(0, selectionStart)}${nextLine}${value.slice(selectionEnd)}`;
|
|
798
|
+
const nextCursor = selectionStart + nextLine.length;
|
|
799
|
+
setPendingSelection(textarea, nextCursor, nextCursor);
|
|
800
|
+
return nextValue;
|
|
801
|
+
}
|
|
802
|
+
const unorderedMarkerOnlyMatch = currentLine.match(unorderedListMarkerOnlyPattern);
|
|
803
|
+
if (unorderedMarkerOnlyMatch) {
|
|
804
|
+
const [, indent] = unorderedMarkerOnlyMatch;
|
|
805
|
+
const replacement = indent;
|
|
806
|
+
const nextValue = [value.slice(0, lineStart), replacement, value.slice(resolvedLineEnd)].join(
|
|
807
|
+
""
|
|
808
|
+
);
|
|
809
|
+
const nextCursor = lineStart + replacement.length;
|
|
810
|
+
setPendingSelection(textarea, nextCursor, nextCursor);
|
|
811
|
+
return nextValue;
|
|
812
|
+
}
|
|
813
|
+
const orderedMatch = currentLine.match(orderedListPattern);
|
|
814
|
+
if (orderedMatch) {
|
|
815
|
+
const [, indent, numberText, content] = orderedMatch;
|
|
816
|
+
const nextLine = content.trim().length === 0 ? "" : `
|
|
817
|
+
${indent}${Number(numberText) + 1}. `;
|
|
818
|
+
const nextValue = `${value.slice(0, selectionStart)}${nextLine}${value.slice(selectionEnd)}`;
|
|
819
|
+
const nextCursor = selectionStart + nextLine.length;
|
|
820
|
+
setPendingSelection(textarea, nextCursor, nextCursor);
|
|
821
|
+
return nextValue;
|
|
822
|
+
}
|
|
823
|
+
const orderedMarkerOnlyMatch = currentLine.match(orderedListMarkerOnlyPattern);
|
|
824
|
+
if (orderedMarkerOnlyMatch) {
|
|
825
|
+
const [, indent] = orderedMarkerOnlyMatch;
|
|
826
|
+
const replacement = indent;
|
|
827
|
+
const nextValue = [value.slice(0, lineStart), replacement, value.slice(resolvedLineEnd)].join(
|
|
828
|
+
""
|
|
829
|
+
);
|
|
830
|
+
const nextCursor = lineStart + replacement.length;
|
|
831
|
+
setPendingSelection(textarea, nextCursor, nextCursor);
|
|
832
|
+
return nextValue;
|
|
833
|
+
}
|
|
834
|
+
return null;
|
|
835
|
+
};
|
|
836
|
+
var indentMarkdownList = (textarea) => {
|
|
837
|
+
const { lineEnd, lineStart, text } = getSelectedLineRange(textarea);
|
|
838
|
+
const lines = text.split("\n");
|
|
839
|
+
if (!lines.some((line) => listItemPattern.test(line))) {
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
const nextLines = lines.map((line) => listItemPattern.test(line) ? ` ${line}` : line);
|
|
843
|
+
return replaceSelectedLineRange(textarea, nextLines.join("\n"), lineStart, lineEnd);
|
|
844
|
+
};
|
|
845
|
+
var outdentMarkdownList = (textarea) => {
|
|
846
|
+
const { lineEnd, lineStart, text } = getSelectedLineRange(textarea);
|
|
847
|
+
const lines = text.split("\n");
|
|
848
|
+
if (!lines.some((line) => listItemPattern.test(line))) {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
const nextLines = lines.map((line) => {
|
|
852
|
+
if (!listItemPattern.test(line)) return line;
|
|
853
|
+
if (line.startsWith(" ")) return line.slice(2);
|
|
854
|
+
if (line.startsWith(" ")) return line.slice(1);
|
|
855
|
+
return line;
|
|
856
|
+
});
|
|
857
|
+
return replaceSelectedLineRange(textarea, nextLines.join("\n"), lineStart, lineEnd);
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
// src/entities/editor-core/model/theme-contract.ts
|
|
861
|
+
var CHAEDITOR_THEME_VARIABLES = {
|
|
862
|
+
border: "--chaeditor-color-border",
|
|
863
|
+
borderStrong: "--chaeditor-color-border-strong",
|
|
864
|
+
focusRing: "--chaeditor-color-focus-ring",
|
|
865
|
+
monoFont: "--chaeditor-font-mono",
|
|
866
|
+
muted: "--chaeditor-color-muted",
|
|
867
|
+
overlayBackdrop: "--chaeditor-color-overlay-backdrop",
|
|
868
|
+
primary: "--chaeditor-color-primary",
|
|
869
|
+
primaryContrast: "--chaeditor-color-primary-contrast",
|
|
870
|
+
primaryHover: "--chaeditor-color-primary-hover",
|
|
871
|
+
primaryMuted: "--chaeditor-color-primary-muted",
|
|
872
|
+
primarySubtle: "--chaeditor-color-primary-subtle",
|
|
873
|
+
sansFont: "--chaeditor-font-sans",
|
|
874
|
+
sansJaFont: "--chaeditor-font-sans-ja",
|
|
875
|
+
success: "--chaeditor-color-success",
|
|
876
|
+
surface: "--chaeditor-color-surface",
|
|
877
|
+
surfaceMuted: "--chaeditor-color-surface-muted",
|
|
878
|
+
surfaceStrong: "--chaeditor-color-surface-strong",
|
|
879
|
+
text: "--chaeditor-color-text",
|
|
880
|
+
textSubtle: "--chaeditor-color-text-subtle",
|
|
881
|
+
error: "--chaeditor-color-error"
|
|
882
|
+
};
|
|
883
|
+
var CHAEDITOR_THEME_DEFAULTS = {
|
|
884
|
+
dark: {
|
|
885
|
+
border: "#52525b",
|
|
886
|
+
borderStrong: "#71717a",
|
|
887
|
+
error: "#f87171",
|
|
888
|
+
focusRing: "#1e3a8a",
|
|
889
|
+
muted: "#a1a1aa",
|
|
890
|
+
overlayBackdrop: "rgb(9 9 11 / 0.82)",
|
|
891
|
+
primary: "#93c5fd",
|
|
892
|
+
primaryContrast: "#18181b",
|
|
893
|
+
primaryHover: "#bfdbfe",
|
|
894
|
+
primaryMuted: "#1e40af",
|
|
895
|
+
primarySubtle: "#1e3a8a",
|
|
896
|
+
success: "#4ade80",
|
|
897
|
+
surface: "#18181b",
|
|
898
|
+
surfaceMuted: "#27272a",
|
|
899
|
+
surfaceStrong: "#3f3f46",
|
|
900
|
+
text: "#fafafa",
|
|
901
|
+
textSubtle: "#d4d4d8"
|
|
902
|
+
},
|
|
903
|
+
fonts: {
|
|
904
|
+
mono: "var(--font-d2coding), 'D2Coding', 'SFMono-Regular', 'JetBrains Mono', Consolas, 'Liberation Mono', monospace",
|
|
905
|
+
sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
906
|
+
sansJa: "system-ui, -apple-system, BlinkMacSystemFont, 'Hiragino Sans', 'Yu Gothic', 'Meiryo', sans-serif"
|
|
907
|
+
},
|
|
908
|
+
light: {
|
|
909
|
+
border: "#d4d4d8",
|
|
910
|
+
borderStrong: "#a1a1aa",
|
|
911
|
+
error: "#ef4444",
|
|
912
|
+
focusRing: "#dbeafe",
|
|
913
|
+
muted: "#71717a",
|
|
914
|
+
overlayBackdrop: "rgb(15 23 42 / 0.86)",
|
|
915
|
+
primary: "#3b82f6",
|
|
916
|
+
primaryContrast: "#ffffff",
|
|
917
|
+
primaryHover: "#2563eb",
|
|
918
|
+
primaryMuted: "#dbeafe",
|
|
919
|
+
primarySubtle: "#eff6ff",
|
|
920
|
+
success: "#22c55e",
|
|
921
|
+
surface: "#ffffff",
|
|
922
|
+
surfaceMuted: "#f4f4f5",
|
|
923
|
+
surfaceStrong: "#e4e4e7",
|
|
924
|
+
text: "#18181b",
|
|
925
|
+
textSubtle: "#52525b"
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
var createChaeditorThemeVars = (theme) => {
|
|
929
|
+
const vars = {};
|
|
930
|
+
if (theme.border) vars[CHAEDITOR_THEME_VARIABLES.border] = theme.border;
|
|
931
|
+
if (theme.borderStrong) vars[CHAEDITOR_THEME_VARIABLES.borderStrong] = theme.borderStrong;
|
|
932
|
+
if (theme.error) vars[CHAEDITOR_THEME_VARIABLES.error] = theme.error;
|
|
933
|
+
if (theme.focusRing) vars[CHAEDITOR_THEME_VARIABLES.focusRing] = theme.focusRing;
|
|
934
|
+
if (theme.monoFont) vars[CHAEDITOR_THEME_VARIABLES.monoFont] = theme.monoFont;
|
|
935
|
+
if (theme.muted) vars[CHAEDITOR_THEME_VARIABLES.muted] = theme.muted;
|
|
936
|
+
if (theme.overlayBackdrop) {
|
|
937
|
+
vars[CHAEDITOR_THEME_VARIABLES.overlayBackdrop] = theme.overlayBackdrop;
|
|
938
|
+
}
|
|
939
|
+
if (theme.primary) vars[CHAEDITOR_THEME_VARIABLES.primary] = theme.primary;
|
|
940
|
+
if (theme.primaryContrast) {
|
|
941
|
+
vars[CHAEDITOR_THEME_VARIABLES.primaryContrast] = theme.primaryContrast;
|
|
942
|
+
}
|
|
943
|
+
if (theme.primaryHover) vars[CHAEDITOR_THEME_VARIABLES.primaryHover] = theme.primaryHover;
|
|
944
|
+
if (theme.primaryMuted) vars[CHAEDITOR_THEME_VARIABLES.primaryMuted] = theme.primaryMuted;
|
|
945
|
+
if (theme.primarySubtle) vars[CHAEDITOR_THEME_VARIABLES.primarySubtle] = theme.primarySubtle;
|
|
946
|
+
if (theme.sansFont) vars[CHAEDITOR_THEME_VARIABLES.sansFont] = theme.sansFont;
|
|
947
|
+
if (theme.sansJaFont) vars[CHAEDITOR_THEME_VARIABLES.sansJaFont] = theme.sansJaFont;
|
|
948
|
+
if (theme.success) vars[CHAEDITOR_THEME_VARIABLES.success] = theme.success;
|
|
949
|
+
if (theme.surface) vars[CHAEDITOR_THEME_VARIABLES.surface] = theme.surface;
|
|
950
|
+
if (theme.surfaceMuted) vars[CHAEDITOR_THEME_VARIABLES.surfaceMuted] = theme.surfaceMuted;
|
|
951
|
+
if (theme.surfaceStrong) vars[CHAEDITOR_THEME_VARIABLES.surfaceStrong] = theme.surfaceStrong;
|
|
952
|
+
if (theme.text) vars[CHAEDITOR_THEME_VARIABLES.text] = theme.text;
|
|
953
|
+
if (theme.textSubtle) vars[CHAEDITOR_THEME_VARIABLES.textSubtle] = theme.textSubtle;
|
|
954
|
+
return vars;
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
// src/entities/editor-core/model/toolbar-preset.ts
|
|
958
|
+
var DEFAULT_MARKDOWN_TOOLBAR_PRESET = [
|
|
959
|
+
{
|
|
960
|
+
itemKeys: ["heading-popover", "subtext"],
|
|
961
|
+
key: "heading-and-subtext"
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
itemKeys: ["bold", "italic", "strike", "underline"],
|
|
965
|
+
key: "text-emphasis"
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
itemKeys: ["text-color", "background-color", "align"],
|
|
969
|
+
key: "highlight-and-alignment"
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
itemKeys: ["horizontal-rule", "quote", "code-block", "table", "spoiler", "toggle-popover"],
|
|
973
|
+
key: "block-syntax"
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
itemKeys: ["math-embed", "file-embed", "image-embed", "link-embed", "video-embed"],
|
|
977
|
+
key: "embed-and-media"
|
|
978
|
+
}
|
|
979
|
+
];
|
|
980
|
+
var resolveMarkdownToolbarPresetSections = ({
|
|
981
|
+
itemRegistry,
|
|
982
|
+
preset = DEFAULT_MARKDOWN_TOOLBAR_PRESET
|
|
983
|
+
}) => preset.map((section) => ({
|
|
984
|
+
items: section.itemKeys.map((itemKey) => itemRegistry[itemKey]).filter((item) => item !== void 0),
|
|
985
|
+
key: section.key
|
|
986
|
+
}));
|
|
987
|
+
|
|
988
|
+
export { CHAEDITOR_THEME_DEFAULTS, CHAEDITOR_THEME_VARIABLES, DEFAULT_MARKDOWN_TOOLBAR_PRESET, EDITOR_ATTACHMENT_FILE_INPUT_ACCEPT, EDITOR_ATTACHMENT_MAX_FILE_SIZE, EDITOR_VIDEO_FILE_INPUT_ACCEPT, EDITOR_VIDEO_MAX_FILE_SIZE, applyTextareaTransform, buildEditorLinkInsertion, continueMarkdownList, createAlignBlockMarkdown, createAttachmentEmbedMarkdown, createChaeditorThemeVars, createImageEmbedMarkdown, createImageEmbedMarkdownGroup, createImageGalleryMarkdown, createMarkdownLink, createMarkdownLinkByMode, createMathEmbedMarkdown, createToggleBlockMarkdown, createUploadedVideoEmbedMarkdown, createVideoEmbedMarkdown, createYoutubeEmbedMarkdown, decodeHtmlAttributeEntities, extractVideoEmbedReference, extractYoutubeId, focusTextarea, getPendingSelection, indentMarkdownList, insertTemplate, isAllowedEditorAttachmentExtension, isAllowedEditorAttachmentFile, isAllowedEditorVideoExtension, isAllowedEditorVideoFile, normalizeMarkdownHtmlAliases, outdentMarkdownList, parseRichMarkdownSegments, parseToggleTitle, prefixLine, preprocessMarkdownInlineSyntax, resolveMarkdownToolbarPresetSections, restoreCursor, toggleHeadingLine, transformMarkdownOutsideCode, wrapSelection };
|