import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { append, addDisposableListener, clearNode, WindowIntervalTimer, getWindow, reset, $ as $$1, scheduleAtNextAnimationFrame, EventType } from '../../../../base/browser/dom.js';
import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js';
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';
import { distinct, coalesce } from '../../../../base/common/arrays.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Emitter } from '../../../../base/common/event.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { KeyCode } from '../../../../base/common/keyCodes.js';
import { Disposable, toDisposable, dispose, DisposableStore } from '../../../../base/common/lifecycle.js';
import { ResourceMap } from '../../../../base/common/map.js';
import { FileAccess } from '../../../../base/common/network.js';
import { clamp } from '../../../../base/common/numbers.js';
import '../../../../base/common/observableInternal/derived.js';
import { autorun } from '../../../../base/common/observableInternal/autorun.js';
import '../../../../base/common/observableInternal/utils.js';
import '../../../../base/common/cancellation.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
import { URI } from '../../../../base/common/uri.js';
import { localizeWithPath } from '../../../../nls.js';
import { createActionViewItem, MenuEntryActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js';
import { MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js';
import { ICommandService } from '../../../../platform/commands/common/commands.service.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { IHoverService } from '../../../../platform/hover/browser/hover.service.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { ColorScheme } from '../../../../platform/theme/common/theme.js';
import { IThemeService } from '../../../../platform/theme/common/themeService.service.js';
import { GeneratingPhrase } from './chat.js';
import { ChatAgentHover, getChatAgentHoverOptions } from './chatAgentHover.js';
import { ChatCommandButtonContentPart } from './chatContentParts/chatCommandContentPart.js';
import { ChatConfirmationContentPart } from './chatContentParts/chatConfirmationContentPart.js';
import { EditorPool, ChatMarkdownContentPart } from './chatContentParts/chatMarkdownContentPart.js';
import { ChatProgressContentPart } from './chatContentParts/chatProgressContentPart.js';
import { ContentReferencesListPool, ChatReferencesContentPart } from './chatContentParts/chatReferencesContentPart.js';
import { ChatTaskContentPart } from './chatContentParts/chatTaskContentPart.js';
import { DiffEditorPool, ChatTextEditContentPart } from './chatContentParts/chatTextEditContentPart.js';
import { TreePool, ChatTreeContentPart } from './chatContentParts/chatTreeContentPart.js';
import { ChatWarningContentPart } from './chatContentParts/chatWarningContentPart.js';
import { ChatFollowups } from './chatFollowups.js';
import { ChatMarkdownDecorationsRenderer } from './chatMarkdownDecorationsRenderer.js';
import { ChatMarkdownRenderer } from './chatMarkdownRenderer.js';
import { ChatCodeBlockContentProvider } from './codeBlockPart.js';
import { CONTEXT_RESPONSE, CONTEXT_REQUEST, CONTEXT_RESPONSE_DETECTED_AGENT_COMMAND, CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_RESPONSE_VOTE, CONTEXT_RESPONSE_FILTERED } from '../common/chatContextKeys.js';
import { chatSubcommandLeader } from '../common/chatParserTypes.js';
import { ChatAgentVoteDirection } from '../common/chatService.js';
import { isResponseVM, isRequestVM, isWelcomeVM } from '../common/chatViewModel.js';
import { getNWords } from '../common/chatWordCounter.js';
import { annotateSpecialMarkdownContent } from '../common/annotations.js';
var ChatListItemRenderer_1;
const _moduleId = "vs/workbench/contrib/chat/browser/chatListRenderer";
const $ = $$1;
let ChatListItemRenderer = class ChatListItemRenderer extends Disposable {
    static { ChatListItemRenderer_1 = this; }
    static { this.ID = 'item'; }
    constructor(editorOptions, location, rendererOptions, delegate, codeBlockModelCollection, overflowWidgetsDomNode, instantiationService, configService, logService, contextKeyService, themeService, commandService, hoverService) {
        super();
        this.location = location;
        this.rendererOptions = rendererOptions;
        this.delegate = delegate;
        this.codeBlockModelCollection = codeBlockModelCollection;
        this.instantiationService = instantiationService;
        this.logService = logService;
        this.contextKeyService = contextKeyService;
        this.themeService = themeService;
        this.commandService = commandService;
        this.hoverService = hoverService;
        this.codeBlocksByResponseId = ( (new Map()));
        this.codeBlocksByEditorUri = ( (new ResourceMap()));
        this.fileTreesByResponseId = ( (new Map()));
        this.focusedFileTreesByResponseId = ( (new Map()));
        this._onDidClickFollowup = this._register(( (new Emitter())));
        this.onDidClickFollowup = this._onDidClickFollowup.event;
        this._onDidClickRerunWithAgentOrCommandDetection = ( (new Emitter()));
        this.onDidClickRerunWithAgentOrCommandDetection = this._onDidClickRerunWithAgentOrCommandDetection.event;
        this._onDidChangeItemHeight = this._register(( (new Emitter())));
        this.onDidChangeItemHeight = this._onDidChangeItemHeight.event;
        this._currentLayoutWidth = 0;
        this._isVisible = true;
        this._onDidChangeVisibility = this._register(( (new Emitter())));
        this.renderer = this._register(this.instantiationService.createInstance(ChatMarkdownRenderer, undefined));
        this.markdownDecorationsRenderer = this.instantiationService.createInstance(ChatMarkdownDecorationsRenderer);
        this._editorPool = this._register(this.instantiationService.createInstance(EditorPool, editorOptions, delegate, overflowWidgetsDomNode));
        this._diffEditorPool = this._register(this.instantiationService.createInstance(DiffEditorPool, editorOptions, delegate, overflowWidgetsDomNode));
        this._treePool = this._register(this.instantiationService.createInstance(TreePool, this._onDidChangeVisibility.event));
        this._contentReferencesListPool = this._register(this.instantiationService.createInstance(ContentReferencesListPool, this._onDidChangeVisibility.event));
        this._register(this.instantiationService.createInstance(ChatCodeBlockContentProvider));
    }
    get templateId() {
        return ChatListItemRenderer_1.ID;
    }
    editorsInUse() {
        return this._editorPool.inUse();
    }
    traceLayout(method, message) {
        {
            this.logService.trace(`ChatListItemRenderer#${method}: ${message}`);
        }
    }
    getProgressiveRenderRate(element) {
        if (element.isComplete) {
            return 80;
        }
        if (element.contentUpdateTimings && element.contentUpdateTimings.impliedWordLoadRate) {
            const minRate = 12;
            const maxRate = 80;
            const rateBoost = 1.5;
            const rate = element.contentUpdateTimings.impliedWordLoadRate * rateBoost;
            return clamp(rate, minRate, maxRate);
        }
        return 8;
    }
    getCodeBlockInfosForResponse(response) {
        const codeBlocks = this.codeBlocksByResponseId.get(response.id);
        return codeBlocks ?? [];
    }
    getCodeBlockInfoForEditor(uri) {
        return this.codeBlocksByEditorUri.get(uri);
    }
    getFileTreeInfosForResponse(response) {
        const fileTrees = this.fileTreesByResponseId.get(response.id);
        return fileTrees ?? [];
    }
    getLastFocusedFileTreeForResponse(response) {
        const fileTrees = this.fileTreesByResponseId.get(response.id);
        const lastFocusedFileTreeIndex = this.focusedFileTreesByResponseId.get(response.id);
        if (fileTrees?.length && lastFocusedFileTreeIndex !== undefined && lastFocusedFileTreeIndex < fileTrees.length) {
            return fileTrees[lastFocusedFileTreeIndex];
        }
        return undefined;
    }
    setVisible(visible) {
        this._isVisible = visible;
        this._onDidChangeVisibility.fire(visible);
    }
    layout(width) {
        this._currentLayoutWidth = width - (this.rendererOptions.noPadding ? 0 : 40);
        for (const editor of this._editorPool.inUse()) {
            editor.layout(this._currentLayoutWidth);
        }
        for (const diffEditor of this._diffEditorPool.inUse()) {
            diffEditor.layout(this._currentLayoutWidth);
        }
    }
    renderTemplate(container) {
        const templateDisposables = ( (new DisposableStore()));
        const rowContainer = append(container, $('.interactive-item-container'));
        if (this.rendererOptions.renderStyle === 'compact') {
            rowContainer.classList.add('interactive-item-compact');
        }
        if (this.rendererOptions.noPadding) {
            rowContainer.classList.add('no-padding');
        }
        let headerParent = rowContainer;
        let valueParent = rowContainer;
        let detailContainerParent;
        let toolbarParent;
        if (this.rendererOptions.renderStyle === 'minimal') {
            rowContainer.classList.add('interactive-item-compact');
            rowContainer.classList.add('minimal');
            const lhsContainer = append(rowContainer, $('.column.left'));
            const rhsContainer = append(rowContainer, $('.column.right'));
            headerParent = lhsContainer;
            detailContainerParent = rhsContainer;
            valueParent = rhsContainer;
            toolbarParent = append(rowContainer, $('.header'));
        }
        const header = append(headerParent, $('.header'));
        const user = append(header, $('.user'));
        user.tabIndex = 0;
        user.role = 'toolbar';
        const avatarContainer = append(user, $('.avatar-container'));
        const username = append(user, $('h3.username'));
        const detailContainer = append(detailContainerParent ?? user, $('span.detail-container'));
        const detail = append(detailContainer, $('span.detail'));
        append(detailContainer, $('span.chat-animated-ellipsis'));
        const value = append(valueParent, $('.value'));
        const elementDisposables = ( (new DisposableStore()));
        const contextKeyService = templateDisposables.add(this.contextKeyService.createScoped(rowContainer));
        const scopedInstantiationService = this.instantiationService.createChild(( (new ServiceCollection([IContextKeyService, contextKeyService]))));
        let titleToolbar;
        if (this.rendererOptions.noHeader) {
            header.classList.add('hidden');
        }
        else {
            titleToolbar = templateDisposables.add(scopedInstantiationService.createInstance(MenuWorkbenchToolBar, toolbarParent ?? header, MenuId.ChatMessageTitle, {
                menuOptions: {
                    shouldForwardArgs: true
                },
                toolbarOptions: {
                    shouldInlineSubmenu: submenu => submenu.actions.length <= 1
                },
                actionViewItemProvider: (action, options) => {
                    if (action instanceof MenuItemAction && (action.item.id === 'workbench.action.chat.voteDown' || action.item.id === 'workbench.action.chat.voteUp')) {
                        return scopedInstantiationService.createInstance(ChatVoteButton, action, options);
                    }
                    return createActionViewItem(scopedInstantiationService, action, options);
                }
            }));
        }
        const agentHover = templateDisposables.add(this.instantiationService.createInstance(ChatAgentHover));
        const hoverContent = () => {
            if (isResponseVM(template.currentElement) && template.currentElement.agent && !template.currentElement.agent.isDefault) {
                agentHover.setAgent(template.currentElement.agent.id);
                return agentHover.domNode;
            }
            return undefined;
        };
        const hoverOptions = getChatAgentHoverOptions(() => isResponseVM(template.currentElement) ? template.currentElement.agent : undefined, this.commandService);
        templateDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), user, hoverContent, hoverOptions));
        templateDisposables.add(addDisposableListener(user, EventType.KEY_DOWN, e => {
            const ev = ( (new StandardKeyboardEvent(e)));
            if (ev.equals(KeyCode.Space) || ev.equals(KeyCode.Enter)) {
                const content = hoverContent();
                if (content) {
                    this.hoverService.showHover({ content, target: user, trapFocus: true, actions: hoverOptions.actions }, true);
                }
            }
            else if (ev.equals(KeyCode.Escape)) {
                this.hoverService.hideHover();
            }
        }));
        const template = { avatarContainer, username, detail, value, rowContainer, elementDisposables, titleToolbar, templateDisposables, contextKeyService, instantiationService: scopedInstantiationService, agentHover };
        return template;
    }
    renderElement(node, index, templateData) {
        this.renderChatTreeItem(node.element, index, templateData);
    }
    renderChatTreeItem(element, index, templateData) {
        templateData.currentElement = element;
        const kind = isRequestVM(element) ? 'request' :
            isResponseVM(element) ? 'response' :
                'welcome';
        this.traceLayout('renderElement', `${kind}, index=${index}`);
        CONTEXT_RESPONSE.bindTo(templateData.contextKeyService).set(isResponseVM(element));
        CONTEXT_REQUEST.bindTo(templateData.contextKeyService).set(isRequestVM(element));
        CONTEXT_RESPONSE_DETECTED_AGENT_COMMAND.bindTo(templateData.contextKeyService).set(isResponseVM(element) && element.agentOrSlashCommandDetected);
        if (isResponseVM(element)) {
            CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING.bindTo(templateData.contextKeyService).set(!!element.agent?.metadata.supportIssueReporting);
            CONTEXT_RESPONSE_VOTE.bindTo(templateData.contextKeyService).set(element.vote === ChatAgentVoteDirection.Up ? 'up' : element.vote === ChatAgentVoteDirection.Down ? 'down' : '');
        }
        else {
            CONTEXT_RESPONSE_VOTE.bindTo(templateData.contextKeyService).set('');
        }
        if (templateData.titleToolbar) {
            templateData.titleToolbar.context = element;
        }
        const isFiltered = !!(isResponseVM(element) && element.errorDetails?.responseIsFiltered);
        CONTEXT_RESPONSE_FILTERED.bindTo(templateData.contextKeyService).set(isFiltered);
        templateData.rowContainer.classList.toggle('interactive-request', isRequestVM(element));
        templateData.rowContainer.classList.toggle('interactive-response', isResponseVM(element));
        templateData.rowContainer.classList.toggle('interactive-welcome', isWelcomeVM(element));
        templateData.rowContainer.classList.toggle('filtered-response', isFiltered);
        templateData.rowContainer.classList.toggle('show-detail-progress', isResponseVM(element) && !element.isComplete && !element.progressMessages.length);
        templateData.username.textContent = element.username;
        if (!this.rendererOptions.noHeader) {
            this.renderAvatar(element, templateData);
        }
        clearNode(templateData.detail);
        if (isResponseVM(element)) {
            this.renderDetail(element, templateData);
        }
        if (isResponseVM(element) && index === this.delegate.getListLength() - 1 && (!element.isComplete || element.renderData) && element.response.value.length) {
            this.traceLayout('renderElement', `start progressive render ${kind}, index=${index}`);
            const timer = templateData.elementDisposables.add(new WindowIntervalTimer());
            const runProgressiveRender = (initial) => {
                try {
                    if (this.doNextProgressiveRender(element, index, templateData, !!initial)) {
                        timer.cancel();
                    }
                }
                catch (err) {
                    timer.cancel();
                    this.logService.error(err);
                }
            };
            timer.cancelAndSet(runProgressiveRender, 50, getWindow(templateData.rowContainer));
            runProgressiveRender(true);
        }
        else if (isResponseVM(element)) {
            this.basicRenderElement(element, index, templateData);
        }
        else if (isRequestVM(element)) {
            this.basicRenderElement(element, index, templateData);
        }
        else {
            this.renderWelcomeMessage(element, templateData);
        }
    }
    renderDetail(element, templateData) {
        templateData.elementDisposables.add(autorun(reader => {
            this._renderDetail(element, templateData);
        }));
    }
    _renderDetail(element, templateData) {
        clearNode(templateData.detail);
        if (element.slashCommand && element.agentOrSlashCommandDetected) {
            const usingMsg = `${chatSubcommandLeader}${element.slashCommand.name}`;
            const msg = ( localizeWithPath(_moduleId, 0, "used {0} [[(rerun without)]]", usingMsg));
            reset(templateData.detail, renderFormattedText(msg, {
                className: 'agentOrSlashCommandDetected',
                inline: true,
                actionHandler: {
                    disposables: templateData.elementDisposables,
                    callback: (content) => {
                        this._onDidClickRerunWithAgentOrCommandDetection.fire(element);
                    },
                }
            }));
        }
        else if (!element.isComplete) {
            templateData.detail.textContent = GeneratingPhrase;
        }
    }
    renderAvatar(element, templateData) {
        const icon = isResponseVM(element) ?
            this.getAgentIcon(element.agent?.metadata) :
            (element.avatarIcon ?? Codicon.account);
        if (icon instanceof URI) {
            const avatarIcon = $$1('img.icon');
            avatarIcon.src = ( (FileAccess.uriToBrowserUri(icon).toString(true)));
            templateData.avatarContainer.replaceChildren($$1('.avatar', undefined, avatarIcon));
        }
        else {
            const avatarIcon = $$1(ThemeIcon.asCSSSelector(icon));
            templateData.avatarContainer.replaceChildren($$1('.avatar.codicon-avatar', undefined, avatarIcon));
        }
    }
    getAgentIcon(agent) {
        if (agent?.themeIcon) {
            return agent.themeIcon;
        }
        else if (agent?.iconDark && this.themeService.getColorTheme().type === ColorScheme.DARK) {
            return agent.iconDark;
        }
        else if (agent?.icon) {
            return agent.icon;
        }
        else {
            return Codicon.copilot;
        }
    }
    basicRenderElement(element, index, templateData) {
        let value = [];
        if (isRequestVM(element)) {
            const markdown = 'message' in element.message ?
                element.message.message :
                this.markdownDecorationsRenderer.convertParsedRequestToMarkdown(element.message);
            value = [{ content: ( (new MarkdownString(markdown))), kind: 'markdownContent' }];
        }
        else if (isResponseVM(element)) {
            value = annotateSpecialMarkdownContent(element.response.value);
            if (element.contentReferences.length) {
                value.unshift({ kind: 'references', references: element.contentReferences });
            }
        }
        clearNode(templateData.value);
        if (isResponseVM(element)) {
            this.renderDetail(element, templateData);
        }
        const parts = [];
        value.forEach((data, index) => {
            const context = {
                element,
                index,
                content: value,
                preceedingContentParts: parts,
            };
            const newPart = this.renderChatContentPart(data, templateData, context);
            if (newPart) {
                templateData.value.appendChild(newPart.domNode);
                parts.push(newPart);
            }
        });
        templateData.renderedParts = parts;
        if (isResponseVM(element) && element.errorDetails?.message) {
            const renderedError = this.instantiationService.createInstance(ChatWarningContentPart, element.errorDetails.responseIsFiltered ? 'info' : 'error', ( (new MarkdownString(element.errorDetails.message))), this.renderer);
            templateData.elementDisposables.add(renderedError);
            templateData.value.appendChild(renderedError.domNode);
        }
        const newHeight = templateData.rowContainer.offsetHeight;
        const fireEvent = !element.currentRenderedHeight || element.currentRenderedHeight !== newHeight;
        element.currentRenderedHeight = newHeight;
        if (fireEvent) {
            const disposable = templateData.elementDisposables.add(scheduleAtNextAnimationFrame(getWindow(templateData.value), () => {
                element.currentRenderedHeight = templateData.rowContainer.offsetHeight;
                disposable.dispose();
                this._onDidChangeItemHeight.fire({ element, height: element.currentRenderedHeight });
            }));
        }
    }
    updateItemHeight(templateData) {
        if (!templateData.currentElement) {
            return;
        }
        const newHeight = templateData.rowContainer.offsetHeight;
        templateData.currentElement.currentRenderedHeight = newHeight;
        this._onDidChangeItemHeight.fire({ element: templateData.currentElement, height: newHeight });
    }
    renderWelcomeMessage(element, templateData) {
        clearNode(templateData.value);
        element.content.forEach((item, i) => {
            if (Array.isArray(item)) {
                const scopedInstaService = this.instantiationService.createChild(( (new ServiceCollection([IContextKeyService, templateData.contextKeyService]))));
                templateData.elementDisposables.add(scopedInstaService.createInstance(ChatFollowups, templateData.value, item, this.location, undefined, followup => this._onDidClickFollowup.fire(followup)));
            }
            else {
                const context = {
                    element,
                    index: i,
                    content: [],
                    preceedingContentParts: []
                };
                const result = this.renderMarkdown(item, templateData, context);
                templateData.value.appendChild(result.domNode);
                templateData.elementDisposables.add(result);
            }
        });
        const newHeight = templateData.rowContainer.offsetHeight;
        const fireEvent = !element.currentRenderedHeight || element.currentRenderedHeight !== newHeight;
        element.currentRenderedHeight = newHeight;
        if (fireEvent) {
            const disposable = templateData.elementDisposables.add(scheduleAtNextAnimationFrame(getWindow(templateData.value), () => {
                element.currentRenderedHeight = templateData.rowContainer.offsetHeight;
                disposable.dispose();
                this._onDidChangeItemHeight.fire({ element, height: element.currentRenderedHeight });
            }));
        }
    }
    doNextProgressiveRender(element, index, templateData, isInRenderElement) {
        if (!this._isVisible) {
            return true;
        }
        let isFullyRendered = false;
        if (element.isCanceled) {
            this.traceLayout('doNextProgressiveRender', `canceled, index=${index}`);
            element.renderData = undefined;
            this.basicRenderElement(element, index, templateData);
            isFullyRendered = true;
        }
        else {
            this.traceLayout('doNextProgressiveRender', `START progressive render, index=${index}, renderData=${JSON.stringify(element.renderData)}`);
            const contentForThisTurn = this.getNextProgressiveRenderContent(element);
            const partsToRender = this.diff(templateData.renderedParts ?? [], contentForThisTurn, element);
            isFullyRendered = partsToRender.every(part => part === null);
            if (isFullyRendered && element.isComplete) {
                this.traceLayout('doNextProgressiveRender', `END progressive render, index=${index} and clearing renderData, response is complete`);
                element.renderData = undefined;
                this.basicRenderElement(element, index, templateData);
            }
            else if (!isFullyRendered) {
                this.traceLayout('doNextProgressiveRender', `doing progressive render, ${partsToRender.length} parts to render`);
                this.renderChatContentDiff(partsToRender, contentForThisTurn, element, templateData);
            }
            else {
                return false;
            }
        }
        const height = templateData.rowContainer.offsetHeight;
        element.currentRenderedHeight = height;
        if (!isInRenderElement) {
            this._onDidChangeItemHeight.fire({ element, height: templateData.rowContainer.offsetHeight });
        }
        return isFullyRendered;
    }
    renderChatContentDiff(partsToRender, contentForThisTurn, element, templateData) {
        const renderedParts = templateData.renderedParts ?? [];
        templateData.renderedParts = renderedParts;
        partsToRender.forEach((partToRender, index) => {
            if (!partToRender) {
                return;
            }
            const alreadyRenderedPart = templateData.renderedParts?.[index];
            if (alreadyRenderedPart) {
                alreadyRenderedPart.dispose();
            }
            const preceedingContentParts = renderedParts.slice(0, index);
            const context = {
                element,
                content: contentForThisTurn,
                preceedingContentParts,
                index
            };
            const newPart = this.renderChatContentPart(partToRender, templateData, context);
            if (newPart) {
                if (alreadyRenderedPart) {
                    try {
                        alreadyRenderedPart.domNode.replaceWith(newPart.domNode);
                    }
                    catch (err) {
                        this.logService.error('ChatListItemRenderer#renderChatContentDiff: error replacing part', err);
                    }
                }
                else {
                    templateData.value.appendChild(newPart.domNode);
                }
                renderedParts[index] = newPart;
            }
            else if (alreadyRenderedPart) {
                alreadyRenderedPart.domNode.remove();
            }
        });
    }
    getNextProgressiveRenderContent(element) {
        const data = this.getDataForProgressiveRender(element);
        const renderableResponse = annotateSpecialMarkdownContent(element.response.value);
        this.traceLayout('getNextProgressiveRenderContent', `Want to render ${data.numWordsToRender}, counting...`);
        let numNeededWords = data.numWordsToRender;
        const partsToRender = [];
        if (element.contentReferences.length) {
            partsToRender.push({ kind: 'references', references: element.contentReferences });
        }
        for (const part of renderableResponse) {
            if (numNeededWords <= 0) {
                break;
            }
            if (part.kind === 'markdownContent') {
                const wordCountResult = getNWords(part.content.value, numNeededWords);
                if (wordCountResult.isFullString) {
                    partsToRender.push(part);
                }
                else {
                    partsToRender.push({ kind: 'markdownContent', content: ( (new MarkdownString(wordCountResult.value, part.content))) });
                }
                this.traceLayout('getNextProgressiveRenderContent', `  Want to render ${numNeededWords} words and found ${wordCountResult.returnedWordCount} words. Total words in chunk: ${wordCountResult.totalWordCount}`);
                numNeededWords -= wordCountResult.returnedWordCount;
            }
            else {
                partsToRender.push(part);
            }
        }
        this.traceLayout('getNextProgressiveRenderContent', `Want to render ${data.numWordsToRender} words and ${data.numWordsToRender - numNeededWords} words available`);
        const newRenderedWordCount = data.numWordsToRender - numNeededWords;
        if (newRenderedWordCount !== element.renderData?.renderedWordCount) {
            element.renderData = { lastRenderTime: Date.now(), renderedWordCount: newRenderedWordCount, renderedParts: partsToRender };
        }
        return partsToRender;
    }
    getDataForProgressiveRender(element) {
        const renderData = element.renderData ?? { lastRenderTime: 0, renderedWordCount: 0 };
        const rate = this.getProgressiveRenderRate(element);
        const numWordsToRender = renderData.lastRenderTime === 0 ?
            1 :
            renderData.renderedWordCount +
                Math.floor((Date.now() - renderData.lastRenderTime) / 1000 * rate);
        return {
            numWordsToRender,
            rate
        };
    }
    diff(renderedParts, contentToRender, element) {
        const diff = [];
        for (let i = 0; i < contentToRender.length; i++) {
            const content = contentToRender[i];
            const renderedPart = renderedParts[i];
            if (!renderedPart || !renderedPart.hasSameContent(content, contentToRender.slice(i + 1), element)) {
                diff.push(content);
            }
            else {
                diff.push(null);
            }
        }
        return diff;
    }
    renderChatContentPart(content, templateData, context) {
        if (content.kind === 'treeData') {
            return this.renderTreeData(content, templateData, context);
        }
        else if (content.kind === 'progressMessage') {
            return this.instantiationService.createInstance(ChatProgressContentPart, content, this.renderer, context);
        }
        else if (content.kind === 'progressTask') {
            return this.renderProgressTask(content, templateData, context);
        }
        else if (content.kind === 'command') {
            return this.instantiationService.createInstance(ChatCommandButtonContentPart, content, context);
        }
        else if (content.kind === 'textEditGroup') {
            return this.renderTextEdit(context, content, templateData);
        }
        else if (content.kind === 'confirmation') {
            return this.renderConfirmation(context, content, templateData);
        }
        else if (content.kind === 'warning') {
            return this.instantiationService.createInstance(ChatWarningContentPart, 'warning', content.content, this.renderer);
        }
        else if (content.kind === 'markdownContent') {
            return this.renderMarkdown(content.content, templateData, context);
        }
        else if (content.kind === 'references') {
            return this.renderContentReferencesListData(content, undefined, context, templateData);
        }
        return undefined;
    }
    renderTreeData(content, templateData, context) {
        const data = content.treeData;
        const treeDataIndex = context.preceedingContentParts.filter(part => part instanceof ChatTreeContentPart).length;
        const treePart = this.instantiationService.createInstance(ChatTreeContentPart, data, context.element, this._treePool, treeDataIndex);
        treePart.addDisposable(treePart.onDidChangeHeight(() => {
            this.updateItemHeight(templateData);
        }));
        if (isResponseVM(context.element)) {
            const fileTreeFocusInfo = {
                treeDataId: ( (data.uri.toString())),
                treeIndex: treeDataIndex,
                focus() {
                    treePart.domFocus();
                }
            };
            treePart.addDisposable(treePart.onDidFocus(() => {
                this.focusedFileTreesByResponseId.set(context.element.id, fileTreeFocusInfo.treeIndex);
            }));
            const fileTrees = this.fileTreesByResponseId.get(context.element.id) ?? [];
            fileTrees.push(fileTreeFocusInfo);
            this.fileTreesByResponseId.set(context.element.id, distinct(fileTrees, (v) => v.treeDataId));
            treePart.addDisposable(toDisposable(() => this.fileTreesByResponseId.set(context.element.id, fileTrees.filter(v => v.treeDataId !== ( (data.uri.toString()))))));
        }
        return treePart;
    }
    renderContentReferencesListData(references, labelOverride, context, templateData) {
        const referencesPart = this.instantiationService.createInstance(ChatReferencesContentPart, references.references, labelOverride, context.element, this._contentReferencesListPool);
        referencesPart.addDisposable(referencesPart.onDidChangeHeight(() => {
            this.updateItemHeight(templateData);
        }));
        return referencesPart;
    }
    renderProgressTask(task, templateData, context) {
        if (!isResponseVM(context.element)) {
            return;
        }
        const taskPart = this.instantiationService.createInstance(ChatTaskContentPart, task, this._contentReferencesListPool, this.renderer, context);
        taskPart.addDisposable(taskPart.onDidChangeHeight(() => {
            this.updateItemHeight(templateData);
        }));
        return taskPart;
    }
    renderConfirmation(context, confirmation, templateData) {
        const part = this.instantiationService.createInstance(ChatConfirmationContentPart, confirmation, context);
        part.addDisposable(part.onDidChangeHeight(() => this.updateItemHeight(templateData)));
        return part;
    }
    renderTextEdit(context, chatTextEdit, templateData) {
        const textEditPart = this.instantiationService.createInstance(ChatTextEditContentPart, chatTextEdit, context, this.rendererOptions, this._diffEditorPool, this._currentLayoutWidth);
        textEditPart.addDisposable(textEditPart.onDidChangeHeight(() => {
            textEditPart.layout(this._currentLayoutWidth);
            this.updateItemHeight(templateData);
        }));
        return textEditPart;
    }
    renderMarkdown(markdown, templateData, context) {
        const element = context.element;
        const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete || !!element.renderData);
        const codeBlockStartIndex = context.preceedingContentParts.reduce((acc, part) => acc + (part instanceof ChatMarkdownContentPart ? part.codeblocks.length : 0), 0);
        const markdownPart = this.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, this._currentLayoutWidth, this.codeBlockModelCollection, this.rendererOptions);
        markdownPart.addDisposable(markdownPart.onDidChangeHeight(() => {
            markdownPart.layout(this._currentLayoutWidth);
            this.updateItemHeight(templateData);
        }));
        const codeBlocksByResponseId = this.codeBlocksByResponseId.get(element.id) ?? [];
        this.codeBlocksByResponseId.set(element.id, codeBlocksByResponseId);
        markdownPart.addDisposable(toDisposable(() => {
            const codeBlocksByResponseId = this.codeBlocksByResponseId.get(element.id);
            if (codeBlocksByResponseId) {
                markdownPart.codeblocks.forEach((info, i) => delete codeBlocksByResponseId[codeBlockStartIndex + i]);
            }
        }));
        markdownPart.codeblocks.forEach((info, i) => {
            codeBlocksByResponseId[codeBlockStartIndex + i] = info;
            if (info.uri) {
                const uri = info.uri;
                this.codeBlocksByEditorUri.set(uri, info);
                markdownPart.addDisposable(toDisposable(() => this.codeBlocksByEditorUri.delete(uri)));
            }
        });
        return markdownPart;
    }
    disposeElement(node, index, templateData) {
        this.traceLayout('disposeElement', `Disposing element, index=${index}`);
        if (templateData.renderedParts) {
            try {
                dispose(coalesce(templateData.renderedParts));
                templateData.renderedParts = undefined;
                clearNode(templateData.value);
            }
            catch (err) {
                throw err;
            }
        }
        templateData.currentElement = undefined;
        templateData.elementDisposables.clear();
    }
    disposeTemplate(templateData) {
        templateData.templateDisposables.dispose();
    }
};
ChatListItemRenderer = ChatListItemRenderer_1 = ( (__decorate([
    ( (__param(6, IInstantiationService))),
    ( (__param(7, IConfigurationService))),
    ( (__param(8, ILogService))),
    ( (__param(9, IContextKeyService))),
    ( (__param(10, IThemeService))),
    ( (__param(11, ICommandService))),
    ( (__param(12, IHoverService)))
], ChatListItemRenderer)));
let ChatListDelegate = class ChatListDelegate {
    constructor(defaultElementHeight, logService) {
        this.defaultElementHeight = defaultElementHeight;
        this.logService = logService;
    }
    _traceLayout(method, message) {
        {
            this.logService.trace(`ChatListDelegate#${method}: ${message}`);
        }
    }
    getHeight(element) {
        const kind = isRequestVM(element) ? 'request' : 'response';
        const height = ('currentRenderedHeight' in element ? element.currentRenderedHeight : undefined) ?? this.defaultElementHeight;
        this._traceLayout('getHeight', `${kind}, height=${height}`);
        return height;
    }
    getTemplateId(element) {
        return ChatListItemRenderer.ID;
    }
    hasDynamicHeight(element) {
        return true;
    }
};
ChatListDelegate = ( (__decorate([
    ( (__param(1, ILogService)))
], ChatListDelegate)));
class ChatVoteButton extends MenuEntryActionViewItem {
    render(container) {
        super.render(container);
        container.classList.toggle('checked', this.action.checked);
    }
}
export { ChatListDelegate, ChatListItemRenderer };
