From 5d02526835497fe0673050bd61ace4c03b22e305 Mon Sep 17 00:00:00 2001 From: HowieHz Date: Sat, 21 Mar 2026 18:49:03 +0800 Subject: [PATCH 1/2] pref: Use rel="noopener noreferrer nofollow ugc" on user links --- packages/comment-widget/package.json | 1 + .../comment-widget/src/base-comment-item.ts | 2 +- .../comment-widget/src/comment-content.ts | 17 +++++++++ packages/comment-widget/src/comment-editor.ts | 13 +++++-- packages/comment-widget/src/user-avatar.ts | 2 +- pnpm-lock.yaml | 36 ++++++++++++++++++- 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/packages/comment-widget/package.json b/packages/comment-widget/package.json index fbd7057..b603e80 100644 --- a/packages/comment-widget/package.json +++ b/packages/comment-widget/package.json @@ -45,6 +45,7 @@ "@lit/context": "^1.1.6", "@lit/localize": "^0.12.2", "@tiptap/core": "^3.10.4", + "@tiptap/extension-link": "^3.10.4", "@tiptap/extensions": "^3.10.4", "@tiptap/pm": "^3.10.4", "@tiptap/starter-kit": "^3.10.4", diff --git a/packages/comment-widget/src/base-comment-item.ts b/packages/comment-widget/src/base-comment-item.ts index 90c6b23..c8e1ed8 100644 --- a/packages/comment-widget/src/base-comment-item.ts +++ b/packages/comment-widget/src/base-comment-item.ts @@ -62,7 +62,7 @@ export class BaseCommentItem extends LitElement { class="item-author font-medium text-sm text-text-1 hover:underline" target="_blank" href=${ifDefined(this.userWebsite)} - rel="noopener noreferrer" + rel="noopener noreferrer nofollow ugc" > ${this.userDisplayName} diff --git a/packages/comment-widget/src/comment-content.ts b/packages/comment-widget/src/comment-content.ts index 9e8367f..05d6ee1 100644 --- a/packages/comment-widget/src/comment-content.ts +++ b/packages/comment-widget/src/comment-content.ts @@ -9,6 +9,16 @@ export class CommentContent extends LitElement { @property({ type: String }) content: string = ''; + private applyLinkAttributes() { + const anchors = + this.shadowRoot?.querySelectorAll('.content a'); + + anchors?.forEach((anchor) => { + anchor.target = '_blank'; + anchor.rel = 'noopener noreferrer nofollow ugc'; + }); + } + protected override firstUpdated(_changedProperties: PropertyValues) { super.firstUpdated(_changedProperties); const codeElements = this.shadowRoot?.querySelectorAll('pre>code'); @@ -38,6 +48,13 @@ export class CommentContent extends LitElement { ); } + protected override updated(_changedProperties: PropertyValues) { + super.updated(_changedProperties); + if (_changedProperties.has('content')) { + this.applyLinkAttributes(); + } + } + private extractLanguageFromCodeElement(codeElement: Element): string | null { const supportedPrefixes = ['language-', 'lang-']; diff --git a/packages/comment-widget/src/comment-editor.ts b/packages/comment-widget/src/comment-editor.ts index 4f9357d..45027f2 100644 --- a/packages/comment-widget/src/comment-editor.ts +++ b/packages/comment-widget/src/comment-editor.ts @@ -95,12 +95,12 @@ export class CommentEditor extends LitElement { async createEditor() { const { Editor } = await import('@tiptap/core'); - const { Placeholder } = await import('@tiptap/extensions'); + const { CharacterCount, Placeholder } = await import('@tiptap/extensions'); + const { Link } = await import('@tiptap/extension-link'); const { StarterKit } = await import('@tiptap/starter-kit'); const { CodeBlockShiki } = await import( 'tiptap-extension-code-block-shiki' ); - const { CharacterCount } = await import('@tiptap/extensions'); this.loading = false; @@ -119,6 +119,15 @@ export class CommentEditor extends LitElement { placeholder: this.placeholder || msg('Write a comment'), }), + Link.configure({ + openOnClick: false, + defaultProtocol: 'https', + HTMLAttributes: { + target: '_blank', + rel: 'noopener noreferrer nofollow ugc', + }, + }), + CodeBlockShiki.configure({ defaultTheme: 'github-dark', }), diff --git a/packages/comment-widget/src/user-avatar.ts b/packages/comment-widget/src/user-avatar.ts index 692e74b..56e7fee 100644 --- a/packages/comment-widget/src/user-avatar.ts +++ b/packages/comment-widget/src/user-avatar.ts @@ -67,7 +67,7 @@ export class UserAvatar extends LitElement { override render() { if (this.href) { - return html` + return html` ${this.renderAvatarContent()} `; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2da9d10..172f044 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,9 @@ importers: '@tiptap/core': specifier: ^3.10.4 version: 3.10.4(@tiptap/pm@3.10.4) + '@tiptap/extension-link': + specifier: ^3.10.4 + version: 3.10.4(@tiptap/core@3.10.4(@tiptap/pm@3.10.4))(@tiptap/pm@3.10.4) '@tiptap/extensions': specifier: ^3.10.4 version: 3.10.4(@tiptap/core@3.10.4(@tiptap/pm@3.10.4))(@tiptap/pm@3.10.4) @@ -132,7 +135,7 @@ importers: version: 2.21.0(vue-router@4.5.1(vue@3.5.24(typescript@5.8.3)))(vue@3.5.24(typescript@5.8.3)) '@halo-dev/console-shared': specifier: link:/Users/ryanwang/Workspace/github/ruibaby/halo-next/ui/packages/shared - version: link:../../../../ruibaby/halo-next/ui/packages/shared + version: link:../../../../../Users/ryanwang/Workspace/github/ruibaby/halo-next/ui/packages/shared vue: specifier: ^3.5.24 version: 3.5.24(typescript@5.8.3) @@ -194,24 +197,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@ast-grep/napi-linux-arm64-musl@0.37.0': resolution: {integrity: sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@ast-grep/napi-linux-x64-gnu@0.37.0': resolution: {integrity: sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@ast-grep/napi-linux-x64-musl@0.37.0': resolution: {integrity: sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@ast-grep/napi-win32-arm64-msvc@0.37.0': resolution: {integrity: sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==} @@ -321,24 +328,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@2.3.4': resolution: {integrity: sha512-y7efHyyM2gYmHy/AdWEip+VgTMe9973aP7XYKPzu/j8JxnPHuSUXftzmPhkVw0lfm4ECGbdBdGD6+rLmTgNZaA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@2.3.4': resolution: {integrity: sha512-mzKFFv/w66e4/jCobFmD3kymCqG+FuWE7sVa4Yjqd9v7qt2UhXo67MSZKY9Ih18V2IwPzRKQPCw6KwdZs6AXSA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@2.3.4': resolution: {integrity: sha512-gKfjWR/6/dfIxPJCw8REdEowiXCkIpl9jycpNVHux8aX2yhWPLjydOshkDL6Y/82PcQJHn95VCj7J+BRcE5o1Q==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@2.3.4': resolution: {integrity: sha512-5TJ6JfVez+yyupJ/iGUici2wzKf0RrSAxJhghQXtAEsc67OIpdwSKAQboemILrwKfHDi5s6mu7mX+VTCTUydkw==} @@ -719,11 +730,13 @@ packages: resolution: {integrity: sha512-hzBmOtYdC4369XxN2SNJ3oBlXKWNif3ieWBT+oh/qvAeox4fQR0ngqyh+kIGOufBnP5Zc2rqJf9LzIbJw3Tx/Q==} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-beta.29': resolution: {integrity: sha512-6B35GmFJJ4RX88OgubrnUmuJBUgRh6/OTXIpy8m/VUnoc683lufIPo26HW/0LxLgxp2GM7KHr3LOULcVxbqq4Q==} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-arm64-ohos@1.0.0-beta.29': resolution: {integrity: sha512-z3ru8fUCunQM8q9I7RbDVMT5cxzxVVVBNNKM5/qAQQrdObd1u8g0LR5z0yLtaFWzybwLVdPtJDRcXtLm5tOBFA==} @@ -734,11 +747,13 @@ packages: resolution: {integrity: sha512-n6fs4L7j99MIiI6vKhQDdyScv4/uMAPtIMkB0zGbUX8MKWT1osym1hvWVdlENjnS/Phf0zzhjyOgoFDzdhI1cQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-beta.29': resolution: {integrity: sha512-C5hcJgtDN4rp6/WsPTQSDVUWrdnIC//ynMGcUIh1O0anm9KnSy47zKQ5D9EqtlEKvO+2PPqmyUVJ2DTq18nlVA==} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-wasm32-wasi@1.0.0-beta.29': resolution: {integrity: sha512-lMN1IBItdZFO182Sdus9oVuNDqyIymn/bsR5KwgeGaiqLsrmpQHBSLwkS/nKJO1nzYlpGDRugFSpnrSJ5ZmihQ==} @@ -806,56 +821,67 @@ packages: resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.46.2': resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.46.2': resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.46.2': resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.46.2': resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.46.2': resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.46.2': resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.46.2': resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.46.2': resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.46.2': resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.46.2': resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.46.2': resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} @@ -909,21 +935,25 @@ packages: resolution: {integrity: sha512-n7UGSBzv7PiX+V1Q2bY3S1XWyN3RCykCQUgfhZ+xWietCM/1349jgN7DoXKPllqlof1GPGBjziHU0sQZTC4tag==} cpu: [arm64] os: [linux] + libc: [glibc] '@rspack/binding-linux-arm64-musl@1.6.1': resolution: {integrity: sha512-P7nx0jsKxx7g3QAnH9UnJDGVgs1M2H7ZQl68SRyrs42TKOd9Md22ynoMIgCK1zoy+skssU6MhWptluSggXqSrA==} cpu: [arm64] os: [linux] + libc: [musl] '@rspack/binding-linux-x64-gnu@1.6.1': resolution: {integrity: sha512-SdiurC1bV/QHnj7rmrBYJLdsat3uUDWl9KjkVjEbtc8kQV0Ri4/vZRH0nswgzx7hZNY2j0jYuCm5O8+3qeJEMg==} cpu: [x64] os: [linux] + libc: [glibc] '@rspack/binding-linux-x64-musl@1.6.1': resolution: {integrity: sha512-JoSJu29nV+auOePhe8x2Fzqxiga1YGNcOMWKJ5Uj8rHBZ8FPAiiE+CpLG8TwfpHsivojrY/sy6fE8JldYLV5TQ==} cpu: [x64] os: [linux] + libc: [musl] '@rspack/binding-wasm32-wasi@1.6.1': resolution: {integrity: sha512-u5NiSHxM7LtIo4cebq/hQPJ9o39u127am3eVJHDzdmBVhTYYO5l7XVUnFmcU8hNHuj/4lJzkFviWFbf3SaRSYA==} @@ -2097,24 +2127,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.1: resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.1: resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.1: resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.1: resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} From 5adf7eeaefdafcabd93df401fe20d09adbaf4b2b Mon Sep 17 00:00:00 2001 From: HowieHz Date: Tue, 7 Apr 2026 03:48:07 +0800 Subject: [PATCH 2/2] refactor(comment-widget): use StarterKit link config directly --- packages/comment-widget/package.json | 1 - packages/comment-widget/src/comment-editor.ts | 15 +++++---------- pnpm-lock.yaml | 5 +---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/comment-widget/package.json b/packages/comment-widget/package.json index b603e80..fbd7057 100644 --- a/packages/comment-widget/package.json +++ b/packages/comment-widget/package.json @@ -45,7 +45,6 @@ "@lit/context": "^1.1.6", "@lit/localize": "^0.12.2", "@tiptap/core": "^3.10.4", - "@tiptap/extension-link": "^3.10.4", "@tiptap/extensions": "^3.10.4", "@tiptap/pm": "^3.10.4", "@tiptap/starter-kit": "^3.10.4", diff --git a/packages/comment-widget/src/comment-editor.ts b/packages/comment-widget/src/comment-editor.ts index 45027f2..cda6186 100644 --- a/packages/comment-widget/src/comment-editor.ts +++ b/packages/comment-widget/src/comment-editor.ts @@ -96,7 +96,6 @@ export class CommentEditor extends LitElement { async createEditor() { const { Editor } = await import('@tiptap/core'); const { CharacterCount, Placeholder } = await import('@tiptap/extensions'); - const { Link } = await import('@tiptap/extension-link'); const { StarterKit } = await import('@tiptap/starter-kit'); const { CodeBlockShiki } = await import( 'tiptap-extension-code-block-shiki' @@ -111,6 +110,11 @@ export class CommentEditor extends LitElement { heading: false, link: { openOnClick: false, + defaultProtocol: 'https', + HTMLAttributes: { + target: '_blank', + rel: 'noopener noreferrer nofollow ugc', + }, }, codeBlock: false, }), @@ -119,15 +123,6 @@ export class CommentEditor extends LitElement { placeholder: this.placeholder || msg('Write a comment'), }), - Link.configure({ - openOnClick: false, - defaultProtocol: 'https', - HTMLAttributes: { - target: '_blank', - rel: 'noopener noreferrer nofollow ugc', - }, - }), - CodeBlockShiki.configure({ defaultTheme: 'github-dark', }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 172f044..0d77863 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,9 +60,6 @@ importers: '@tiptap/core': specifier: ^3.10.4 version: 3.10.4(@tiptap/pm@3.10.4) - '@tiptap/extension-link': - specifier: ^3.10.4 - version: 3.10.4(@tiptap/core@3.10.4(@tiptap/pm@3.10.4))(@tiptap/pm@3.10.4) '@tiptap/extensions': specifier: ^3.10.4 version: 3.10.4(@tiptap/core@3.10.4(@tiptap/pm@3.10.4))(@tiptap/pm@3.10.4) @@ -558,7 +555,7 @@ packages: axios: ^1.7.x '@halo-dev/api-client@https://pkg.pr.new/@halo-dev/api-client@7679': - resolution: {tarball: https://pkg.pr.new/@halo-dev/api-client@7679} + resolution: {integrity: sha512-cSjFE8p9hZrKxBhTwhODdxg18J5Hv6ymOCrHjrVizmVKVFgdKF2l3PixmlJcHXEnLhxLvSRrCg51tHKx5ggSvQ==, tarball: https://pkg.pr.new/@halo-dev/api-client@7679} version: 2.21.1 peerDependencies: axios: ^1.7.x