Change:
}) : undefined; this._messageWidget.value = new MessageWidget(this._editor, position, typeof message === 'string' ? message : this._message!.element); <del> // close on blur, cursor, model change, dispose this._messageListeners.add(this._editor.onDidBlurEditorText(() => { if (!this._focus) { this.closeMessage(); </del> <ins> // close on blur (debounced to allow to tab into the message), cursor, model change, dispose this._messageListeners.add(Event.debounce(this._editor.onDidBlurEditorText, (last, event) => event, 0)(() => { if (this._mouseOverMessage) { return; // override when mouse over message } if (this._messageWidget.value && dom.isAncestor(document.activeElement, this._messageWidget.value.getDomNode())) { return; // override when focus is inside the message </ins> } <ins> this.closeMessage(); </ins> } )); this._messageListeners.add(this._editor.onDidChangeCursorPosition(() => this.closeMessage())); this._messageListeners.add(this._editor.onDidDispose(() => this.closeMessage())); this._messageListeners.add(this._editor.onDidChangeModel(() => this.closeMessage())); <del> this._messageListeners.add(dom.addDisposableListener(this._messageWidget.value.getDomNode(), dom.EventType.MOUSE_ENTER, () => this._focus = true, true)); this._messageListeners.add(dom.addDisposableListener(this._messageWidget.value.getDomNode(), dom.EventType.MOUSE_LEAVE, () => this._focus = false, true)); // 3sec this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), 3000)); </del> <ins> this._messageListeners.add(dom.addDisposableListener(this._messageWidget.value.getDomNode(), dom.EventType.MOUSE_ENTER, () => this._mouseOverMessage = true, true)); this._messageListeners.add(dom.addDisposableListener(this._messageWidget.value.getDomNode(), dom.EventType.MOUSE_LEAVE, () => this._mouseOverMessage = false, true)); </ins> // close on mouse move let bounds: Range;

Discussion:
Testing Tiniest of nits, and perhaps this is inherited from all hovers, but I felt like the hover disappeared too quickly. If I was reading particularly slowly or looked away and then back at the message, I might've missed it.
felt the same, I think using the workbench hover infra would help.
Also, it disappears when you still hover on it: !
I think given the new complexity of this hover and the actions we should do a tactical fix here. I suggest to remove the timer that removes the message automatically after 3 seconds: There are still other listeners in place to hide the message, so I think we are good.
makes sense.
trying to verify: "readonly hover" means "hover in readonly files" ?
best to verify would be in repo open a file from folder and try to edit in such a file. You should see a hover with a link.

Testing This would benefit from an underline instead of the bold treatment to match the rendering we use in the workspace banner links (e.g. restricted mode) <img width="490" alt="CleanShot 2023-06-26 at 21 53 54 src=""
I also think its hard to discover, but I think we are not using underline for links in hovers so far?
We also do not show underlines in other places we show markdown in hovers: <img width="485" alt="image" src=""I used bold because the blue background of the hover made the contrast poor with our usual blue link color. We could use another color instead though.
I think the intent of the blue color in the message widget is to draw more attention, i.e. giving the signal of an information message vs. just being a normal tooltip. But maybe we can make the background color of the widget the same as hovers and then user blue color for links. I think that way we can still keep the blue border but it will not collide with the links.
Sample with a mix of hover colors and info colors for border: <img width="764" alt="image" src=""
And in dark theme: ! Changes I did: use hover colors use the same border radius of as in editor hovers allow to tab into the message with keyboard to invoke the link within