diff --git a/src/lib/logging/guiSink.scss b/src/lib/logging/guiSink.scss
index 17338bb1c..1f200635f 100644
--- a/src/lib/logging/guiSink.scss
+++ b/src/lib/logging/guiSink.scss
@@ -10,6 +10,8 @@
border: 1px solid;
border-radius: 4px;
font-weight: 500;
+ // Ensure decent wrapping across all browsers.
+ overflow-wrap: break-word;
// Colours come from bootstrap.
&.error {
diff --git a/src/lib/logging/guiSink.tsx b/src/lib/logging/guiSink.tsx
index 6fb9898ef..bc1368337 100644
--- a/src/lib/logging/guiSink.tsx
+++ b/src/lib/logging/guiSink.tsx
@@ -1,3 +1,4 @@
+import { insertBetween } from '@lib/util/array';
import { insertStylesheet } from '@lib/util/css';
import type { LoggingSink } from './sink';
@@ -20,8 +21,15 @@ export class GuiSink implements LoggingSink {
private createMessage(className: string, message: string, exception?: unknown): HTMLSpanElement {
const extraMessage = exception instanceof Error ? `: ${exception.message}` : '';
const content = message + extraMessage;
-
- return {content};
+ // Insert word-break hints before all forward slashes so that URLs are
+ // more likely to be split in natural places. In case it's still too
+ // long, CSS properties will break even further, but without these,
+ // the long URLs tend to be put on their own line.
+ // Need to use a factory for insertBetween, otherwise the same
+ // element will be reused and it'll only be placed at the last slash.
+ const children = insertBetween(content.split(/(?=\/)/), () => );
+
+ return {children};
}
private addMessage(el: HTMLSpanElement): void {
diff --git a/src/lib/util/array.ts b/src/lib/util/array.ts
index a224947ff..4b4609fc0 100644
--- a/src/lib/util/array.ts
+++ b/src/lib/util/array.ts
@@ -44,3 +44,26 @@ export function collatedSort(array: string[]): string[] {
export function enumerate(array: T[]): Array<[T, number]> {
return array.map((el, idx) => [el, idx]);
}
+
+function isFactory(maybeFactory: T2 | (() => T2)): maybeFactory is () => T2 {
+ return typeof maybeFactory === 'function';
+}
+
+/**
+ * Create an array wherein a given element is inserted between every two
+ * consecutive elements of the original array.
+ *
+ * Example:
+ * insertBetween([1,2,3], 0) // => [1, 0, 2, 0, 3]
+ * insertBetween([1], 0) // => [1]
+ *
+ * @param {readonly T1[]} arr The original array.
+ * @param {T2} newElement The element to insert, or a factory creating these elements.
+ * @return {(Array)} Resulting array.
+ */
+export function insertBetween(arr: readonly T1[], newElement: T2 | (() => T2)): Array {
+ return [
+ ...arr.slice(0, 1),
+ ...arr.slice(1).flatMap((elmt) => [isFactory(newElement) ? newElement() : newElement, elmt]),
+ ];
+}