@cyberyozh/text-editor 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.d.mts +15 -1
- package/dist/editor.d.ts +15 -1
- package/dist/editor.js +56 -17
- package/dist/editor.js.map +1 -1
- package/dist/editor.mjs +61 -18
- package/dist/editor.mjs.map +1 -1
- package/dist/render.d.mts +9 -1
- package/dist/render.d.ts +9 -1
- package/dist/render.js +40 -10
- package/dist/render.js.map +1 -1
- package/dist/render.mjs +45 -11
- package/dist/render.mjs.map +1 -1
- package/package.json +1 -1
- package/src/editor/editor.tsx +8 -0
- package/src/editor/handlers/insertion-handlers.test.ts +37 -0
- package/src/editor/handlers/insertion-handlers.ts +14 -9
- package/src/editor/handlers/markup-insertion.ts +9 -0
- package/src/extensions/base/link.test.ts +60 -0
- package/src/extensions/base/link.ts +61 -9
- package/src/extensions/editor/get-editor-extensions.ts +3 -1
- package/src/utils/get-shared-extensions.ts +20 -4
- package/src/utils/html-to-tiptap-content.ts +13 -4
package/dist/editor.mjs
CHANGED
|
@@ -4297,8 +4297,29 @@ var isSameDomain = (url) => {
|
|
|
4297
4297
|
return false;
|
|
4298
4298
|
}
|
|
4299
4299
|
};
|
|
4300
|
+
var isTrustedDomain = (url, trustedDomains) => {
|
|
4301
|
+
try {
|
|
4302
|
+
const { hostname } = new URL(url);
|
|
4303
|
+
if (typeof window !== "undefined" && hostname === window.location.hostname) {
|
|
4304
|
+
return true;
|
|
4305
|
+
}
|
|
4306
|
+
return trustedDomains.some((trusted) => {
|
|
4307
|
+
const domain2 = trusted.toLowerCase();
|
|
4308
|
+
return hostname === domain2 || hostname.endsWith(`.${domain2}`);
|
|
4309
|
+
});
|
|
4310
|
+
} catch {
|
|
4311
|
+
return false;
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4300
4314
|
var LinkExtension = Link.extend({
|
|
4315
|
+
addOptions() {
|
|
4316
|
+
return {
|
|
4317
|
+
...this.parent?.() ?? {},
|
|
4318
|
+
trustedDomains: []
|
|
4319
|
+
};
|
|
4320
|
+
},
|
|
4301
4321
|
addAttributes() {
|
|
4322
|
+
const { trustedDomains } = this.options;
|
|
4302
4323
|
return {
|
|
4303
4324
|
...this.parent?.(),
|
|
4304
4325
|
target: {
|
|
@@ -4312,7 +4333,7 @@ var LinkExtension = Link.extend({
|
|
|
4312
4333
|
default: null,
|
|
4313
4334
|
parseHTML: (element) => {
|
|
4314
4335
|
const href = element.getAttribute("href") ?? "";
|
|
4315
|
-
return
|
|
4336
|
+
return isTrustedDomain(href, trustedDomains) ? null : "noopener noreferrer nofollow";
|
|
4316
4337
|
}
|
|
4317
4338
|
}
|
|
4318
4339
|
};
|
|
@@ -4330,15 +4351,20 @@ var LinkExtension = Link.extend({
|
|
|
4330
4351
|
protocols,
|
|
4331
4352
|
defaultProtocol
|
|
4332
4353
|
})
|
|
4333
|
-
).filter(
|
|
4354
|
+
).filter(
|
|
4355
|
+
(link) => this.options.shouldAutoLink?.(link.value) !== false
|
|
4356
|
+
).map((link) => ({
|
|
4357
|
+
text: link.value,
|
|
4358
|
+
data: { href: link.href },
|
|
4359
|
+
index: link.start
|
|
4360
|
+
})),
|
|
4334
4361
|
type: this.type,
|
|
4335
4362
|
getAttributes: (match) => {
|
|
4336
4363
|
const href = match.data?.href ?? "";
|
|
4337
|
-
const sameDomain = isSameDomain(href);
|
|
4338
4364
|
return {
|
|
4339
4365
|
href,
|
|
4340
|
-
target:
|
|
4341
|
-
rel:
|
|
4366
|
+
target: isSameDomain(href) ? "_self" : "_blank",
|
|
4367
|
+
rel: isTrustedDomain(href, this.options.trustedDomains) ? null : "noopener noreferrer nofollow"
|
|
4342
4368
|
};
|
|
4343
4369
|
}
|
|
4344
4370
|
})
|
|
@@ -4353,7 +4379,11 @@ var LinkExtension = Link.extend({
|
|
|
4353
4379
|
if (!view.editable) return false;
|
|
4354
4380
|
const range = getMarkRange(view.state.doc.resolve(pos), this.type);
|
|
4355
4381
|
if (!range) return false;
|
|
4356
|
-
const selection = TextSelection3.create(
|
|
4382
|
+
const selection = TextSelection3.create(
|
|
4383
|
+
view.state.doc,
|
|
4384
|
+
range.from,
|
|
4385
|
+
range.to
|
|
4386
|
+
);
|
|
4357
4387
|
view.dispatch(view.state.tr.setSelection(selection));
|
|
4358
4388
|
return true;
|
|
4359
4389
|
}
|
|
@@ -6069,7 +6099,11 @@ var Commands = Extension.create({
|
|
|
6069
6099
|
|
|
6070
6100
|
// src/utils/get-shared-extensions.ts
|
|
6071
6101
|
import StarterKit from "@tiptap/starter-kit";
|
|
6072
|
-
import {
|
|
6102
|
+
import {
|
|
6103
|
+
Details,
|
|
6104
|
+
DetailsContent,
|
|
6105
|
+
DetailsSummary
|
|
6106
|
+
} from "@tiptap/extension-details";
|
|
6073
6107
|
import Highlight from "@tiptap/extension-highlight";
|
|
6074
6108
|
import Subscript from "@tiptap/extension-subscript";
|
|
6075
6109
|
import Superscript from "@tiptap/extension-superscript";
|
|
@@ -6412,14 +6446,14 @@ var TextStyleKit = Extension6.create({
|
|
|
6412
6446
|
});
|
|
6413
6447
|
|
|
6414
6448
|
// src/utils/get-shared-extensions.ts
|
|
6415
|
-
function getSharedExtensions() {
|
|
6449
|
+
function getSharedExtensions(options = {}) {
|
|
6416
6450
|
return [
|
|
6417
6451
|
StarterKit.configure({
|
|
6418
6452
|
codeBlock: false,
|
|
6419
6453
|
link: false
|
|
6420
6454
|
}),
|
|
6421
6455
|
Highlight.configure({ multicolor: true }),
|
|
6422
|
-
LinkExtension,
|
|
6456
|
+
LinkExtension.configure({ trustedDomains: options.trustedDomains ?? [] }),
|
|
6423
6457
|
Superscript,
|
|
6424
6458
|
Subscript,
|
|
6425
6459
|
TextAlign.configure({
|
|
@@ -6660,9 +6694,10 @@ function getEditorExtensions(options) {
|
|
|
6660
6694
|
isEditable = false,
|
|
6661
6695
|
getImageById,
|
|
6662
6696
|
onKatexClick,
|
|
6663
|
-
onFigureClick
|
|
6697
|
+
onFigureClick,
|
|
6698
|
+
trustedDomains
|
|
6664
6699
|
} = options;
|
|
6665
|
-
const sharedExtensions = getSharedExtensions();
|
|
6700
|
+
const sharedExtensions = getSharedExtensions({ trustedDomains });
|
|
6666
6701
|
return [
|
|
6667
6702
|
...sharedExtensions,
|
|
6668
6703
|
Callout2,
|
|
@@ -6896,6 +6931,12 @@ function applyUploadResultsToNodes(imagesToNodes, results) {
|
|
|
6896
6931
|
node.attrs.storageKey = result.storageKey;
|
|
6897
6932
|
});
|
|
6898
6933
|
}
|
|
6934
|
+
function shouldHandleInsertedFile(file, event) {
|
|
6935
|
+
if (!file) return false;
|
|
6936
|
+
event.preventDefault();
|
|
6937
|
+
event.stopPropagation();
|
|
6938
|
+
return file.type.startsWith("image/");
|
|
6939
|
+
}
|
|
6899
6940
|
|
|
6900
6941
|
// src/editor/handlers/insertion-handlers.ts
|
|
6901
6942
|
function useInsertionHandlers(imageHandlers) {
|
|
@@ -6907,9 +6948,7 @@ function useInsertionHandlers(imageHandlers) {
|
|
|
6907
6948
|
}
|
|
6908
6949
|
}, [insertedImage, activeDialog]);
|
|
6909
6950
|
function genericInsertionHandler(file, event) {
|
|
6910
|
-
event
|
|
6911
|
-
event.stopPropagation();
|
|
6912
|
-
if (!file || !file.type.startsWith("image/")) return;
|
|
6951
|
+
if (!file || !shouldHandleInsertedFile(file, event)) return;
|
|
6913
6952
|
if (!imageHandlers?.upload) return;
|
|
6914
6953
|
setInsertedImage(file);
|
|
6915
6954
|
openDialogCommand("image");
|
|
@@ -6945,7 +6984,9 @@ function useInsertionHandlers(imageHandlers) {
|
|
|
6945
6984
|
setDisabled(false);
|
|
6946
6985
|
});
|
|
6947
6986
|
} else {
|
|
6948
|
-
view.dispatch(
|
|
6987
|
+
view.dispatch(
|
|
6988
|
+
view.state.tr.replaceSelection(removeImagesFromSlice(slice))
|
|
6989
|
+
);
|
|
6949
6990
|
}
|
|
6950
6991
|
return true;
|
|
6951
6992
|
}
|
|
@@ -7721,7 +7762,8 @@ function TiptapContent({
|
|
|
7721
7762
|
isEditable = true,
|
|
7722
7763
|
isDisabled = false,
|
|
7723
7764
|
lang,
|
|
7724
|
-
error
|
|
7765
|
+
error,
|
|
7766
|
+
trustedDomains
|
|
7725
7767
|
}) {
|
|
7726
7768
|
const [linkDialogContext, setLinkDialogContext] = useState13(null);
|
|
7727
7769
|
const [katexDialogContext, setKatexDialogContext] = useState13(
|
|
@@ -7756,6 +7798,7 @@ function TiptapContent({
|
|
|
7756
7798
|
suggestion: createSuggestions(translations2, !!imageHandlers),
|
|
7757
7799
|
isEditable: isEditable && !disabled,
|
|
7758
7800
|
getImageById: imageHandlers?.getImage,
|
|
7801
|
+
trustedDomains,
|
|
7759
7802
|
onKatexClick: (latex, pos) => {
|
|
7760
7803
|
setKatexDialogContext({ latex, pos });
|
|
7761
7804
|
openDialogCommand("katex");
|
|
@@ -7945,7 +7988,7 @@ import { TableKit as TableKit2 } from "@tiptap/extension-table";
|
|
|
7945
7988
|
function isJSONContent(value) {
|
|
7946
7989
|
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
7947
7990
|
}
|
|
7948
|
-
function htmlToTiptapContent(content) {
|
|
7991
|
+
function htmlToTiptapContent(content, options = {}) {
|
|
7949
7992
|
if (isJSONContent(content)) {
|
|
7950
7993
|
return content;
|
|
7951
7994
|
}
|
|
@@ -7961,7 +8004,7 @@ function htmlToTiptapContent(content) {
|
|
|
7961
8004
|
if (!content || content.trim() === "") {
|
|
7962
8005
|
return { type: "doc", content: [{ type: "paragraph" }] };
|
|
7963
8006
|
}
|
|
7964
|
-
const sharedExtensions = getSharedExtensions();
|
|
8007
|
+
const sharedExtensions = getSharedExtensions(options);
|
|
7965
8008
|
const extensions = [...sharedExtensions, BaseFigure, TableKit2, BaseCodeBlock];
|
|
7966
8009
|
try {
|
|
7967
8010
|
return generateJSON(content, extensions);
|