diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html
index 8d2d8b25c9..fbf00c7dd7 100644
--- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html
+++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html
@@ -47,7 +47,11 @@
{{ item.name }}
- person
+ @if (item.creatorVerified) {
+ verified
+ } @else {
+ person
+ }
{{ item.creatorDisplayName }}
diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss
index 2713303d6c..cdf4005199 100644
--- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss
+++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss
@@ -37,6 +37,9 @@
background: rgba(4, 138, 211, 0.04);
border-color: rgba(4, 138, 211, 0.32);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+ .tb-iot-hub-item-link-name, .tb-iot-hub-item-link-author {
+ text-decoration: underline;
+ }
}
&--unavailable {
@@ -128,6 +131,11 @@
width: 14px;
height: 14px;
}
+
+ // Verified-creator accent — matches the item card chip color.
+ .tb-iot-hub-item-link-verified-icon {
+ color: #00695c;
+ }
}
.tb-iot-hub-item-link-skeleton-block,
diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss
index 57e3d25101..be2ab69a76 100644
--- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss
+++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss
@@ -128,7 +128,7 @@ $gallery-border-hover-color: #2a7dec;
max-width: 100%;
}
- a:not(.mdc-button) {
+ a:not(.mdc-button):not(.tb-iot-hub-item-link-card) {
font-weight: 500;
color: $link-color;
text-decoration: none;
diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts
index 57df5a8074..a63d17c914 100644
--- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts
+++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts
@@ -109,9 +109,36 @@ export class TbIotHubMarkdownComponent implements OnInit, OnChanges {
parsed = replaceItemLinkPlaceholders(parsed);
parsed = this.resolveImages(parsed);
parsed = this.resolveVariables(parsed);
+ parsed = this.forceLinksOpenInNewTab(parsed);
return parsed;
}
+ // Ensure every link rendered through tb-markdown opens in a new tab.
+ // 1) For markdown links `[text](url)` — append the
+ // `{:target="_blank"}` suffix to the link TEXT (not the URL)
+ // so MarkedOptionsService.renderer.link, which checks
+ // `token.text.endsWith(targetBlankBlock)`, emits
+ // `target="_blank"` on the rendered . Skip links whose
+ // text already ends with the suffix and skip image syntax
+ // (``).
+ // 2) For raw HTML anchors authored in the markdown — add
+ // `target="_blank"` when no `target=` attribute is already
+ // present.
+ private forceLinksOpenInNewTab(content: string): string {
+ const TARGET_BLANK_BLOCK = '{:target="_blank"}';
+ content = content.replace(
+ /(?
+ text.endsWith(TARGET_BLANK_BLOCK) ? match : `[${text}${TARGET_BLANK_BLOCK}](${url})`
+ );
+ content = content.replace(
+ /]*)>/gi,
+ (match, attrs: string) =>
+ /\btarget\s*=/i.test(attrs) ? match : ``
+ );
+ return content;
+ }
+
private prefixResourceUrls(markdown: string): string {
const baseUrl = this.iotHubApiService.baseUrl;
return markdown.replace(/([("])(\/api\/resources\/[^)"]*)/g, `$1${baseUrl}$2`);