mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-29 00:46:00 +01:00
Implement copy/vote action toolbars in interactive session (#176711)
This commit is contained in:
@@ -180,6 +180,8 @@ export class MenuId {
|
||||
static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar');
|
||||
static readonly InlineSuggestionToolbar = new MenuId('InlineSuggestionToolbar');
|
||||
static readonly InteractiveSessionContext = new MenuId('InteractiveSessionContext');
|
||||
static readonly InteractiveSessionCodeBlock = new MenuId('InteractiveSessionCodeblock');
|
||||
static readonly InteractiveSessionTitle = new MenuId('InteractiveSessionTitle');
|
||||
|
||||
/**
|
||||
* Create or reuse a `MenuId` with the given identifier
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { INTERACTIVE_SESSION_CATEGORY } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions';
|
||||
|
||||
export function registerInteractiveSessionCodeBlockActions() {
|
||||
registerAction2(class CopyCodeBlockAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.interactiveSession.copyCodeBlock',
|
||||
title: {
|
||||
value: localize('interactive.copyCodeBlock.label', "Copy"),
|
||||
original: 'Copy'
|
||||
},
|
||||
f1: false,
|
||||
category: INTERACTIVE_SESSION_CATEGORY,
|
||||
icon: Codicon.copy,
|
||||
menu: {
|
||||
id: MenuId.InteractiveSessionCodeBlock,
|
||||
group: 'navigation',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const code = args[0];
|
||||
if (typeof code !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
clipboardService.writeText(code);
|
||||
}
|
||||
});
|
||||
|
||||
// registerAction2(class InsertCodeBlockAction extends Action2 {
|
||||
// constructor() {
|
||||
// super({
|
||||
// id: 'workbench.action.interactiveSession.insertCodeBlock',
|
||||
// title: {
|
||||
// value: localize('interactive.insertCodeBlock.label', "Insert at Cursor"),
|
||||
// original: 'Insert at Cursor'
|
||||
// },
|
||||
// f1: false,
|
||||
// category: INTERACTIVE_SESSION_CATEGORY,
|
||||
// menu: {
|
||||
// id: MenuId.InteractiveSessionCodeBlock,
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
// const code = args[0];
|
||||
// if (typeof code !== 'string') {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // TODO
|
||||
// const clipboardService = accessor.get(IClipboardService);
|
||||
// clipboardService.writeText(code);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
+133
-87
@@ -29,6 +29,8 @@ import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/contrib/markd
|
||||
import { ViewportSemanticTokensContribution } from 'vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens';
|
||||
import { SmartSelectController } from 'vs/editor/contrib/smartSelect/browser/smartSelect';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -49,10 +51,11 @@ export type InteractiveTreeItem = IInteractiveRequestViewModel | IInteractiveRes
|
||||
|
||||
interface IInteractiveListItemTemplate {
|
||||
rowContainer: HTMLElement;
|
||||
header: HTMLElement;
|
||||
titleToolbar: MenuWorkbenchToolBar;
|
||||
avatar: HTMLElement;
|
||||
username: HTMLElement;
|
||||
value: HTMLElement;
|
||||
templateDisposables: IDisposable;
|
||||
elementDisposables: DisposableStore;
|
||||
}
|
||||
|
||||
@@ -137,15 +140,22 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IInteractiveListItemTemplate {
|
||||
const templateDisposables = new DisposableStore();
|
||||
const rowContainer = dom.append(container, $('.interactive-item-container'));
|
||||
const header = dom.append(rowContainer, $('.header'));
|
||||
const avatar = dom.append(header, $('.avatar'));
|
||||
const username = document.createElement('h3');
|
||||
header.appendChild(username);
|
||||
const user = dom.append(header, $('.user'));
|
||||
const avatar = dom.append(user, $('.avatar'));
|
||||
const username = dom.append(user, $('h3.username'));
|
||||
const value = dom.append(rowContainer, $('.value'));
|
||||
const elementDisposables = new DisposableStore();
|
||||
|
||||
const template: IInteractiveListItemTemplate = { header, avatar, username, value, rowContainer, elementDisposables };
|
||||
const titleToolbar = templateDisposables.add(this.instantiationService.createInstance(MenuWorkbenchToolBar, header, MenuId.InteractiveSessionTitle, {
|
||||
menuOptions: {
|
||||
shouldForwardArgs: true
|
||||
}
|
||||
}));
|
||||
|
||||
const template: IInteractiveListItemTemplate = { avatar, username, value, rowContainer, elementDisposables, titleToolbar, templateDisposables };
|
||||
return template;
|
||||
}
|
||||
|
||||
@@ -154,6 +164,8 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
const kind = isRequestVM(element) ? 'request' : 'response';
|
||||
this.traceLayout('renderElement', `${kind}, index=${index}`);
|
||||
|
||||
templateData.titleToolbar.context = element;
|
||||
|
||||
templateData.rowContainer.classList.toggle('interactive-request', isRequestVM(element));
|
||||
templateData.rowContainer.classList.toggle('interactive-response', isResponseVM(element));
|
||||
templateData.username.textContent = element.username;
|
||||
@@ -192,7 +204,7 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
}
|
||||
|
||||
private basicRenderElement(markdownValue: string, element: InteractiveTreeItem, index: number, templateData: IInteractiveListItemTemplate) {
|
||||
const result = this.renderMarkdown(element, index, new MarkdownString(markdownValue), templateData.elementDisposables, templateData);
|
||||
const result = this.renderMarkdown(new MarkdownString(markdownValue), templateData.elementDisposables, templateData);
|
||||
dom.clearNode(templateData.value);
|
||||
templateData.value.appendChild(result.element);
|
||||
templateData.elementDisposables.add(result);
|
||||
@@ -243,7 +255,7 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
} else if (toRender) {
|
||||
// Doing the progressive render
|
||||
const plusCursor = toRender.match(/```.*$/) ? toRender + `\n${InteractiveListItemRenderer.cursorCharacter}` : toRender + ` ${InteractiveListItemRenderer.cursorCharacter}`;
|
||||
const result = this.renderMarkdown(element, index, new MarkdownString(plusCursor), disposables, templateData, true);
|
||||
const result = this.renderMarkdown(new MarkdownString(plusCursor), disposables, templateData, true);
|
||||
dom.clearNode(templateData.value);
|
||||
templateData.value.appendChild(result.element);
|
||||
disposables.add(result);
|
||||
@@ -262,27 +274,14 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
return !!isFullyRendered;
|
||||
}
|
||||
|
||||
private renderMarkdown(element: InteractiveTreeItem, index: number, markdown: IMarkdownString, disposables: DisposableStore, templateData: IInteractiveListItemTemplate, fillInIncompleteTokens = false): IMarkdownRenderResult {
|
||||
private renderMarkdown(markdown: IMarkdownString, disposables: DisposableStore, templateData: IInteractiveListItemTemplate, fillInIncompleteTokens = false): IMarkdownRenderResult {
|
||||
const disposablesList: IDisposable[] = [];
|
||||
const result = this.renderer.render(markdown, {
|
||||
fillInIncompleteTokens,
|
||||
codeBlockRendererSync: (languageId, value) => {
|
||||
const editorInfo = this._editorPool.get();
|
||||
disposablesList.push(editorInfo);
|
||||
editorInfo.setText(value);
|
||||
editorInfo.setLanguage(languageId);
|
||||
|
||||
const layoutEditor = (context: string) => {
|
||||
editorInfo.layout(this._currentLayoutWidth);
|
||||
};
|
||||
|
||||
layoutEditor('init');
|
||||
|
||||
disposables.add(editorInfo.textModel.onDidChangeContent(() => {
|
||||
layoutEditor('textmodel');
|
||||
}));
|
||||
|
||||
return editorInfo.element;
|
||||
const ref = this.renderCodeBlock(languageId, value, disposables);
|
||||
disposablesList.push(ref);
|
||||
return ref.object.element;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -290,6 +289,25 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
return result;
|
||||
}
|
||||
|
||||
private renderCodeBlock(languageId: string, value: string, disposables: DisposableStore): IDisposableReference<IInteractiveResultEditorInfo> {
|
||||
const ref = this._editorPool.get();
|
||||
const editorInfo = ref.object;
|
||||
editorInfo.setText(value);
|
||||
editorInfo.setLanguage(languageId);
|
||||
|
||||
const layoutEditor = (context: string) => {
|
||||
editorInfo.layout(this._currentLayoutWidth);
|
||||
};
|
||||
|
||||
layoutEditor('init');
|
||||
|
||||
disposables.add(editorInfo.textModel.onDidChangeContent(() => {
|
||||
layoutEditor('textmodel');
|
||||
}));
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
private getProgressiveMarkdownToRender(element: IInteractiveResponseViewModel): string | undefined {
|
||||
const renderData = element.renderData ?? { renderedWordCount: 0, lastRenderTime: 0 };
|
||||
const rate = this.getProgressiveRenderRate(element);
|
||||
@@ -319,6 +337,7 @@ export class InteractiveListItemRenderer extends Disposable implements ITreeRend
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IInteractiveListItemTemplate): void {
|
||||
templateData.templateDisposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,12 +398,12 @@ interface IInteractiveResultEditorInfo {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
class EditorPool extends Disposable {
|
||||
private _pool: ResourcePool<IInteractiveResultEditorInfo>;
|
||||
class CodeBlockPart extends Disposable implements IInteractiveResultEditorInfo {
|
||||
private readonly editor: CodeEditorWidget;
|
||||
private readonly toolbar: MenuWorkbenchToolBar;
|
||||
|
||||
public get inUse(): ReadonlySet<IInteractiveResultEditorInfo> {
|
||||
return this._pool.inUse;
|
||||
}
|
||||
public readonly textModel: ITextModel;
|
||||
public readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private readonly options: InteractiveSessionEditorOptions,
|
||||
@@ -393,15 +412,17 @@ class EditorPool extends Disposable {
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
) {
|
||||
super();
|
||||
this._pool = this._register(new ResourcePool(() => this.editorFactory()));
|
||||
|
||||
// TODO listen to changes on options
|
||||
}
|
||||
|
||||
private editorFactory(): IInteractiveResultEditorInfo {
|
||||
const disposables = new DisposableStore();
|
||||
const wrapper = $('.interactive-result-editor-wrapper');
|
||||
const editor = disposables.add(this.instantiationService.createInstance(CodeEditorWidget, wrapper, {
|
||||
this.element = $('.interactive-result-editor-wrapper');
|
||||
|
||||
this.toolbar = disposables.add(this.instantiationService.createInstance(MenuWorkbenchToolBar, this.element, MenuId.InteractiveSessionCodeBlock, {
|
||||
menuOptions: {
|
||||
shouldForwardArgs: true
|
||||
}
|
||||
}));
|
||||
|
||||
const editorElement = dom.append(this.element, $('.interactive-result-editor'));
|
||||
this.editor = disposables.add(this.instantiationService.createInstance(CodeEditorWidget, editorElement, {
|
||||
...getSimpleEditorOptions(),
|
||||
readOnly: true,
|
||||
wordWrap: 'off',
|
||||
@@ -435,65 +456,90 @@ class EditorPool extends Disposable {
|
||||
}));
|
||||
|
||||
const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName('javascript');
|
||||
const textModel = disposables.add(this.modelService.createModel('', this.languageService.createById(vscodeLanguageId), undefined));
|
||||
editor.setModel(textModel);
|
||||
|
||||
return {
|
||||
element: wrapper,
|
||||
textModel,
|
||||
layout: (width: number) => {
|
||||
const realContentHeight = editor.getContentHeight();
|
||||
editor.layout({ width, height: realContentHeight });
|
||||
},
|
||||
setText: (newText: string) => {
|
||||
let currentText = textModel.getLinesContent().join('\n');
|
||||
if (newText === currentText) {
|
||||
return;
|
||||
}
|
||||
|
||||
let removedChars = 0;
|
||||
if (currentText.endsWith(` ${InteractiveListItemRenderer.cursorCharacter}`)) {
|
||||
removedChars = 2;
|
||||
} else if (currentText.endsWith(InteractiveListItemRenderer.cursorCharacter)) {
|
||||
removedChars = 1;
|
||||
}
|
||||
|
||||
if (removedChars > 0) {
|
||||
currentText = currentText.slice(0, currentText.length - removedChars);
|
||||
}
|
||||
|
||||
if (newText.startsWith(currentText)) {
|
||||
const text = newText.slice(currentText.length);
|
||||
const lastLine = textModel.getLineCount();
|
||||
const lastCol = textModel.getLineMaxColumn(lastLine);
|
||||
const insertAtCol = lastCol - removedChars;
|
||||
textModel.applyEdits([{ range: new Range(lastLine, insertAtCol, lastLine, lastCol), text }]);
|
||||
} else {
|
||||
// console.log(`Failed to optimize setText`);
|
||||
textModel.setValue(newText);
|
||||
}
|
||||
},
|
||||
setLanguage: (languageId: string) => {
|
||||
const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName(languageId);
|
||||
if (vscodeLanguageId) {
|
||||
textModel.setLanguage(vscodeLanguageId);
|
||||
}
|
||||
},
|
||||
dispose: () => {
|
||||
disposables.dispose();
|
||||
}
|
||||
};
|
||||
this.textModel = disposables.add(this.modelService.createModel('', this.languageService.createById(vscodeLanguageId), undefined));
|
||||
this.editor.setModel(this.textModel);
|
||||
}
|
||||
|
||||
get(): IInteractiveResultEditorInfo {
|
||||
layout(width: number): void {
|
||||
const realContentHeight = this.editor.getContentHeight();
|
||||
this.editor.layout({ width, height: realContentHeight });
|
||||
}
|
||||
|
||||
setText(newText: string): void {
|
||||
this.toolbar.context = newText;
|
||||
|
||||
let currentText = this.textModel.getLinesContent().join('\n');
|
||||
if (newText === currentText) {
|
||||
return;
|
||||
}
|
||||
|
||||
let removedChars = 0;
|
||||
if (currentText.endsWith(` ${InteractiveListItemRenderer.cursorCharacter}`)) {
|
||||
removedChars = 2;
|
||||
} else if (currentText.endsWith(InteractiveListItemRenderer.cursorCharacter)) {
|
||||
removedChars = 1;
|
||||
}
|
||||
|
||||
if (removedChars > 0) {
|
||||
currentText = currentText.slice(0, currentText.length - removedChars);
|
||||
}
|
||||
|
||||
if (newText.startsWith(currentText)) {
|
||||
const text = newText.slice(currentText.length);
|
||||
const lastLine = this.textModel.getLineCount();
|
||||
const lastCol = this.textModel.getLineMaxColumn(lastLine);
|
||||
const insertAtCol = lastCol - removedChars;
|
||||
this.textModel.applyEdits([{ range: new Range(lastLine, insertAtCol, lastLine, lastCol), text }]);
|
||||
} else {
|
||||
// console.log(`Failed to optimize setText`);
|
||||
this.textModel.setValue(newText);
|
||||
}
|
||||
}
|
||||
|
||||
setLanguage(languageId: string): void {
|
||||
const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName(languageId);
|
||||
if (vscodeLanguageId) {
|
||||
this.textModel.setLanguage(vscodeLanguageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IDisposableReference<T> extends IDisposable {
|
||||
object: T;
|
||||
}
|
||||
|
||||
class EditorPool extends Disposable {
|
||||
private _pool: ResourcePool<IInteractiveResultEditorInfo>;
|
||||
|
||||
public get inUse(): ReadonlySet<IInteractiveResultEditorInfo> {
|
||||
return this._pool.inUse;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly options: InteractiveSessionEditorOptions,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
this._pool = this._register(new ResourcePool(() => this.editorFactory()));
|
||||
|
||||
// TODO listen to changes on options
|
||||
}
|
||||
|
||||
private editorFactory(): IInteractiveResultEditorInfo {
|
||||
return this.instantiationService.createInstance(CodeBlockPart, this.options);
|
||||
}
|
||||
|
||||
get(): IDisposableReference<IInteractiveResultEditorInfo> {
|
||||
const object = this._pool.get();
|
||||
return {
|
||||
...object,
|
||||
object,
|
||||
dispose: () => this._pool.release(object)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO does something in lifecycle.ts cover this?
|
||||
|
||||
class ResourcePool<T extends IDisposable> extends Disposable {
|
||||
private readonly pool: T[] = [];
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { INTERACTIVE_SESSION_CATEGORY } from 'vs/workbench/contrib/interactiveSession/browser/actions/interactiveSessionActions';
|
||||
import { isRequestVM, isResponseVM } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionViewModel';
|
||||
|
||||
export function registerInteractiveSessionTitleActions() {
|
||||
registerAction2(class VoteUpAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.interactiveSession.voteUp',
|
||||
title: {
|
||||
value: localize('interactive.voteUp.label', "Vote Up"),
|
||||
original: 'Vote Up'
|
||||
},
|
||||
f1: false,
|
||||
category: INTERACTIVE_SESSION_CATEGORY,
|
||||
icon: Codicon.thumbsup,
|
||||
menu: {
|
||||
id: MenuId.InteractiveSessionTitle,
|
||||
group: 'navigation',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const item = args[0];
|
||||
if (!isRequestVM(item) && !isResponseVM(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO call provider method
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class VoteDownAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.interactiveSession.voteDown',
|
||||
title: {
|
||||
value: localize('interactive.voteDown.label', "Vote Down"),
|
||||
original: 'Vote Down'
|
||||
},
|
||||
f1: false,
|
||||
category: INTERACTIVE_SESSION_CATEGORY,
|
||||
icon: Codicon.thumbsdown,
|
||||
menu: {
|
||||
id: MenuId.InteractiveSessionTitle,
|
||||
group: 'navigation',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const item = args[0];
|
||||
if (!isRequestVM(item) && !isResponseVM(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO call provider method
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -24,12 +24,17 @@
|
||||
|
||||
.interactive-list .interactive-item-container .header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.interactive-list .interactive-item-container .header .user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.interactive-list .interactive-item-container .header h3 {
|
||||
.interactive-list .interactive-item-container .header .username {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
@@ -55,6 +60,14 @@
|
||||
color: var(--vscode-badge-foreground);
|
||||
}
|
||||
|
||||
.interactive-list .interactive-item-container .header .monaco-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.interactive-list .interactive-item-container.interactive-response:hover .header .monaco-toolbar {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.interactive-list .interactive-item-container .value {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@@ -139,6 +152,25 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.interactive-session .interactive-result-editor-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.interactive-session .interactive-result-editor-wrapper .monaco-toolbar {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -13px;
|
||||
right: 10px;
|
||||
height: 26px;
|
||||
background-color: var(--vscode-interactive-result-editor-background-color);
|
||||
border: 1px solid var(--vscode-interactive-responseBorder);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.interactive-session .interactive-result-editor-wrapper:hover .monaco-toolbar {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.interactive-session .interactive-result-editor-wrapper .monaco-editor,
|
||||
.interactive-session .interactive-result-editor-wrapper .monaco-editor .overflow-guard {
|
||||
border-radius: 4px;
|
||||
|
||||
Reference in New Issue
Block a user