@cyberyozh/text-editor 0.4.0 → 0.5.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/dist/editor.d.mts CHANGED
@@ -50,6 +50,12 @@ interface TiptapProps {
50
50
  */
51
51
  lang?: SupportedLanguage;
52
52
  error?: string;
53
+ /**
54
+ * Domains (with all their subdomains) whose links are treated as internal:
55
+ * rel="noopener noreferrer nofollow" is not added on paste / HTML parsing.
56
+ * Read once when the editor instance is created.
57
+ */
58
+ trustedDomains?: string[];
53
59
  }
54
60
  interface KatexDialogContextProps {
55
61
  latex?: string;
@@ -57,7 +63,15 @@ interface KatexDialogContextProps {
57
63
  }
58
64
  declare const Tiptap: (props: TiptapProps) => react_jsx_runtime.JSX.Element;
59
65
 
60
- declare function htmlToTiptapContent(content: string | JSONContent$1): JSONContent$1;
66
+ interface SharedExtensionsOptions {
67
+ /**
68
+ * Domains (with all their subdomains) whose links are treated as internal —
69
+ * see the trustedDomains option of LinkExtension.
70
+ */
71
+ trustedDomains?: string[];
72
+ }
73
+
74
+ declare function htmlToTiptapContent(content: string | JSONContent$1, options?: SharedExtensionsOptions): JSONContent$1;
61
75
 
62
76
  interface CombinedErrors extends Error {
63
77
  cause: Array<Error>;
package/dist/editor.d.ts CHANGED
@@ -50,6 +50,12 @@ interface TiptapProps {
50
50
  */
51
51
  lang?: SupportedLanguage;
52
52
  error?: string;
53
+ /**
54
+ * Domains (with all their subdomains) whose links are treated as internal:
55
+ * rel="noopener noreferrer nofollow" is not added on paste / HTML parsing.
56
+ * Read once when the editor instance is created.
57
+ */
58
+ trustedDomains?: string[];
53
59
  }
54
60
  interface KatexDialogContextProps {
55
61
  latex?: string;
@@ -57,7 +63,15 @@ interface KatexDialogContextProps {
57
63
  }
58
64
  declare const Tiptap: (props: TiptapProps) => react_jsx_runtime.JSX.Element;
59
65
 
60
- declare function htmlToTiptapContent(content: string | JSONContent$1): JSONContent$1;
66
+ interface SharedExtensionsOptions {
67
+ /**
68
+ * Domains (with all their subdomains) whose links are treated as internal —
69
+ * see the trustedDomains option of LinkExtension.
70
+ */
71
+ trustedDomains?: string[];
72
+ }
73
+
74
+ declare function htmlToTiptapContent(content: string | JSONContent$1, options?: SharedExtensionsOptions): JSONContent$1;
61
75
 
62
76
  interface CombinedErrors extends Error {
63
77
  cause: Array<Error>;
package/dist/editor.js CHANGED
@@ -4334,8 +4334,29 @@ var isSameDomain = (url) => {
4334
4334
  return false;
4335
4335
  }
4336
4336
  };
4337
+ var isTrustedDomain = (url, trustedDomains) => {
4338
+ try {
4339
+ const { hostname } = new URL(url);
4340
+ if (typeof window !== "undefined" && hostname === window.location.hostname) {
4341
+ return true;
4342
+ }
4343
+ return trustedDomains.some((trusted) => {
4344
+ const domain2 = trusted.toLowerCase();
4345
+ return hostname === domain2 || hostname.endsWith(`.${domain2}`);
4346
+ });
4347
+ } catch {
4348
+ return false;
4349
+ }
4350
+ };
4337
4351
  var LinkExtension = import_extension_link.default.extend({
4352
+ addOptions() {
4353
+ return {
4354
+ ...this.parent?.() ?? {},
4355
+ trustedDomains: []
4356
+ };
4357
+ },
4338
4358
  addAttributes() {
4359
+ const { trustedDomains } = this.options;
4339
4360
  return {
4340
4361
  ...this.parent?.(),
4341
4362
  target: {
@@ -4349,7 +4370,7 @@ var LinkExtension = import_extension_link.default.extend({
4349
4370
  default: null,
4350
4371
  parseHTML: (element) => {
4351
4372
  const href = element.getAttribute("href") ?? "";
4352
- return isSameDomain(href) ? null : "noopener noreferrer nofollow";
4373
+ return isTrustedDomain(href, trustedDomains) ? null : "noopener noreferrer nofollow";
4353
4374
  }
4354
4375
  }
4355
4376
  };
@@ -4367,15 +4388,20 @@ var LinkExtension = import_extension_link.default.extend({
4367
4388
  protocols,
4368
4389
  defaultProtocol
4369
4390
  })
4370
- ).filter((link) => this.options.shouldAutoLink?.(link.value) !== false).map((link) => ({ text: link.value, data: { href: link.href }, index: link.start })),
4391
+ ).filter(
4392
+ (link) => this.options.shouldAutoLink?.(link.value) !== false
4393
+ ).map((link) => ({
4394
+ text: link.value,
4395
+ data: { href: link.href },
4396
+ index: link.start
4397
+ })),
4371
4398
  type: this.type,
4372
4399
  getAttributes: (match) => {
4373
4400
  const href = match.data?.href ?? "";
4374
- const sameDomain = isSameDomain(href);
4375
4401
  return {
4376
4402
  href,
4377
- target: sameDomain ? "_self" : "_blank",
4378
- rel: sameDomain ? null : "noopener noreferrer nofollow"
4403
+ target: isSameDomain(href) ? "_self" : "_blank",
4404
+ rel: isTrustedDomain(href, this.options.trustedDomains) ? null : "noopener noreferrer nofollow"
4379
4405
  };
4380
4406
  }
4381
4407
  })
@@ -4390,7 +4416,11 @@ var LinkExtension = import_extension_link.default.extend({
4390
4416
  if (!view.editable) return false;
4391
4417
  const range = (0, import_core4.getMarkRange)(view.state.doc.resolve(pos), this.type);
4392
4418
  if (!range) return false;
4393
- const selection = import_state3.TextSelection.create(view.state.doc, range.from, range.to);
4419
+ const selection = import_state3.TextSelection.create(
4420
+ view.state.doc,
4421
+ range.from,
4422
+ range.to
4423
+ );
4394
4424
  view.dispatch(view.state.tr.setSelection(selection));
4395
4425
  return true;
4396
4426
  }
@@ -6449,14 +6479,14 @@ var TextStyleKit = import_core17.Extension.create({
6449
6479
  });
6450
6480
 
6451
6481
  // src/utils/get-shared-extensions.ts
6452
- function getSharedExtensions() {
6482
+ function getSharedExtensions(options = {}) {
6453
6483
  return [
6454
6484
  import_starter_kit.default.configure({
6455
6485
  codeBlock: false,
6456
6486
  link: false
6457
6487
  }),
6458
6488
  import_extension_highlight.default.configure({ multicolor: true }),
6459
- LinkExtension,
6489
+ LinkExtension.configure({ trustedDomains: options.trustedDomains ?? [] }),
6460
6490
  import_extension_superscript.default,
6461
6491
  import_extension_subscript.default,
6462
6492
  import_extension_text_align.default.configure({
@@ -6697,9 +6727,10 @@ function getEditorExtensions(options) {
6697
6727
  isEditable = false,
6698
6728
  getImageById,
6699
6729
  onKatexClick,
6700
- onFigureClick
6730
+ onFigureClick,
6731
+ trustedDomains
6701
6732
  } = options;
6702
- const sharedExtensions = getSharedExtensions();
6733
+ const sharedExtensions = getSharedExtensions({ trustedDomains });
6703
6734
  return [
6704
6735
  ...sharedExtensions,
6705
6736
  Callout2,
@@ -7758,7 +7789,8 @@ function TiptapContent({
7758
7789
  isEditable = true,
7759
7790
  isDisabled = false,
7760
7791
  lang,
7761
- error
7792
+ error,
7793
+ trustedDomains
7762
7794
  }) {
7763
7795
  const [linkDialogContext, setLinkDialogContext] = (0, import_react38.useState)(null);
7764
7796
  const [katexDialogContext, setKatexDialogContext] = (0, import_react38.useState)(
@@ -7793,6 +7825,7 @@ function TiptapContent({
7793
7825
  suggestion: createSuggestions(translations2, !!imageHandlers),
7794
7826
  isEditable: isEditable && !disabled,
7795
7827
  getImageById: imageHandlers?.getImage,
7828
+ trustedDomains,
7796
7829
  onKatexClick: (latex, pos) => {
7797
7830
  setKatexDialogContext({ latex, pos });
7798
7831
  openDialogCommand("katex");
@@ -7982,7 +8015,7 @@ var import_extension_table2 = require("@tiptap/extension-table");
7982
8015
  function isJSONContent(value) {
7983
8016
  return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
7984
8017
  }
7985
- function htmlToTiptapContent(content) {
8018
+ function htmlToTiptapContent(content, options = {}) {
7986
8019
  if (isJSONContent(content)) {
7987
8020
  return content;
7988
8021
  }
@@ -7998,7 +8031,7 @@ function htmlToTiptapContent(content) {
7998
8031
  if (!content || content.trim() === "") {
7999
8032
  return { type: "doc", content: [{ type: "paragraph" }] };
8000
8033
  }
8001
- const sharedExtensions = getSharedExtensions();
8034
+ const sharedExtensions = getSharedExtensions(options);
8002
8035
  const extensions = [...sharedExtensions, BaseFigure, import_extension_table2.TableKit, BaseCodeBlock];
8003
8036
  try {
8004
8037
  return (0, import_core20.generateJSON)(content, extensions);