diff --git a/arduino-ide-extension/src/browser/library/library-list-widget.ts b/arduino-ide-extension/src/browser/library/library-list-widget.ts
index 25480de95..690799923 100644
--- a/arduino-ide-extension/src/browser/library/library-list-widget.ts
+++ b/arduino-ide-extension/src/browser/library/library-list-widget.ts
@@ -20,7 +20,7 @@ import { Installable } from '../../common/protocol';
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
import { nls } from '@theia/core/lib/common';
import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer';
-import { findChildTheiaButton } from '../utils/dom';
+import { findChildTheiaButton, splitByBoldTag } from '../utils/dom';
@injectable()
export class LibraryListWidget extends ListWidget<
@@ -81,7 +81,7 @@ export class LibraryListWidget extends ListWidget<
let installDependencies: boolean | undefined = undefined;
if (dependencies.length) {
const message = document.createElement('div');
- message.innerHTML =
+ const textContent =
dependencies.length === 1
? nls.localize(
'arduino/library/needsOneDependency',
@@ -95,6 +95,22 @@ export class LibraryListWidget extends ListWidget<
item.name,
version
);
+ const segments = splitByBoldTag(textContent);
+ if (!segments) {
+ message.textContent = textContent;
+ } else {
+ segments.map((segment) => {
+ const span = document.createElement('span');
+ if (typeof segment === 'string') {
+ span.textContent = segment;
+ } else {
+ const bold = document.createElement('b');
+ bold.textContent = segment.textContent;
+ span.appendChild(bold);
+ }
+ message.appendChild(span);
+ });
+ }
const listContainer = document.createElement('div');
listContainer.style.maxHeight = '300px';
listContainer.style.overflowY = 'auto';
diff --git a/arduino-ide-extension/src/browser/utils/dom.ts b/arduino-ide-extension/src/browser/utils/dom.ts
index 938b496ab..47cc830e6 100644
--- a/arduino-ide-extension/src/browser/utils/dom.ts
+++ b/arduino-ide-extension/src/browser/utils/dom.ts
@@ -35,3 +35,35 @@ export function findChildTheiaButton(
function isHTMLElement(element: Element): element is HTMLElement {
return element instanceof HTMLElement;
}
+
+type Segment = string | { textContent: string; bold: true };
+/**
+ * Returns with an array of `Segments` by splitting raw HTML text on the `` groups. If splitting is not possible, returns `undefined`.
+ * Example: `onetwothreefourfive` will provide an five element length array. Where the 1st and 3rd elements are objects and the rest are string.
+ */
+export function splitByBoldTag(text: string): Segment[] | undefined {
+ const matches = text.matchAll(new RegExp(/<\s*b[^>]*>(.*?)<\s*\/\s*b>/gm));
+ if (!matches) {
+ return undefined;
+ }
+ const segments: Segment[] = [];
+ const textLength = text.length;
+ let processedLength = 0;
+ for (const match of matches) {
+ const { index } = match;
+ if (typeof index === 'number') {
+ if (!segments.length && index) {
+ segments.push(text.substring(0, index));
+ }
+ if (processedLength > 0) {
+ segments.push(text.substring(processedLength, index));
+ }
+ segments.push({ textContent: match[1], bold: true });
+ processedLength = index + match[0].length;
+ }
+ }
+ if (segments.length && textLength > processedLength) {
+ segments.push(text.substring(processedLength));
+ }
+ return segments.length ? segments : undefined;
+}
diff --git a/arduino-ide-extension/src/test/browser/dom.test.ts b/arduino-ide-extension/src/test/browser/dom.test.ts
new file mode 100644
index 000000000..5106024cc
--- /dev/null
+++ b/arduino-ide-extension/src/test/browser/dom.test.ts
@@ -0,0 +1,52 @@
+import { splitByBoldTag } from '../../browser/utils/dom';
+import { expect } from 'chai';
+
+describe('dom', () => {
+ describe('splitByBoldTag', () => {
+ it('should split by bold tags', () => {
+ const actual = splitByBoldTag('onematchOnetwo');
+ const expected = ['one', { textContent: 'matchOne', bold: true }, 'two'];
+ expect(actual).to.be.deep.equal(expected);
+ });
+
+ it('should handle starting bold tags', () => {
+ const actual = splitByBoldTag(
+ 'matchOneonematchTwo two matchThree three'
+ );
+ const expected = [
+ { textContent: 'matchOne', bold: true },
+ 'one',
+ { textContent: 'matchTwo', bold: true },
+ ' two ',
+ { textContent: 'matchThree', bold: true },
+ ' three',
+ ];
+ expect(actual).to.be.deep.equal(expected);
+ });
+
+ it('should handle unclosed bold tags', () => {
+ const actual = splitByBoldTag(
+ 'matchOneonematchTwo two matchThree three '
+ );
+ const expected = [
+ { textContent: 'matchOne', bold: true },
+ 'one',
+ { textContent: 'matchTwo', bold: true },
+ ' two ',
+ { textContent: 'matchThree', bold: true },
+ ' three ',
+ ];
+ expect(actual).to.be.deep.equal(expected);
+ });
+
+ it('should handle no matches', () => {
+ const actual = splitByBoldTag('alma');
+ expect(actual).to.be.undefined;
+ });
+
+ it('should handle empty strings', () => {
+ const actual = splitByBoldTag('');
+ expect(actual).to.be.undefined;
+ });
+ });
+});