mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 02:26:04 +00:00
On cell chat
This commit is contained in:
parent
9c7cdf4589
commit
ecd4f3806f
|
@ -65,7 +65,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
|||
|
||||
const defaultAriaLabel = localize('aria-label', "Inline Chat Input");
|
||||
|
||||
const _inputEditorOptions: IEditorConstructionOptions = {
|
||||
export const _inputEditorOptions: IEditorConstructionOptions = {
|
||||
padding: { top: 2, bottom: 2 },
|
||||
overviewRulerLanes: 0,
|
||||
glyphMargin: false,
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container {
|
||||
padding: 8px 12px 0px 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
outline: 1px solid var(--vscode-inlineChatInput-border);
|
||||
outline-offset: -1px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content.synthetic-focus {
|
||||
outline: 1px solid var(--vscode-inlineChatInput-focusBorder);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content .input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 2px 2px 2px 6px;
|
||||
background-color: var(--vscode-inlineChatInput-background);
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content .input .monaco-editor-background {
|
||||
background-color: var(--vscode-inlineChatInput-background);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content .input .editor-placeholder {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
color: var(--vscode-inlineChatInput-placeholderForeground);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content .input .editor-placeholder.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .content .input .editor-container {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .toolbar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
padding-right: 4px;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
background: var(--vscode-inlineChatInput-background);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .body .toolbar .actions-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* progress */
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .progress {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .progress .monaco-progress-container {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* status */
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .status {
|
||||
height: 22px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-chat-part .cell-chat-container .status span {
|
||||
overflow: hidden;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 11px;
|
||||
align-self: baseline;
|
||||
display: flex;
|
||||
}
|
|
@ -171,6 +171,7 @@ export enum CellLayoutState {
|
|||
|
||||
export interface CodeCellLayoutInfo {
|
||||
readonly fontInfo: FontInfo | null;
|
||||
readonly chatHeight: number;
|
||||
readonly editorHeight: number;
|
||||
readonly editorWidth: number;
|
||||
readonly estimatedHasHorizontalScrolling: boolean;
|
||||
|
@ -189,6 +190,7 @@ export interface CodeCellLayoutInfo {
|
|||
|
||||
export interface CodeCellLayoutChangeEvent {
|
||||
readonly source?: string;
|
||||
readonly chatHeight?: boolean;
|
||||
readonly editorHeight?: boolean;
|
||||
readonly commentHeight?: boolean;
|
||||
readonly outputHeight?: boolean;
|
||||
|
@ -200,6 +202,7 @@ export interface CodeCellLayoutChangeEvent {
|
|||
|
||||
export interface MarkupCellLayoutInfo {
|
||||
readonly fontInfo: FontInfo | null;
|
||||
readonly chatHeight: number;
|
||||
readonly editorWidth: number;
|
||||
readonly editorHeight: number;
|
||||
readonly statusBarHeight: number;
|
||||
|
@ -249,6 +252,7 @@ export interface ICellViewModel extends IGenericCellViewModel {
|
|||
readonly mime: string;
|
||||
cellKind: CellKind;
|
||||
lineNumbers: 'on' | 'off' | 'inherit';
|
||||
chatHeight: number;
|
||||
focusMode: CellFocusMode;
|
||||
outputIsHovered: boolean;
|
||||
getText(): string;
|
||||
|
@ -798,7 +802,8 @@ export enum CellEditState {
|
|||
export enum CellFocusMode {
|
||||
Container,
|
||||
Editor,
|
||||
Output
|
||||
Output,
|
||||
ChatInput
|
||||
}
|
||||
|
||||
export enum CursorAtBoundary {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/notebook';
|
||||
import 'vs/css!./media/notebookCellChat';
|
||||
import 'vs/css!./media/notebookCellEditorHint';
|
||||
import 'vs/css!./media/notebookCellInsertToolbar';
|
||||
import 'vs/css!./media/notebookCellStatusBar';
|
||||
|
|
|
@ -0,0 +1,605 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $, Dimension, addDisposableListener, append, getTotalWidth, h } from 'vs/base/browser/dom';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { MarshalledId } from 'vs/base/common/marshallingIds';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { TextEdit } from 'vs/editor/common/languages';
|
||||
import { ICursorStateComputer, ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
|
||||
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { AsyncProgress } from 'vs/platform/progress/common/progress';
|
||||
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
import { IInlineChatSessionService, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
|
||||
import { ProgressingEditsOptions, asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies';
|
||||
import { _inputEditorOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget';
|
||||
import { CTX_INLINE_CHAT_HAS_PROVIDER, EditMode, IInlineChatProgressItem, IInlineChatRequest } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { CELL_TITLE_CELL_GROUP_ID, INotebookCellActionContext, NotebookCellAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
import { CellFocusMode, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart';
|
||||
import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
const CTX_NOTEBOOK_CELL_CHAT_FOCUSED = new RawContextKey<boolean>('notebookCellChatFocused', false, localize('notebookCellChatFocused', "Whether the cell chat editor is focused"));
|
||||
const CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST = new RawContextKey<boolean>('notebookChatHasActiveRequest', false, localize('notebookChatHasActiveRequest', "Whether the cell chat editor has an active request"));
|
||||
export const MENU_NOTEBOOK_CELL_CHAT_WIDGET = MenuId.for('notebookCellChatWidget');
|
||||
|
||||
export class CellChatPart extends CellContentPart {
|
||||
private readonly _elements = h(
|
||||
'div.cell-chat-container@root',
|
||||
[
|
||||
h('div.body', [
|
||||
h('div.content@content', [
|
||||
h('div.input@input', [
|
||||
h('div.editor-placeholder@placeholder'),
|
||||
h('div.editor-container@editor'),
|
||||
]),
|
||||
h('div.toolbar@editorToolbar'),
|
||||
]),
|
||||
]),
|
||||
h('div.progress@progress'),
|
||||
h('div.status@status')
|
||||
]
|
||||
);
|
||||
|
||||
private _controller: NotebookCellChatController | undefined;
|
||||
|
||||
get activeCell() {
|
||||
return this.currentCell;
|
||||
}
|
||||
|
||||
private _widget: Lazy<CellChatWidget>;
|
||||
|
||||
constructor(
|
||||
private readonly _notebookEditor: INotebookEditorDelegate,
|
||||
partContainer: HTMLElement,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._widget = new Lazy(() => this._instantiationService.createInstance(CellChatWidget, this._notebookEditor, partContainer));
|
||||
}
|
||||
|
||||
getWidget() {
|
||||
return this._widget.value;
|
||||
}
|
||||
|
||||
override didRenderCell(element: ICellViewModel): void {
|
||||
this._controller?.dispose();
|
||||
this._controller = this._instantiationService.createInstance(NotebookCellChatController, this._notebookEditor, this, element);
|
||||
|
||||
super.didRenderCell(element);
|
||||
}
|
||||
|
||||
override unrenderCell(element: ICellViewModel): void {
|
||||
this._controller?.dispose();
|
||||
this._controller = undefined;
|
||||
super.unrenderCell(element);
|
||||
}
|
||||
|
||||
override updateInternalLayoutNow(element: ICellViewModel): void {
|
||||
this._elements.root.style.width = `${element.layoutInfo.editorWidth}px`;
|
||||
this._controller?.layout();
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class CellChatWidget extends Disposable {
|
||||
private static _modelPool: number = 1;
|
||||
|
||||
private readonly _elements = h(
|
||||
'div.cell-chat-container@root',
|
||||
[
|
||||
h('div.body', [
|
||||
h('div.content@content', [
|
||||
h('div.input@input', [
|
||||
h('div.editor-placeholder@placeholder'),
|
||||
h('div.editor-container@editor'),
|
||||
]),
|
||||
h('div.toolbar@editorToolbar'),
|
||||
]),
|
||||
]),
|
||||
h('div.progress@progress'),
|
||||
h('div.status@status')
|
||||
]
|
||||
);
|
||||
private readonly _progressBar: ProgressBar;
|
||||
private readonly _toolbar: MenuWorkbenchToolBar;
|
||||
|
||||
private readonly _inputEditor: IActiveCodeEditor;
|
||||
private readonly _inputModel: ITextModel;
|
||||
private readonly _ctxInputEditorFocused: IContextKey<boolean>;
|
||||
|
||||
private _activeCell: ICellViewModel | undefined;
|
||||
|
||||
set placeholder(value: string) {
|
||||
this._elements.placeholder.innerText = value;
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
private readonly _notebookEditor: INotebookEditorDelegate,
|
||||
_partContainer: HTMLElement,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
append(_partContainer, this._elements.root);
|
||||
this._elements.input.style.height = '24px';
|
||||
|
||||
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
|
||||
isSimpleWidget: true,
|
||||
contributions: EditorExtensionsRegistry.getSomeEditorContributions([
|
||||
SnippetController2.ID,
|
||||
SuggestController.ID
|
||||
])
|
||||
};
|
||||
|
||||
this._inputEditor = <IActiveCodeEditor>this._instantiationService.createInstance(CodeEditorWidget, this._elements.editor, {
|
||||
..._inputEditorOptions,
|
||||
ariaLabel: localize('cell-chat-aria-label', "Cell Chat Input"),
|
||||
}, codeEditorWidgetOptions);
|
||||
this._register(this._inputEditor);
|
||||
const uri = URI.from({ scheme: 'vscode', authority: 'inline-chat', path: `/notebook-cell-chat/model${CellChatWidget._modelPool++}.txt` });
|
||||
this._inputModel = this._register(this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri));
|
||||
this._inputEditor.setModel(this._inputModel);
|
||||
|
||||
// placeholder
|
||||
this._elements.placeholder.style.fontSize = `${this._inputEditor.getOption(EditorOption.fontSize)}px`;
|
||||
this._elements.placeholder.style.lineHeight = `${this._inputEditor.getOption(EditorOption.lineHeight)}px`;
|
||||
this._register(addDisposableListener(this._elements.placeholder, 'click', () => this._inputEditor.focus()));
|
||||
|
||||
const togglePlaceholder = () => {
|
||||
const hasText = this._inputModel.getValueLength() > 0;
|
||||
this._elements.placeholder.classList.toggle('hidden', hasText);
|
||||
};
|
||||
this._store.add(this._inputModel.onDidChangeContent(togglePlaceholder));
|
||||
togglePlaceholder();
|
||||
|
||||
// toolbar
|
||||
this._toolbar = this._register(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.editorToolbar, MENU_NOTEBOOK_CELL_CHAT_WIDGET, {
|
||||
telemetrySource: 'interactiveEditorWidget-toolbar',
|
||||
toolbarOptions: { primaryGroup: 'main' }
|
||||
}));
|
||||
|
||||
// Create chat response div
|
||||
const copilotGeneratedCodeSpan = $('span.copilot-generated-code', {}, 'Copilot generated code may be incorrect');
|
||||
this._elements.status.appendChild(copilotGeneratedCodeSpan);
|
||||
|
||||
this._register(this._inputEditor.onDidFocusEditorWidget(() => {
|
||||
if (this._activeCell) {
|
||||
this._activeCell.focusMode = CellFocusMode.ChatInput;
|
||||
}
|
||||
}));
|
||||
|
||||
this._ctxInputEditorFocused = CTX_NOTEBOOK_CELL_CHAT_FOCUSED.bindTo(this._contextKeyService);
|
||||
this._register(this._inputEditor.onDidFocusEditorWidget(() => {
|
||||
this._ctxInputEditorFocused.set(true);
|
||||
}));
|
||||
this._register(this._inputEditor.onDidBlurEditorWidget(() => {
|
||||
this._ctxInputEditorFocused.set(false);
|
||||
}));
|
||||
|
||||
this._progressBar = new ProgressBar(this._elements.progress);
|
||||
this._register(this._progressBar);
|
||||
}
|
||||
|
||||
show(element: ICellViewModel) {
|
||||
this._elements.root.style.display = 'block';
|
||||
|
||||
this._activeCell = element;
|
||||
|
||||
this._toolbar.context = <INotebookCellActionContext>{
|
||||
ui: true,
|
||||
cell: element,
|
||||
notebookEditor: this._notebookEditor,
|
||||
$mid: MarshalledId.NotebookCellActionContext
|
||||
};
|
||||
|
||||
this.layout();
|
||||
this._inputEditor.focus();
|
||||
this._activeCell.chatHeight = 62;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._elements.root.style.display = 'none';
|
||||
if (this._activeCell) {
|
||||
this._activeCell.chatHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateProgress(show: boolean) {
|
||||
if (show) {
|
||||
this._progressBar.infinite();
|
||||
} else {
|
||||
this._progressBar.stop();
|
||||
}
|
||||
}
|
||||
|
||||
layout() {
|
||||
if (this._activeCell) {
|
||||
const innerEditorWidth = this._activeCell.layoutInfo.editorWidth - (getTotalWidth(this._elements.editorToolbar) + 8 /* L/R-padding */);
|
||||
this._inputEditor.layout(new Dimension(innerEditorWidth, this._inputEditor.getContentHeight()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotebookCellChatController extends Disposable {
|
||||
private static _cellChatControllers = new WeakMap<ICellViewModel, NotebookCellChatController>();
|
||||
|
||||
static get(cell: ICellViewModel): NotebookCellChatController | undefined {
|
||||
return NotebookCellChatController._cellChatControllers.get(cell);
|
||||
}
|
||||
|
||||
private _activeSession?: Session;
|
||||
private readonly _ctxHasActiveRequest: IContextKey<boolean>;
|
||||
private _isVisible: boolean = false;
|
||||
|
||||
constructor(
|
||||
private readonly _notebookEditor: INotebookEditorDelegate,
|
||||
private readonly _chatPart: CellChatPart,
|
||||
private readonly _cell: ICellViewModel,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService,
|
||||
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService,
|
||||
) {
|
||||
super();
|
||||
|
||||
NotebookCellChatController._cellChatControllers.set(this._cell, this);
|
||||
this._ctxHasActiveRequest = CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST.bindTo(this._contextKeyService);
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
this._ctxHasActiveRequest.reset();
|
||||
NotebookCellChatController._cellChatControllers.delete(this._cell);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
layout() {
|
||||
if (this._isVisible) {
|
||||
this._chatPart.getWidget().layout();
|
||||
}
|
||||
}
|
||||
|
||||
async startSession() {
|
||||
this._isVisible = true;
|
||||
if (this._activeSession) {
|
||||
this._inlineChatSessionService.releaseSession(this._activeSession);
|
||||
}
|
||||
|
||||
const editors = this._notebookEditor.codeEditors.find(editor => editor[0] === this._chatPart.activeCell);
|
||||
if (!editors || !editors[1].hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._chatPart.getWidget().show(this._cell);
|
||||
this._activeSession = await this._createSession(editors[1]);
|
||||
this._chatPart.getWidget().placeholder = this._activeSession?.session.placeholder ?? localize('default.placeholder', "Ask a question");
|
||||
}
|
||||
|
||||
async acceptInput() {
|
||||
assertType(this._activeSession);
|
||||
assertType(this._activeSession.lastInput);
|
||||
|
||||
const value = this._activeSession.lastInput.value;
|
||||
const editors = this._notebookEditor.codeEditors.find(editor => editor[0] === this._chatPart.activeCell);
|
||||
if (!editors || !editors[1].hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = editors[1];
|
||||
|
||||
if (!this._activeSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ctxHasActiveRequest.set(true);
|
||||
this._chatPart.getWidget().updateProgress(true);
|
||||
|
||||
const request: IInlineChatRequest = {
|
||||
requestId: generateUuid(),
|
||||
prompt: value,
|
||||
attempt: 0,
|
||||
selection: { selectionStartLineNumber: 1, selectionStartColumn: 1, positionLineNumber: 1, positionColumn: 1 },
|
||||
wholeRange: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
|
||||
live: false
|
||||
};
|
||||
|
||||
const requestCts = new CancellationTokenSource();
|
||||
const progressEdits: TextEdit[][] = [];
|
||||
const progressiveEditsQueue = new Queue();
|
||||
const progressiveEditsClock = StopWatch.create();
|
||||
const progressiveEditsAvgDuration = new MovingAverage();
|
||||
const progressiveEditsCts = new CancellationTokenSource(requestCts.token);
|
||||
const progress = new AsyncProgress<IInlineChatProgressItem>(async data => {
|
||||
console.log('received chunk', data, request);
|
||||
|
||||
if (requestCts.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.edits?.length) {
|
||||
if (!request.live) {
|
||||
throw new Error('Progress in NOT supported in non-live mode');
|
||||
}
|
||||
progressEdits.push(data.edits);
|
||||
progressiveEditsAvgDuration.update(progressiveEditsClock.elapsed());
|
||||
progressiveEditsClock.reset();
|
||||
|
||||
progressiveEditsQueue.queue(async () => {
|
||||
// making changes goes into a queue because otherwise the async-progress time will
|
||||
// influence the time it takes to receive the changes and progressive typing will
|
||||
// become infinitely fast
|
||||
await this._makeChanges(editor, data.edits!, data.editsShouldBeInstant
|
||||
? undefined
|
||||
: { duration: progressiveEditsAvgDuration.value, token: progressiveEditsCts.token }
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const task = this._activeSession.provider.provideResponse(this._activeSession.session, request, progress, requestCts.token);
|
||||
const reply = await task;
|
||||
|
||||
if (progressiveEditsQueue.size > 0) {
|
||||
// we must wait for all edits that came in via progress to complete
|
||||
await Event.toPromise(progressiveEditsQueue.onDrained);
|
||||
}
|
||||
await progress.drain();
|
||||
|
||||
if (!reply) {
|
||||
this._ctxHasActiveRequest.set(false);
|
||||
this._chatPart.getWidget().updateProgress(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const markdownContents = new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false });
|
||||
const replyResponse = new ReplyResponse(reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), progressEdits);
|
||||
for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) {
|
||||
await this._makeChanges(editor, replyResponse.allLocalEdits[i], undefined);
|
||||
}
|
||||
this._ctxHasActiveRequest.set(false);
|
||||
this._chatPart.getWidget().updateProgress(false);
|
||||
}
|
||||
|
||||
async cancelCurrentRequest() {
|
||||
if (this._activeSession) {
|
||||
this._inlineChatSessionService.releaseSession(this._activeSession);
|
||||
}
|
||||
|
||||
this._activeSession = undefined;
|
||||
}
|
||||
|
||||
async dismiss() {
|
||||
this._isVisible = false;
|
||||
this.cancelCurrentRequest();
|
||||
this._chatPart.getWidget().hide();
|
||||
}
|
||||
|
||||
private async _createSession(editor: IActiveCodeEditor) {
|
||||
const createSessionCts = new CancellationTokenSource();
|
||||
const session = await this._inlineChatSessionService.createSession(
|
||||
editor,
|
||||
{ editMode: EditMode.Live },
|
||||
createSessionCts.token
|
||||
);
|
||||
|
||||
createSessionCts.dispose();
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
private async _makeChanges(editor: IActiveCodeEditor, edits: TextEdit[], opts: ProgressingEditsOptions | undefined) {
|
||||
assertType(this._activeSession);
|
||||
|
||||
const moreMinimalEdits = await this._editorWorkerService.computeMoreMinimalEdits(this._activeSession.textModelN.uri, edits);
|
||||
// this._log('edits from PROVIDER and after making them MORE MINIMAL', this._activeSession.provider.debugName, edits, moreMinimalEdits);
|
||||
|
||||
if (moreMinimalEdits?.length === 0) {
|
||||
// nothing left to do
|
||||
return;
|
||||
}
|
||||
|
||||
const actualEdits = !opts && moreMinimalEdits ? moreMinimalEdits : edits;
|
||||
const editOperations = actualEdits.map(TextEdit.asEditOperation);
|
||||
|
||||
try {
|
||||
// this._ignoreModelContentChanged = true;
|
||||
this._activeSession.wholeRange.trackEdits(editOperations);
|
||||
if (opts) {
|
||||
await this.makeProgressiveChanges(editor, editOperations, opts);
|
||||
} else {
|
||||
await this.makeChanges(editor, editOperations);
|
||||
}
|
||||
// this._ctxDidEdit.set(this._activeSession.hasChangedText);
|
||||
} finally {
|
||||
// this._ignoreModelContentChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
async makeProgressiveChanges(editor: IActiveCodeEditor, edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise<void> {
|
||||
// push undo stop before first edit
|
||||
// if (++this._editCount === 1) {
|
||||
// this._editor.pushUndoStop();
|
||||
// }
|
||||
|
||||
const durationInSec = opts.duration / 1000;
|
||||
for (const edit of edits) {
|
||||
const wordCount = countWords(edit.text ?? '');
|
||||
const speed = wordCount / durationInSec;
|
||||
// console.log({ durationInSec, wordCount, speed: wordCount / durationInSec });
|
||||
await performAsyncTextEdit(editor.getModel(), asProgressiveEdit(edit, speed, opts.token));
|
||||
}
|
||||
}
|
||||
|
||||
async makeChanges(editor: IActiveCodeEditor, edits: ISingleEditOperation[]): Promise<void> {
|
||||
const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => {
|
||||
let last: Position | null = null;
|
||||
for (const edit of undoEdits) {
|
||||
last = !last || last.isBefore(edit.range.getEndPosition()) ? edit.range.getEndPosition() : last;
|
||||
// this._inlineDiffDecorations.collectEditOperation(edit);
|
||||
}
|
||||
return last && [Selection.fromPositions(last)];
|
||||
};
|
||||
|
||||
// push undo stop before first edit
|
||||
// if (++this._editCount === 1) {
|
||||
// this._editor.pushUndoStop();
|
||||
// }
|
||||
editor.executeEdits('inline-chat-live', edits, cursorStateComputerAndInlineDiffCollection);
|
||||
}
|
||||
}
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
id: 'notebook.cell.chat.start',
|
||||
title: {
|
||||
value: localize('notebook.cell.chat.start', "Start Chat"),
|
||||
original: 'Start Chat'
|
||||
},
|
||||
icon: Codicon.sparkle,
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
group: CELL_TITLE_CELL_GROUP_ID,
|
||||
order: 0,
|
||||
when: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_PROVIDER, EditorContextKeys.writable, ContextKeyExpr.equals(`config.${NotebookSetting.cellChat}`, true))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {
|
||||
const ctrl = NotebookCellChatController.get(context.cell);
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl.startSession();
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
id: 'notebook.cell.chat.accept',
|
||||
title: {
|
||||
value: localize('notebook.cell.chat.accept', "Make Request"),
|
||||
original: 'Make Request'
|
||||
},
|
||||
icon: Codicon.send,
|
||||
keybinding: {
|
||||
when: CTX_NOTEBOOK_CELL_CHAT_FOCUSED,
|
||||
weight: KeybindingWeight.EditorCore + 7,
|
||||
primary: KeyCode.Enter
|
||||
},
|
||||
menu: {
|
||||
id: MENU_NOTEBOOK_CELL_CHAT_WIDGET,
|
||||
group: 'main',
|
||||
order: 1,
|
||||
when: CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST.negate()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {
|
||||
const ctrl = NotebookCellChatController.get(context.cell);
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl.acceptInput();
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
id: 'notebook.cell.chat.stop',
|
||||
title: {
|
||||
value: localize('notebook.cell.chat.stop', "Stop Request"),
|
||||
original: 'Make Request'
|
||||
},
|
||||
icon: Codicon.debugStop,
|
||||
menu: {
|
||||
id: MENU_NOTEBOOK_CELL_CHAT_WIDGET,
|
||||
group: 'main',
|
||||
order: 1,
|
||||
when: CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {
|
||||
const ctrl = NotebookCellChatController.get(context.cell);
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl.cancelCurrentRequest();
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
id: 'notebook.cell.chat.close',
|
||||
title: {
|
||||
value: localize('notebook.cell.chat.close', "Close Chat"),
|
||||
original: 'Close Chat'
|
||||
},
|
||||
icon: Codicon.close,
|
||||
menu: {
|
||||
id: MENU_NOTEBOOK_CELL_CHAT_WIDGET,
|
||||
group: 'main',
|
||||
order: 2
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {
|
||||
const ctrl = NotebookCellChatController.get(context.cell);
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl.dismiss();
|
||||
}
|
||||
});
|
|
@ -136,7 +136,9 @@ export class CellEditorStatusBar extends CellContentPart {
|
|||
element.focusMode = CellFocusMode.Editor;
|
||||
} else {
|
||||
const currentMode = element.focusMode;
|
||||
if (currentMode === CellFocusMode.Output && this._notebookEditor.hasWebviewFocus()) {
|
||||
if (currentMode === CellFocusMode.ChatInput) {
|
||||
element.focusMode = CellFocusMode.ChatInput;
|
||||
} else if (currentMode === CellFocusMode.Output && this._notebookEditor.hasWebviewFocus()) {
|
||||
element.focusMode = CellFocusMode.Output;
|
||||
} else {
|
||||
element.focusMode = CellFocusMode.Container;
|
||||
|
|
|
@ -346,7 +346,7 @@ export class CodeCell extends Disposable {
|
|||
this.templateData.editor?.focus();
|
||||
}
|
||||
|
||||
this.templateData.container.classList.toggle('cell-editor-focus', this.viewCell.focusMode === CellFocusMode.Editor);
|
||||
this.templateData.container.classList.toggle('cell-editor-focus', this.viewCell.focusMode === CellFocusMode.Editor || this.viewCell.focusMode === CellFocusMode.ChatInput);
|
||||
this.templateData.container.classList.toggle('cell-output-focus', this.viewCell.focusMode === CellFocusMode.Output);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
|||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellPartsCollection } from 'vs/workbench/contrib/notebook/browser/view/cellPart';
|
||||
import { CellChatPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellChatWidget';
|
||||
import { CellComments } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellComments';
|
||||
import { CellContextKeyPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys';
|
||||
import { CellDecorations } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations';
|
||||
|
@ -141,6 +142,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
|
||||
const codeInnerContent = DOM.append(container, $('.cell.code'));
|
||||
const editorPart = DOM.append(codeInnerContent, $('.cell-editor-part'));
|
||||
const cellChatPart = DOM.append(editorPart, $('.cell-chat-part'));
|
||||
const cellInputCollapsedContainer = DOM.append(codeInnerContent, $('.input-collapse-container'));
|
||||
cellInputCollapsedContainer.style.display = 'none';
|
||||
const editorContainer = DOM.append(editorPart, $('.cell-editor-container'));
|
||||
|
@ -163,6 +165,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')));
|
||||
|
||||
const cellParts = new CellPartsCollection(DOM.getWindow(rootContainer), [
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellChatPart, this.notebookEditor, cellChatPart)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, undefined)),
|
||||
templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom)),
|
||||
templateDisposables.add(new FoldedCellHint(this.notebookEditor, DOM.append(container, $('.notebook-folded-hint')))),
|
||||
|
@ -264,6 +267,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
const executionOrderLabel = DOM.append(focusIndicatorLeft.domNode, $('div.execution-count-label'));
|
||||
executionOrderLabel.title = localize('cellExecutionOrderCountLabel', 'Execution Order');
|
||||
const editorPart = DOM.append(cellContainer, $('.cell-editor-part'));
|
||||
const cellChatPart = DOM.append(editorPart, $('.cell-chat-part'));
|
||||
const editorContainer = DOM.append(editorPart, $('.cell-editor-container'));
|
||||
const cellCommentPartContainer = DOM.append(container, $('.cell-comment-container'));
|
||||
|
||||
|
@ -308,6 +312,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
const focusIndicatorPart = templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom));
|
||||
const cellParts = new CellPartsCollection(DOM.getWindow(rootContainer), [
|
||||
focusIndicatorPart,
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellChatPart, this.notebookEditor, cellChatPart)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, editor)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellProgressBar, editorPart, cellInputCollapsedContainer)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(RunToolbar, this.notebookEditor, contextKeyService, container, runButtonContainer)),
|
||||
|
|
|
@ -64,6 +64,20 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
throw new Error('editorHeight is write-only');
|
||||
}
|
||||
|
||||
private _chatHeight = 0;
|
||||
set chatHeight(height: number) {
|
||||
if (this._chatHeight === height) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._chatHeight = height;
|
||||
this.layoutChange({ chatHeight: true }, 'CodeCellViewModel#chatHeight');
|
||||
}
|
||||
|
||||
get chatHeight() {
|
||||
return this._chatHeight;
|
||||
}
|
||||
|
||||
private _commentHeight = 0;
|
||||
|
||||
set commentHeight(height: number) {
|
||||
|
@ -163,13 +177,14 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
editorWidth: initialNotebookLayoutInfo
|
||||
? this.viewContext.notebookOptions.computeCodeCellEditorWidth(initialNotebookLayoutInfo.width)
|
||||
: 0,
|
||||
chatHeight: 0,
|
||||
statusBarHeight: 0,
|
||||
commentHeight: 0,
|
||||
outputContainerOffset: 0,
|
||||
outputTotalHeight: 0,
|
||||
outputShowMoreContainerHeight: 0,
|
||||
outputShowMoreContainerOffset: 0,
|
||||
totalHeight: this.computeTotalHeight(17, 0, 0),
|
||||
totalHeight: this.computeTotalHeight(17, 0, 0, 0),
|
||||
codeIndicatorHeight: 0,
|
||||
outputIndicatorHeight: 0,
|
||||
bottomToolbarOffset: 0,
|
||||
|
@ -215,6 +230,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
let editorHeight: number;
|
||||
let totalHeight: number;
|
||||
let hasHorizontalScrolling = false;
|
||||
const chatHeight = state.chatHeight ? this._chatHeight : this._layoutInfo.chatHeight;
|
||||
if (!state.editorHeight && this._layoutInfo.layoutState === CellLayoutState.FromCache && !state.outputHeight) {
|
||||
// No new editorHeight info - keep cached totalHeight and estimate editorHeight
|
||||
const estimate = this.estimateEditorHeight(state.font?.lineHeight ?? this._layoutInfo.fontInfo?.lineHeight);
|
||||
|
@ -225,22 +241,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
} else if (state.editorHeight || this._layoutInfo.layoutState === CellLayoutState.Measured) {
|
||||
// Editor has been measured
|
||||
editorHeight = this._editorHeight;
|
||||
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight, outputShowMoreContainerHeight);
|
||||
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight, outputShowMoreContainerHeight, chatHeight);
|
||||
newState = CellLayoutState.Measured;
|
||||
hasHorizontalScrolling = this._layoutInfo.estimatedHasHorizontalScrolling;
|
||||
} else {
|
||||
const estimate = this.estimateEditorHeight(state.font?.lineHeight ?? this._layoutInfo.fontInfo?.lineHeight);
|
||||
editorHeight = estimate.editorHeight;
|
||||
hasHorizontalScrolling = estimate.hasHorizontalScrolling;
|
||||
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight, outputShowMoreContainerHeight);
|
||||
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight, outputShowMoreContainerHeight, chatHeight);
|
||||
newState = CellLayoutState.Estimated;
|
||||
}
|
||||
|
||||
const statusBarHeight = this.viewContext.notebookOptions.computeEditorStatusbarHeight(this.internalMetadata, this.uri);
|
||||
const codeIndicatorHeight = editorHeight + statusBarHeight;
|
||||
const codeIndicatorHeight = chatHeight + editorHeight + statusBarHeight;
|
||||
const outputIndicatorHeight = outputTotalHeight + outputShowMoreContainerHeight;
|
||||
const outputContainerOffset = notebookLayoutConfiguration.editorToolbarHeight
|
||||
+ notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN
|
||||
+ chatHeight
|
||||
+ editorHeight
|
||||
+ statusBarHeight;
|
||||
const outputShowMoreContainerOffset = totalHeight
|
||||
|
@ -254,6 +271,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
|
||||
this._layoutInfo = {
|
||||
fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null,
|
||||
chatHeight,
|
||||
editorHeight,
|
||||
editorWidth,
|
||||
statusBarHeight,
|
||||
|
@ -294,6 +312,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null,
|
||||
editorHeight: this._layoutInfo.editorHeight,
|
||||
editorWidth,
|
||||
chatHeight: 0,
|
||||
statusBarHeight: 0,
|
||||
commentHeight,
|
||||
outputContainerOffset,
|
||||
|
@ -325,6 +344,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
if (totalHeight !== undefined && this._layoutInfo.layoutState !== CellLayoutState.Measured) {
|
||||
this._layoutInfo = {
|
||||
fontInfo: this._layoutInfo.fontInfo,
|
||||
chatHeight: this._layoutInfo.chatHeight,
|
||||
editorHeight: this._layoutInfo.editorHeight,
|
||||
editorWidth: this._layoutInfo.editorWidth,
|
||||
statusBarHeight: this.layoutInfo.statusBarHeight,
|
||||
|
@ -351,7 +371,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
getHeight(lineHeight: number) {
|
||||
if (this._layoutInfo.layoutState === CellLayoutState.Uninitialized) {
|
||||
const estimate = this.estimateEditorHeight(lineHeight);
|
||||
return this.computeTotalHeight(estimate.editorHeight, 0, 0);
|
||||
return this.computeTotalHeight(estimate.editorHeight, 0, 0, 0);
|
||||
} else {
|
||||
return this._layoutInfo.totalHeight;
|
||||
}
|
||||
|
@ -383,11 +403,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
};
|
||||
}
|
||||
|
||||
private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number {
|
||||
private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number, chatHeight: number): number {
|
||||
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
|
||||
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
|
||||
return layoutConfiguration.editorToolbarHeight
|
||||
+ layoutConfiguration.cellTopMargin
|
||||
+ chatHeight
|
||||
+ editorHeight
|
||||
+ this.viewContext.notebookOptions.computeEditorStatusbarHeight(this.internalMetadata, this.uri)
|
||||
+ this._commentHeight
|
||||
|
|
|
@ -45,6 +45,17 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
this._updateTotalHeight(this._computeTotalHeight());
|
||||
}
|
||||
|
||||
private _chatHeight = 0;
|
||||
|
||||
set chatHeight(newHeight: number) {
|
||||
this._chatHeight = newHeight;
|
||||
this._updateTotalHeight(this._computeTotalHeight());
|
||||
}
|
||||
|
||||
get chatHeight() {
|
||||
return this._chatHeight;
|
||||
}
|
||||
|
||||
private _editorHeight = 0;
|
||||
private _statusBarHeight = 0;
|
||||
set editorHeight(newHeight: number) {
|
||||
|
@ -108,6 +119,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
|
||||
|
||||
this._layoutInfo = {
|
||||
chatHeight: 0,
|
||||
editorHeight: 0,
|
||||
previewHeight: 0,
|
||||
fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
|
||||
|
@ -199,6 +211,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
fontInfo: state.font || this._layoutInfo.fontInfo,
|
||||
editorWidth,
|
||||
previewHeight,
|
||||
chatHeight: this._chatHeight,
|
||||
editorHeight: this._editorHeight,
|
||||
statusBarHeight: this._statusBarHeight,
|
||||
bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight, this.viewType),
|
||||
|
@ -217,6 +230,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
this._layoutInfo = {
|
||||
fontInfo: state.font || this._layoutInfo.fontInfo,
|
||||
editorWidth,
|
||||
chatHeight: this._chatHeight,
|
||||
editorHeight: this._editorHeight,
|
||||
statusBarHeight: this._statusBarHeight,
|
||||
previewHeight: this._previewHeight,
|
||||
|
@ -240,6 +254,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
previewHeight: this._layoutInfo.previewHeight,
|
||||
bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset,
|
||||
totalHeight: totalHeight,
|
||||
chatHeight: this._chatHeight,
|
||||
editorHeight: this._editorHeight,
|
||||
statusBarHeight: this._statusBarHeight,
|
||||
layoutState: CellLayoutState.FromCache,
|
||||
|
|
|
@ -1045,7 +1045,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
|
|||
}
|
||||
}
|
||||
|
||||
export type CellViewModel = CodeCellViewModel | MarkupCellViewModel;
|
||||
export type CellViewModel = (CodeCellViewModel | MarkupCellViewModel) & ICellViewModel;
|
||||
|
||||
export function createCellViewModel(instantiationService: IInstantiationService, notebookViewModel: NotebookViewModel, cell: NotebookCellTextModel, viewContext: ViewContext) {
|
||||
if (cell.cellKind === CellKind.Code) {
|
||||
|
|
|
@ -966,7 +966,8 @@ export const NotebookSetting = {
|
|||
remoteSaving: 'notebook.experimental.remoteSave',
|
||||
gotoSymbolsAllSymbols: 'notebook.gotoSymbols.showAllSymbols',
|
||||
scrollToRevealCell: 'notebook.scrolling.revealNextCellOnExecute',
|
||||
anchorToFocusedCell: 'notebook.scrolling.experimental.anchorToFocusedCell'
|
||||
anchorToFocusedCell: 'notebook.scrolling.experimental.anchorToFocusedCell',
|
||||
cellChat: 'notebook.experimental.cellChat'
|
||||
} as const;
|
||||
|
||||
export const enum CellStatusbarAlignment {
|
||||
|
|
Loading…
Reference in a new issue