From 4d4466891769b8040fe1ca4ec6a06e36fe2285be Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 16 Jan 2025 11:51:56 +0100 Subject: [PATCH] auto accept changes (#238043) * add setting to auto apply AI changes * fix rounding error * tweak animation --- .../contrib/chat/browser/chat.contribution.ts | 6 ++ .../chatEditingModifiedFileEntry.ts | 57 +++++++++++- .../contrib/chat/browser/chatEditorActions.ts | 54 ++++++++++- .../chat/browser/chatEditorController.ts | 89 +++++++++++-------- .../contrib/chat/browser/chatEditorOverlay.ts | 36 ++++---- .../chat/browser/media/chatEditorOverlay.css | 19 ++++ .../contrib/chat/common/chatEditingService.ts | 4 + .../chatEdit/notebookChatActionsOverlay.ts | 3 +- 8 files changed, 209 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 88fb0c5ba8e..db49afe10e6 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -128,6 +128,12 @@ configurationRegistry.registerConfiguration({ markdownDescription: nls.localize('chat.editing.alwaysSaveWithGeneratedChanges', "Whether files that have changes made by chat can be saved without confirmation."), default: false, }, + 'chat.editing.automaticallyAcceptChanges': { + type: 'boolean', + scope: ConfigurationScope.APPLICATION, + markdownDescription: nls.localize('chat.editing.automaticallyAcceptChanges', "Whether changes made by chat are automatically accepted without prior review."), + default: false, + }, 'chat.editing.confirmEditRequestRemoval': { type: 'boolean', scope: ConfigurationScope.APPLICATION, diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index 604bf523b60..200f64bce6d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -7,7 +7,7 @@ import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { Emitter } from '../../../../../base/common/event.js'; import { Disposable, IReference, toDisposable } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; -import { autorun, IObservable, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js'; +import { autorun, derived, IObservable, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js'; import { themeColorFromId } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/core/editOperation.js'; @@ -91,6 +91,12 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie return this._maxLineNumberObs; } + private readonly _reviewModeTempObs = observableValue(this, undefined); + readonly reviewMode: IObservable; + + private readonly _autoAcceptCountdown = observableValue(this, undefined); + readonly autoAcceptCountdown: IObservable = this._autoAcceptCountdown; + private _isFirstEditAfterStartOrSnapshot: boolean = true; private _edit: OffsetEdit = OffsetEdit.empty; private _isEditFromUs: boolean = false; @@ -199,6 +205,14 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie this._diffTrimWhitespace.read(r); this._updateDiffInfoSeq(); })); + + // review mode depends on setting and temporary override + const autoAccept = observableConfigValue('chat.editing.automaticallyAcceptChanges', false, configService); + this.reviewMode = derived(r => { + const configuredValue = !autoAccept.read(r); + const tempValue = this._reviewModeTempObs.read(r); + return tempValue || configuredValue; + }); } override dispose(): void { @@ -212,6 +226,22 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie return this; } + enableReviewModeUntilSettled(): void { + + this._reviewModeTempObs.set(true, undefined); + + const cleanup = autorun(r => { + // reset config when settled + const resetConfig = this.state.read(r) !== WorkingSetEntryState.Modified; + if (resetConfig) { + this._store.delete(cleanup); + this._reviewModeTempObs.set(undefined, undefined); + } + }); + + this._store.add(cleanup); + } + private _clearCurrentEditLineDecoration() { this._editDecorations = this.doc.deltaDecorations(this._editDecorations, []); } @@ -258,6 +288,31 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie this._isCurrentlyBeingModifiedObs.set(false, tx); this._rewriteRatioObs.set(0, tx); this._clearCurrentEditLineDecoration(); + + // AUTO accept mode + if (!this.reviewMode.get()) { + + const future = Date.now() + 10000; // 10secs + const update = () => { + + const reviewMode = this.reviewMode.get(); + if (reviewMode) { + // switched back to review mode + this._autoAcceptCountdown.set(undefined, undefined); + return; + } + + const remain = Math.round((future - Date.now()) / 1000); + if (remain <= 0) { + this.accept(undefined); + this._autoAcceptCountdown.set(undefined, undefined); + } else { + this._autoAcceptCountdown.set(remain, undefined); + setTimeout(update, 1000); + } + }; + update(); + } } private _mirrorEdits(event: IModelContentChangedEvent) { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts index ebd21d41a0b..0c6b0f4dee7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; -import { localize2 } from '../../../../nls.js'; +import { localize, localize2 } from '../../../../nls.js'; import { EditorAction2, ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { Codicon } from '../../../../base/common/codicons.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { CHAT_CATEGORY } from './actions/chatActions.js'; -import { ChatEditorController, ctxHasEditorModification, ctxHasRequestInProgress } from './chatEditorController.js'; +import { ChatEditorController, ctxHasEditorModification, ctxReviewModeEnabled } from './chatEditorController.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { ACTIVE_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; @@ -45,6 +45,7 @@ abstract class NavigateAction extends Action2 { id: MenuId.ChatEditingEditorContent, group: 'navigate', order: !next ? 2 : 3, + when: ctxReviewModeEnabled } }); } @@ -130,7 +131,7 @@ abstract class AcceptDiscardAction extends Action2 { ? localize2('accept2', 'Accept') : localize2('discard2', 'Discard'), category: CHAT_CATEGORY, - precondition: ContextKeyExpr.and(ctxHasRequestInProgress.negate(), hasUndecidedChatEditingResourceContextKey), + precondition: ContextKeyExpr.and(hasUndecidedChatEditingResourceContextKey), icon: accept ? Codicon.check : Codicon.discard, @@ -146,6 +147,7 @@ abstract class AcceptDiscardAction extends Action2 { id: MenuId.ChatEditingEditorContent, group: 'a_resolve', order: accept ? 0 : 1, + when: !accept ? ctxReviewModeEnabled : undefined } }); } @@ -272,6 +274,7 @@ class OpenDiffAction extends EditorAction2 { id: MenuId.ChatEditingEditorContent, group: 'a_resolve', order: 2, + when: ctxReviewModeEnabled }] }); } @@ -281,12 +284,55 @@ class OpenDiffAction extends EditorAction2 { } } +export class ReviewChangesAction extends EditorAction2 { + + constructor() { + super({ + id: 'chatEditor.action.reviewChanges', + title: localize2('review', "Review"), + menu: [{ + id: MenuId.ChatEditingEditorContent, + group: 'a_resolve', + order: 3, + when: ctxReviewModeEnabled.negate(), + }] + }); + } + + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor) { + const chatEditingService = accessor.get(IChatEditingService); + + if (!editor.hasModel()) { + return; + } + + const session = chatEditingService.editingSessionsObs.get().find(session => session.getEntry(editor.getModel().uri)); + const entry = session?.getEntry(editor.getModel().uri); + entry?.enableReviewModeUntilSettled(); + } +} + export function registerChatEditorActions() { registerAction2(class NextAction extends NavigateAction { constructor() { super(true); } }); registerAction2(class PrevAction extends NavigateAction { constructor() { super(false); } }); + registerAction2(ReviewChangesAction); registerAction2(AcceptAction); registerAction2(AcceptHunkAction); registerAction2(RejectAction); registerAction2(RejectHunkAction); registerAction2(OpenDiffAction); + } + +export const navigationBearingFakeActionId = 'chatEditor.navigation.bearings'; + +MenuRegistry.appendMenuItem(MenuId.ChatEditingEditorContent, { + command: { + id: navigationBearingFakeActionId, + title: localize('label', "Navigation Status"), + precondition: ContextKeyExpr.false(), + }, + group: 'navigate', + order: -1, + when: ctxReviewModeEnabled, +}); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts index f6f4e917c80..48b5fe9ff4d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts @@ -39,6 +39,7 @@ import { ChatEditorOverlayWidget } from './chatEditorOverlay.js'; export const ctxHasEditorModification = new RawContextKey('chat.hasEditorModifications', undefined, localize('chat.hasEditorModifications', "The current editor contains chat modifications")); export const ctxHasRequestInProgress = new RawContextKey('chat.ctxHasRequestInProgress', false, localize('chat.ctxHasRequestInProgress', "The current editor shows a file from an edit session which is still in progress")); +export const ctxReviewModeEnabled = new RawContextKey('chat.ctxReviewModeEnabled', true, localize('chat.ctxReviewModeEnabled', "Review mode for chat changes is enabled")); export class ChatEditorController extends Disposable implements IEditorContribution { @@ -57,6 +58,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut private readonly _ctxHasEditorModification: IContextKey; private readonly _ctxRequestInProgress: IContextKey; + private readonly _ctxReviewModelEnabled: IContextKey; static get(editor: ICodeEditor): ChatEditorController | null { const controller = editor.getContribution(ChatEditorController.ID); @@ -84,13 +86,13 @@ export class ChatEditorController extends Disposable implements IEditorContribut this._overlayWidget = _instantiationService.createInstance(ChatEditorOverlayWidget, _editor); this._ctxHasEditorModification = ctxHasEditorModification.bindTo(contextKeyService); this._ctxRequestInProgress = ctxHasRequestInProgress.bindTo(contextKeyService); + this._ctxReviewModelEnabled = ctxReviewModeEnabled.bindTo(contextKeyService); const editorObs = observableCodeEditor(this._editor); const fontInfoObs = editorObs.getOption(EditorOption.fontInfo); const lineHeightObs = editorObs.getOption(EditorOption.lineHeight); const modelObs = editorObs.model; - this._store.add(autorun(r => { let isStreamingEdits = false; for (const session of _chatEditingService.editingSessionsObs.read(r)) { @@ -139,11 +141,13 @@ export class ChatEditorController extends Disposable implements IEditorContribut const { session, entries, idx, entry } = currentEditorEntry; + this._ctxReviewModelEnabled.set(entry.reviewMode.read(r)); + // context this._currentEntryIndex.set(idx, undefined); // overlay widget - if (entry.state.read(r) === WorkingSetEntryState.Accepted || entry.state.read(r) === WorkingSetEntryState.Rejected) { + if (entry.state.read(r) !== WorkingSetEntryState.Modified) { this._overlayWidget.hide(); } else { this._overlayWidget.show(session, entry, entries[(idx + 1) % entries.length]); @@ -172,9 +176,11 @@ export class ChatEditorController extends Disposable implements IEditorContribut // Add line decorations (just markers, no UI) for diff navigation this._updateDiffLineDecorations(diff); + const reviewMode = entry.reviewMode.read(r); + // Add diff decoration to the UI (unless in diff editor) if (!this._editor.getOption(EditorOption.inDiffEditor)) { - this._updateDiffRendering(entry, diff); + this._updateDiffRendering(entry, diff, reviewMode); } else { this._clearDiffRendering(); } @@ -246,6 +252,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut this._currentChangeIndex.set(undefined, undefined); this._currentEntryIndex.set(undefined, undefined); this._ctxHasEditorModification.reset(); + this._ctxReviewModelEnabled.reset(); } private _clearDiffRendering() { @@ -260,7 +267,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut this._scrollLock = false; } - private _updateDiffRendering(entry: IModifiedFileEntry, diff: IDocumentDiff): void { + private _updateDiffRendering(entry: IModifiedFileEntry, diff: IDocumentDiff, reviewMode: boolean): void { const originalModel = entry.originalModel; @@ -309,18 +316,21 @@ export class ChatEditorController extends Disposable implements IEditorContribut mightContainRTL, ); const decorations: InlineDecoration[] = []; - for (const i of diffEntry.innerChanges || []) { - decorations.push(new InlineDecoration( - i.originalRange.delta(-(diffEntry.original.startLineNumber - 1)), - diffDeleteDecoration.className!, - InlineDecorationType.Regular - )); - // If the original range is empty, the start line number is 1 and the new range spans the entire file, don't draw an Added decoration - if (!(i.originalRange.isEmpty() && i.originalRange.startLineNumber === 1 && i.modifiedRange.endLineNumber === editorLineCount) && !i.modifiedRange.isEmpty()) { - modifiedVisualDecorations.push({ - range: i.modifiedRange, options: chatDiffAddDecoration - }); + if (reviewMode) { + for (const i of diffEntry.innerChanges || []) { + decorations.push(new InlineDecoration( + i.originalRange.delta(-(diffEntry.original.startLineNumber - 1)), + diffDeleteDecoration.className!, + InlineDecorationType.Regular + )); + + // If the original range is empty, the start line number is 1 and the new range spans the entire file, don't draw an Added decoration + if (!(i.originalRange.isEmpty() && i.originalRange.startLineNumber === 1 && i.modifiedRange.endLineNumber === editorLineCount) && !i.modifiedRange.isEmpty()) { + modifiedVisualDecorations.push({ + range: i.modifiedRange, options: chatDiffAddDecoration + }); + } } } @@ -354,33 +364,38 @@ export class ChatEditorController extends Disposable implements IEditorContribut options: modifiedDecoration }); } - const domNode = document.createElement('div'); - domNode.className = 'chat-editing-original-zone view-lines line-delete monaco-mouse-cursor-text'; - const result = renderLines(source, renderOptions, decorations, domNode); - if (!isCreatedContent) { - const viewZoneData: IViewZone = { - afterLineNumber: diffEntry.modified.startLineNumber - 1, - heightInLines: result.heightInLines, - domNode, - ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42 - }; + if (reviewMode) { + const domNode = document.createElement('div'); + domNode.className = 'chat-editing-original-zone view-lines line-delete monaco-mouse-cursor-text'; + const result = renderLines(source, renderOptions, decorations, domNode); - this._viewZones.push(viewZoneChangeAccessor.addZone(viewZoneData)); - } + if (!isCreatedContent) { - // Add content widget for each diff change - const widget = this._instantiationService.createInstance(DiffHunkWidget, entry, diffEntry, this._editor.getModel()!.getVersionId(), this._editor, isCreatedContent ? 0 : result.heightInLines); - widget.layout(diffEntry.modified.startLineNumber); + const viewZoneData: IViewZone = { + afterLineNumber: diffEntry.modified.startLineNumber - 1, + heightInLines: result.heightInLines, + domNode, + ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42 + }; - this._diffHunkWidgets.push(widget); - diffHunkDecorations.push({ - range: diffEntry.modified.toInclusiveRange() ?? new Range(diffEntry.modified.startLineNumber, 1, diffEntry.modified.startLineNumber, Number.MAX_SAFE_INTEGER), - options: { - description: 'diff-hunk-widget', - stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + this._viewZones.push(viewZoneChangeAccessor.addZone(viewZoneData)); } - }); + + + // Add content widget for each diff change + const widget = this._instantiationService.createInstance(DiffHunkWidget, entry, diffEntry, this._editor.getModel()!.getVersionId(), this._editor, isCreatedContent ? 0 : result.heightInLines); + widget.layout(diffEntry.modified.startLineNumber); + + this._diffHunkWidgets.push(widget); + diffHunkDecorations.push({ + range: diffEntry.modified.toInclusiveRange() ?? new Range(diffEntry.modified.startLineNumber, 1, diffEntry.modified.startLineNumber, Number.MAX_SAFE_INTEGER), + options: { + description: 'diff-hunk-widget', + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + } + }); + } } this._diffVisualDecorations.set(modifiedVisualDecorations); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts index 304a02f01cb..f24e81fd641 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts @@ -9,7 +9,7 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit import { HiddenItemStrategy, MenuWorkbenchToolBar, WorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IChatEditingSession, IModifiedFileEntry } from '../common/chatEditingService.js'; -import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { ACTIVE_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; import { Range } from '../../../../editor/common/core/range.js'; @@ -20,8 +20,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { assertType } from '../../../../base/common/types.js'; import { localize } from '../../../../nls.js'; -import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -import { AcceptAction, RejectAction } from './chatEditorActions.js'; +import { AcceptAction, navigationBearingFakeActionId, RejectAction } from './chatEditorActions.js'; import { ChatEditorController } from './chatEditorController.js'; import './media/chatEditorOverlay.css'; import { findDiffEditorContainingCodeEditor } from '../../../../editor/browser/widget/diffEditor/commands.js'; @@ -123,6 +122,25 @@ export class ChatEditorOverlayWidget implements IOverlayWidget { constructor() { super(undefined, action, { ...options, icon: false, label: true, keybindingNotRenderedWithLabel: true }); } + + override render(container: HTMLElement): void { + super.render(container); + + if (action.id === AcceptAction.ID) { + assertType(this.label); + assertType(this.element); + + this._store.add(autorun(r => { + const value = that._entry.read(r)?.entry.autoAcceptCountdown.read(r); + this.label!.innerText = value === undefined + ? this.action.label + : localize('pattern', "{0} ({1})", this.action.label, value); + + this.element?.classList.toggle('auto', !!value); + })); + } + } + override set actionRunner(actionRunner: IActionRunner) { super.actionRunner = actionRunner; @@ -258,15 +276,3 @@ export class ChatEditorOverlayWidget implements IOverlayWidget { } } } - -export const navigationBearingFakeActionId = 'chatEditor.navigation.bearings'; - -MenuRegistry.appendMenuItem(MenuId.ChatEditingEditorContent, { - command: { - id: navigationBearingFakeActionId, - title: localize('label', "Navigation Status"), - precondition: ContextKeyExpr.false(), - }, - group: 'navigate', - order: -1 -}); diff --git a/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css b/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css index 153da41fe6e..dabc35cdafa 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css @@ -89,3 +89,22 @@ color: var(--vscode-button-foreground); opacity: 1; } + +.chat-editor-overlay-widget .action-item.auto > .action-label { + animation: textShift 5s linear infinite alternate; + background: linear-gradient(90deg, + var(--vscode-button-foreground) 0%, + var(--vscode-button-foreground) 45%, + var(--vscode-editorGutter-addedBackground) 50%, + var(--vscode-button-foreground) 55%, + var(--vscode-button-foreground) 100%); + background-size: 200% 100%; + color: transparent; + -webkit-background-clip: text; + background-clip: text; +} + +@keyframes textShift { + 0% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index d4db6b9695e..560bb470981 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -138,6 +138,10 @@ export interface IModifiedFileEntry { readonly lastModifyingRequestId: string; accept(transaction: ITransaction | undefined): Promise; reject(transaction: ITransaction | undefined): Promise; + + reviewMode: IObservable; + autoAcceptCountdown: IObservable; + enableReviewModeUntilSettled(): void; } export interface IChatEditingSessionStream { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts index 7ac61fbcb30..f58810283e3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts @@ -18,8 +18,7 @@ import { autorun, autorunWithStore, IObservable, ISettableObservable, observable import { isEqual } from '../../../../../../base/common/resources.js'; import { CellDiffInfo } from '../../diff/notebookDiffViewModel.js'; import { INotebookDeletedCellDecorator } from './notebookCellDecorators.js'; -import { AcceptAction, RejectAction } from '../../../../chat/browser/chatEditorActions.js'; -import { navigationBearingFakeActionId } from '../../../../chat/browser/chatEditorOverlay.js'; +import { AcceptAction, navigationBearingFakeActionId, RejectAction } from '../../../../chat/browser/chatEditorActions.js'; export class NotebookChatActionsOverlayController extends Disposable { constructor(