mirror of
https://github.com/coder/code-server.git
synced 2026-05-09 05:47:26 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
131
lib/vscode/src/vs/workbench/services/hover/browser/hover.ts
Normal file
131
lib/vscode/src/vs/workbench/services/hover/browser/hover.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
|
||||
|
||||
export const IHoverService = createDecorator<IHoverService>('hoverService');
|
||||
|
||||
/**
|
||||
* Enables the convenient display of rich markdown-based hovers in the workbench.
|
||||
*/
|
||||
export interface IHoverService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* Shows a hover, provided a hover with the same options object is not already visible.
|
||||
* @param options A set of options defining the characteristics of the hover.
|
||||
* @param focus Whether to focus the hover (useful for keyboard accessibility).
|
||||
*
|
||||
* **Example:** A simple usage with a single element target.
|
||||
*
|
||||
* ```typescript
|
||||
* showHover({
|
||||
* text: new MarkdownString('Hello world'),
|
||||
* target: someElement
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
showHover(options: IHoverOptions, focus?: boolean): IDisposable | undefined;
|
||||
|
||||
/**
|
||||
* Hides the hover if it was visible.
|
||||
*/
|
||||
hideHover(): void;
|
||||
}
|
||||
|
||||
export interface IHoverOptions {
|
||||
/**
|
||||
* The text to display in the primary section of the hover. The type of text determines the
|
||||
* default `hideOnHover` behavior.
|
||||
*/
|
||||
text: IMarkdownString | string;
|
||||
|
||||
/**
|
||||
* The target for the hover. This determines the position of the hover and it will only be
|
||||
* hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for
|
||||
* simple cases and a IHoverTarget for more complex cases where multiple elements and/or a
|
||||
* dispose method is required.
|
||||
*/
|
||||
target: IHoverTarget | HTMLElement;
|
||||
|
||||
/**
|
||||
* A set of actions for the hover's "status bar".
|
||||
*/
|
||||
actions?: IHoverAction[];
|
||||
|
||||
/**
|
||||
* An optional array of classes to add to the hover element.
|
||||
*/
|
||||
additionalClasses?: string[];
|
||||
|
||||
/**
|
||||
* An optional link handler for markdown links, if this is not provided the IOpenerService will
|
||||
* be used to open the links using its default options.
|
||||
*/
|
||||
linkHandler?(url: string): void;
|
||||
|
||||
/**
|
||||
* Whether to hide the hover when the mouse leaves the `target` and enters the actual hover.
|
||||
* This is false by default when text is an `IMarkdownString` and true when `text` is a
|
||||
* `string`. Note that this will be ignored if any `actions` are provided as hovering is
|
||||
* required to make them accessible.
|
||||
*
|
||||
* In general hiding on hover is desired for:
|
||||
* - Regular text where selection is not important
|
||||
* - Markdown that contains no links where selection is not important
|
||||
*/
|
||||
hideOnHover?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to anchor the hover above (default) or below the target. This option will be ignored
|
||||
* if there is not enough room to layout the hover in the specified anchor position.
|
||||
*/
|
||||
anchorPosition?: AnchorPosition;
|
||||
}
|
||||
|
||||
export interface IHoverAction {
|
||||
/**
|
||||
* The label to use in the hover's status bar.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The command ID of the action, this is used to resolve the keybinding to display after the
|
||||
* action label.
|
||||
*/
|
||||
commandId: string;
|
||||
|
||||
/**
|
||||
* An optional class of an icon that will be displayed before the label.
|
||||
*/
|
||||
iconClass?: string;
|
||||
|
||||
/**
|
||||
* The callback to run the action.
|
||||
* @param target The action element that was activated.
|
||||
*/
|
||||
run(target: HTMLElement): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A target for a hover.
|
||||
*/
|
||||
export interface IHoverTarget extends IDisposable {
|
||||
/**
|
||||
* A set of target elements used to position the hover. If multiple elements are used the hover
|
||||
* will try to not overlap any target element. An example use case for this is show a hover for
|
||||
* wrapped text.
|
||||
*/
|
||||
readonly targetElements: readonly HTMLElement[];
|
||||
|
||||
/**
|
||||
* An optional absolute x coordinate to position the hover with, for example to position the
|
||||
* hover using `MouseEvent.pageX`.
|
||||
*/
|
||||
x?: number;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/hover';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { editorHoverBackground, editorHoverBorder, textLinkForeground, editorHoverForeground, editorHoverStatusBarBackground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { HoverWidget } from 'vs/workbench/services/hover/browser/hoverWidget';
|
||||
import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class HoverService implements IHoverService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private _currentHoverOptions: IHoverOptions | undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService
|
||||
) {
|
||||
}
|
||||
|
||||
showHover(options: IHoverOptions, focus?: boolean): IDisposable | undefined {
|
||||
if (this._currentHoverOptions === options) {
|
||||
return undefined;
|
||||
}
|
||||
this._currentHoverOptions = options;
|
||||
|
||||
const hover = this._instantiationService.createInstance(HoverWidget, options);
|
||||
hover.onDispose(() => this._currentHoverOptions = undefined);
|
||||
const provider = this._contextViewService as IContextViewProvider;
|
||||
provider.showContextView(new HoverContextViewDelegate(hover, focus));
|
||||
hover.onRequestLayout(() => provider.layout());
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observer = new IntersectionObserver(e => this._intersectionChange(e, hover), { threshold: 0 });
|
||||
const firstTargetElement = 'targetElements' in options.target ? options.target.targetElements[0] : options.target;
|
||||
observer.observe(firstTargetElement);
|
||||
hover.onDispose(() => observer.disconnect());
|
||||
}
|
||||
|
||||
return hover;
|
||||
}
|
||||
|
||||
hideHover(): void {
|
||||
if (!this._currentHoverOptions) {
|
||||
return;
|
||||
}
|
||||
this._currentHoverOptions = undefined;
|
||||
this._contextViewService.hideContextView();
|
||||
}
|
||||
|
||||
private _intersectionChange(entries: IntersectionObserverEntry[], hover: IDisposable): void {
|
||||
const entry = entries[entries.length - 1];
|
||||
if (!entry.isIntersecting) {
|
||||
hover.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HoverContextViewDelegate implements IDelegate {
|
||||
|
||||
get anchorPosition() {
|
||||
return this._hover.anchor;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _hover: HoverWidget,
|
||||
private readonly _focus: boolean = false
|
||||
) {
|
||||
}
|
||||
|
||||
render(container: HTMLElement) {
|
||||
this._hover.render(container);
|
||||
if (this._focus) {
|
||||
this._hover.focus();
|
||||
}
|
||||
return this._hover;
|
||||
}
|
||||
|
||||
getAnchor() {
|
||||
return {
|
||||
x: this._hover.x,
|
||||
y: this._hover.y
|
||||
};
|
||||
}
|
||||
|
||||
layout() {
|
||||
this._hover.layout();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IHoverService, HoverService, true);
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const hoverBackground = theme.getColor(editorHoverBackground);
|
||||
if (hoverBackground) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover { background-color: ${hoverBackground}; }`);
|
||||
}
|
||||
const hoverBorder = theme.getColor(editorHoverBorder);
|
||||
if (hoverBorder) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover { border: 1px solid ${hoverBorder}; }`);
|
||||
collector.addRule(`.monaco-workbench .workbench-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`);
|
||||
collector.addRule(`.monaco-workbench .workbench-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`);
|
||||
collector.addRule(`.monaco-workbench .workbench-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`);
|
||||
}
|
||||
const link = theme.getColor(textLinkForeground);
|
||||
if (link) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover a { color: ${link}; }`);
|
||||
}
|
||||
const hoverForeground = theme.getColor(editorHoverForeground);
|
||||
if (hoverForeground) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover { color: ${hoverForeground}; }`);
|
||||
}
|
||||
const actionsBackground = theme.getColor(editorHoverStatusBarBackground);
|
||||
if (actionsBackground) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover .hover-row .actions { background-color: ${actionsBackground}; }`);
|
||||
}
|
||||
const codeBackground = theme.getColor(textCodeBlockBackground);
|
||||
if (codeBackground) {
|
||||
collector.addRule(`.monaco-workbench .workbench-hover code { background-color: ${codeBackground}; }`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,274 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IHoverTarget, IHoverOptions } from 'vs/workbench/services/hover/browser/hover';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { HoverWidget as BaseHoverWidget, renderHoverAction } from 'vs/base/browser/ui/hover/hoverWidget';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { MarkdownString, MarkdownStringTextNewlineStyle } from 'vs/base/common/htmlContent';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export class HoverWidget extends Widget {
|
||||
private readonly _messageListeners = new DisposableStore();
|
||||
private readonly _mouseTracker: CompositeMouseTracker;
|
||||
|
||||
private readonly _hover: BaseHoverWidget;
|
||||
private readonly _target: IHoverTarget;
|
||||
private readonly _linkHandler: (url: string) => any;
|
||||
|
||||
private _isDisposed: boolean = false;
|
||||
private _anchor: AnchorPosition;
|
||||
private _x: number = 0;
|
||||
private _y: number = 0;
|
||||
|
||||
get isDisposed(): boolean { return this._isDisposed; }
|
||||
get domNode(): HTMLElement { return this._hover.containerDomNode; }
|
||||
|
||||
private readonly _onDispose = this._register(new Emitter<void>());
|
||||
get onDispose(): Event<void> { return this._onDispose.event; }
|
||||
private readonly _onRequestLayout = this._register(new Emitter<void>());
|
||||
get onRequestLayout(): Event<void> { return this._onRequestLayout.event; }
|
||||
|
||||
get anchor(): AnchorPosition { return this._anchor; }
|
||||
get x(): number { return this._x; }
|
||||
get y(): number { return this._y; }
|
||||
|
||||
constructor(
|
||||
options: IHoverOptions,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
@IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._linkHandler = options.linkHandler || this._openerService.open;
|
||||
|
||||
this._target = 'targetElements' in options.target ? options.target : new ElementHoverTarget(options.target);
|
||||
|
||||
this._hover = this._register(new BaseHoverWidget());
|
||||
this._hover.containerDomNode.classList.add('workbench-hover', 'fadeIn');
|
||||
if (options.additionalClasses) {
|
||||
this._hover.containerDomNode.classList.add(...options.additionalClasses);
|
||||
}
|
||||
|
||||
this._anchor = options.anchorPosition ?? AnchorPosition.ABOVE;
|
||||
|
||||
// Don't allow mousedown out of the widget, otherwise preventDefault will call and text will
|
||||
// not be selected.
|
||||
this.onmousedown(this._hover.containerDomNode, e => e.stopPropagation());
|
||||
|
||||
// Hide hover on escape
|
||||
this.onkeydown(this._hover.containerDomNode, e => {
|
||||
if (e.equals(KeyCode.Escape)) {
|
||||
this.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
const rowElement = $('div.hover-row.markdown-hover');
|
||||
const contentsElement = $('div.hover-contents');
|
||||
const markdown = typeof options.text === 'string' ? new MarkdownString().appendText(options.text, MarkdownStringTextNewlineStyle.Break) : options.text;
|
||||
|
||||
const mdRenderer = this._instantiationService.createInstance(
|
||||
MarkdownRenderer,
|
||||
{ codeBlockFontFamily: this._configurationService.getValue<IEditorOptions>('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily }
|
||||
);
|
||||
|
||||
const { element } = mdRenderer.render(markdown, {
|
||||
actionHandler: {
|
||||
callback: (content) => this._linkHandler(content),
|
||||
disposeables: this._messageListeners
|
||||
},
|
||||
codeBlockRenderCallback: () => {
|
||||
contentsElement.classList.add('code-hover-contents');
|
||||
// This changes the dimensions of the hover so trigger a layout
|
||||
this._onRequestLayout.fire();
|
||||
}
|
||||
});
|
||||
contentsElement.appendChild(element);
|
||||
rowElement.appendChild(contentsElement);
|
||||
this._hover.contentsDomNode.appendChild(rowElement);
|
||||
|
||||
if (options.actions && options.actions.length > 0) {
|
||||
const statusBarElement = $('div.hover-row.status-bar');
|
||||
const actionsElement = $('div.actions');
|
||||
options.actions.forEach(action => {
|
||||
const keybinding = this._keybindingService.lookupKeybinding(action.commandId);
|
||||
const keybindingLabel = keybinding ? keybinding.getLabel() : null;
|
||||
renderHoverAction(actionsElement, {
|
||||
label: action.label,
|
||||
commandId: action.commandId,
|
||||
run: e => {
|
||||
action.run(e);
|
||||
this.dispose();
|
||||
},
|
||||
iconClass: action.iconClass
|
||||
}, keybindingLabel);
|
||||
});
|
||||
statusBarElement.appendChild(actionsElement);
|
||||
this._hover.containerDomNode.appendChild(statusBarElement);
|
||||
}
|
||||
|
||||
const mouseTrackerTargets = [...this._target.targetElements];
|
||||
let hideOnHover: boolean;
|
||||
if (options.hideOnHover === undefined) {
|
||||
if (options.actions && options.actions.length > 0) {
|
||||
// If there are actions, require hover so they can be accessed
|
||||
hideOnHover = false;
|
||||
} else {
|
||||
// Defaults to true when string, false when markdown as it may contain links
|
||||
hideOnHover = typeof options.text === 'string';
|
||||
}
|
||||
} else {
|
||||
// It's set explicitly
|
||||
hideOnHover = options.hideOnHover;
|
||||
}
|
||||
if (!hideOnHover) {
|
||||
mouseTrackerTargets.push(this._hover.containerDomNode);
|
||||
}
|
||||
this._mouseTracker = new CompositeMouseTracker(mouseTrackerTargets);
|
||||
this._register(this._mouseTracker.onMouseOut(() => this.dispose()));
|
||||
this._register(this._mouseTracker);
|
||||
}
|
||||
|
||||
public render(container?: HTMLElement): void {
|
||||
if (this._hover.containerDomNode.parentElement !== container) {
|
||||
container?.appendChild(this._hover.containerDomNode);
|
||||
}
|
||||
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public layout() {
|
||||
this._hover.containerDomNode.classList.remove('right-aligned');
|
||||
this._hover.contentsDomNode.style.maxHeight = '';
|
||||
|
||||
const targetBounds = this._target.targetElements.map(e => e.getBoundingClientRect());
|
||||
|
||||
// Get horizontal alignment and position
|
||||
let targetLeft = this._target.x !== undefined ? this._target.x : Math.min(...targetBounds.map(e => e.left));
|
||||
if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) {
|
||||
this._x = document.documentElement.clientWidth - this._workbenchLayoutService.getWindowBorderWidth() - 1;
|
||||
this._hover.containerDomNode.classList.add('right-aligned');
|
||||
} else {
|
||||
this._x = targetLeft;
|
||||
}
|
||||
|
||||
// Get vertical alignment and position
|
||||
if (this._anchor === AnchorPosition.ABOVE) {
|
||||
const targetTop = Math.min(...targetBounds.map(e => e.top));
|
||||
if (targetTop - this._hover.containerDomNode.clientHeight < 0) {
|
||||
const targetBottom = Math.max(...targetBounds.map(e => e.bottom));
|
||||
this._anchor = AnchorPosition.BELOW;
|
||||
this._y = targetBottom - 2;
|
||||
} else {
|
||||
this._y = targetTop;
|
||||
}
|
||||
} else {
|
||||
const targetBottom = Math.max(...targetBounds.map(e => e.bottom));
|
||||
if (targetBottom + this._hover.containerDomNode.clientHeight > window.innerHeight) {
|
||||
console.log(targetBottom, this._hover.containerDomNode.clientHeight, window.innerHeight);
|
||||
const targetTop = Math.min(...targetBounds.map(e => e.top));
|
||||
this._anchor = AnchorPosition.ABOVE;
|
||||
this._y = targetTop;
|
||||
} else {
|
||||
this._y = targetBottom - 2;
|
||||
}
|
||||
}
|
||||
|
||||
this._hover.onContentsChanged();
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._hover.containerDomNode.focus();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (!this._isDisposed) {
|
||||
this._onDispose.fire();
|
||||
this._hover.containerDomNode.parentElement?.removeChild(this.domNode);
|
||||
this._messageListeners.dispose();
|
||||
this._target.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
this._isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
class CompositeMouseTracker extends Widget {
|
||||
private _isMouseIn: boolean = false;
|
||||
private _mouseTimeout: number | undefined;
|
||||
|
||||
private readonly _onMouseOut = new Emitter<void>();
|
||||
get onMouseOut(): Event<void> { return this._onMouseOut.event; }
|
||||
|
||||
constructor(
|
||||
private _elements: HTMLElement[]
|
||||
) {
|
||||
super();
|
||||
this._elements.forEach(n => this.onmouseover(n, () => this._onTargetMouseOver()));
|
||||
this._elements.forEach(n => this.onnonbubblingmouseout(n, () => this._onTargetMouseOut()));
|
||||
}
|
||||
|
||||
private _onTargetMouseOver(): void {
|
||||
this._isMouseIn = true;
|
||||
this._clearEvaluateMouseStateTimeout();
|
||||
}
|
||||
|
||||
private _onTargetMouseOut(): void {
|
||||
this._isMouseIn = false;
|
||||
this._evaluateMouseState();
|
||||
}
|
||||
|
||||
private _evaluateMouseState(): void {
|
||||
this._clearEvaluateMouseStateTimeout();
|
||||
// Evaluate whether the mouse is still outside asynchronously such that other mouse targets
|
||||
// have the opportunity to first their mouse in event.
|
||||
this._mouseTimeout = window.setTimeout(() => this._fireIfMouseOutside(), 0);
|
||||
}
|
||||
|
||||
private _clearEvaluateMouseStateTimeout(): void {
|
||||
if (this._mouseTimeout) {
|
||||
clearTimeout(this._mouseTimeout);
|
||||
this._mouseTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _fireIfMouseOutside(): void {
|
||||
if (!this._isMouseIn) {
|
||||
this._onMouseOut.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ElementHoverTarget implements IHoverTarget {
|
||||
readonly targetElements: readonly HTMLElement[];
|
||||
|
||||
constructor(
|
||||
private _element: HTMLElement
|
||||
) {
|
||||
this.targetElements = [this._element];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .workbench-hover {
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
animation: fadein 100ms linear;
|
||||
/* Must be higher than sash's z-index and terminal canvases */
|
||||
z-index: 40;
|
||||
overflow: hidden;
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.monaco-workbench .workbench-hover a {
|
||||
color: #3794ff;
|
||||
}
|
||||
|
||||
.monaco-workbench .workbench-hover.right-aligned {
|
||||
/* The context view service wraps strangely when it's right up against the edge without this */
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .workbench-hover.right-aligned .hover-row.status-bar .actions {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.monaco-workbench .workbench-hover.right-aligned .hover-row.status-bar .actions .action-container {
|
||||
margin-right: 0;
|
||||
margin-left: 16px;
|
||||
}
|
||||
Reference in New Issue
Block a user