diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 63ade6f2143..04be9018f8b 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -1055,6 +1055,9 @@ class StandaloneAudioService implements IAudioCueService { async playSound(cue: Sound, allowManyInParallel?: boolean | undefined): Promise { } + playAudioCueLoop(cue: AudioCue): IDisposable { + return toDisposable(() => { }); + } } export interface IEditorOverrideServices { diff --git a/src/vs/platform/audioCues/browser/audioCueService.ts b/src/vs/platform/audioCues/browser/audioCueService.ts index 90a707db13a..e9486c80517 100644 --- a/src/vs/platform/audioCues/browser/audioCueService.ts +++ b/src/vs/platform/audioCues/browser/audioCueService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { FileAccess } from 'vs/base/common/network'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -22,6 +22,7 @@ export interface IAudioCueService { onEnabledChanged(cue: AudioCue): Event; playSound(cue: Sound, allowManyInParallel?: boolean): Promise; + playAudioCueLoop(cue: AudioCue): IDisposable; } export class AudioCueService extends Disposable implements IAudioCueService { @@ -87,6 +88,23 @@ export class AudioCueService extends Disposable implements IAudioCueService { } } + public playAudioCueLoop(cue: AudioCue): IDisposable { + let playing = true; + const playSound = () => { + if (playing) { + this.playSound(cue.sound, true).finally(() => { + if (playing) { + playSound(); + } + }); + } + }; + playSound(); + return toDisposable(() => { + playing = false; + }); + } + private readonly obsoleteAudioCuesEnabled = observableFromEvent( Event.filter(this.configurationService.onDidChangeConfiguration, (e) => e.affectsConfiguration('audioCues.enabled') diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 0956e550ac8..73f36ae572f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -16,6 +16,7 @@ import 'vs/css!./media/chat'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -115,6 +116,7 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatService private readonly chatService: IChatService, @IChatWidgetService chatWidgetService: IChatWidgetService, @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IAudioCueService private readonly audioCueService: IAudioCueService ) { super(); CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true); @@ -390,8 +392,10 @@ export class ChatWidget extends Disposable implements IChatWidget { } const input = query ?? editorValue; + const cue = this.audioCueService.playAudioCueLoop(AudioCue.break); const result = await this.chatService.sendRequest(this.viewModel.sessionId, input); if (result) { + cue.dispose(); this.inputPart.acceptInput(query); result.responseCompletePromise.then(() => { const responses = this.viewModel?.getItems().filter(isResponseVM);