@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.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 isSameDomain(href) ? null : "noopener noreferrer nofollow";
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((link) => this.options.shouldAutoLink?.(link.value) !== false).map((link) => ({ text: link.value, data: { href: link.href }, index: link.start })),
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: sameDomain ? "_self" : "_blank",
4341
- rel: sameDomain ? null : "noopener noreferrer nofollow"
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(view.state.doc, range.from, range.to);
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 { Details, DetailsContent, DetailsSummary } from "@tiptap/extension-details";
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.preventDefault();
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(view.state.tr.replaceSelection(removeImagesFromSlice(slice)));
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);