add light animations for editing and todo/editing list#300201
add light animations for editing and todo/editing list#300201
Conversation
There was a problem hiding this comment.
Pull request overview
Adds new UI animations to the chat experience (above-input widgets, todo title flip, and edit bubble expansion) and updates supporting code/types used by the chat workbench.
Changes:
- Add reveal/collapse and title-flip animations for above-input widgets (todo list, editing list, status/tip widgets, question carousel).
- Animate the “edit request” input bubble expanding from the original bubble width.
- Update Monaco type declarations and register a new CSS custom property for stylelint.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/browser/widget/media/chat.css | Adds new keyframes and classes for above-input reveal/collapse, todo title flip, and edit bubble expansion. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatStatusWidget.ts | Triggers the new reveal animation when the status widget becomes visible. |
| src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts | Triggers reveal/collapse animations for the question carousel and working set list. |
| src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts | Captures bubble width and triggers reveal animation for the getting-started tip. |
| src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts | Adds todo list expand/collapse animations, title flip animation logic, and shared animation helper functions. |
| src/vs/monaco.d.ts | Tightens types for some editor options (notably placeholder). |
| build/lib/stylelint/vscode-known-variables.json | Adds --chat-edit-bubble-width to the known variables list. |
Comments suppressed due to low confidence (3)
src/vs/workbench/contrib/chat/browser/widget/media/chat.css:872
- The reveal keyframe ends with
max-height: 500px. Combined withanimation-fill-mode: forwardson the reveal class, this can leave the element permanently capped at 500px (clipping content). Consider restoringmax-heightafter the animation (e.g. removing the reveal class onanimationend) or avoiding a fixed max-height in the final keyframe.
to {
opacity: 1;
transform: translateY(0) scale(1);
max-height: 500px;
}
src/vs/workbench/contrib/chat/browser/widget/media/chat.css:883
- Please add a reduced-motion override for these animations. VS Code commonly disables animations under
.monaco-workbench.monaco-reduce-motion(seesrc/vs/workbench/contrib/chat/electron-browser/actions/media/voiceChatActions.css).
animation: kf-chat-above-input-reveal 350ms cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
transform-origin: bottom center;
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts:505
triggerRevealAnimationalways leaves thechat-above-input-revealclass on the element. Since the CSS usesanimation-fill-mode: forwardsand animatesmax-height, consider removing the reveal class onanimationend(or otherwise restoring layout styles) so the element doesn’t remain in an animation-filled state after the transition.
export function triggerRevealAnimation(element: HTMLElement): void {
element.classList.remove('chat-above-input-reveal');
element.classList.remove('chat-above-input-collapse');
void element.offsetWidth; // Force reflow to restart animation
element.classList.add('chat-above-input-reveal');
You can also share your feedback on Copilot code review. Take the survey.
| import { triggerRevealAnimation } from './chatContentParts/chatTodoListWidget.js'; | ||
| import { ChatContentMarkdownRenderer } from './chatContentMarkdownRenderer.js'; |
There was a problem hiding this comment.
Importing triggerRevealAnimation from chatTodoListWidget.ts couples the main chat widget to the todo list implementation. Consider moving the animation helpers into a shared utility module and importing from there instead, so changes to the todo widget don’t unintentionally affect other widgets.
| import { IChatContentPartRenderContext } from '../chatContentParts/chatContentParts.js'; | ||
| import { CollapsibleListPool, IChatCollapsibleListItem } from '../chatContentParts/chatReferencesContentPart.js'; | ||
| import { ChatTodoListWidget } from '../chatContentParts/chatTodoListWidget.js'; | ||
| import { ChatTodoListWidget, triggerCollapseAnimation, triggerRevealAnimation } from '../chatContentParts/chatTodoListWidget.js'; |
There was a problem hiding this comment.
triggerRevealAnimation/triggerCollapseAnimation are imported from chatTodoListWidget.ts, which couples the input part to the todo widget implementation. Since these helpers are now shared by multiple widgets, consider moving them into a small shared utility module to avoid unnecessary dependencies and keep the todo widget file focused.
| .todo-list-container.chat-above-input-reveal { | ||
| animation: kf-chat-above-input-reveal 350ms cubic-bezier(0.25, 0.8, 0.25, 1) forwards; | ||
| transform-origin: bottom center; | ||
| } |
There was a problem hiding this comment.
The reveal animation animates max-height but the reveal class doesn’t set overflow: hidden/clip, so children can paint outside the shrinking/growing box during the animation (causing overlap). Add an overflow rule for the reveal state if you want the height animation to actually constrain content during the transition.
| from { | ||
| opacity: 1; | ||
| max-height: 200px; | ||
| transform: translateY(0); | ||
| } |
There was a problem hiding this comment.
kf-chat-above-input-collapse hardcodes max-height: 200px at the start of the animation. If the actual content is taller, it will snap/clip immediately when the collapse starts. Consider using a value that can’t be smaller than the element’s actual height (or setting the start height dynamically).
This issue also appears on line 868 of the same file.
| .interactive-request.editing .chat-edit-input-container { | ||
| animation: kf-chat-edit-bubble-expand 280ms cubic-bezier(0.32, 1.2, 0.5, 1) forwards; | ||
| transform-origin: right center; | ||
| } |
There was a problem hiding this comment.
kf-chat-edit-bubble-expand should also be disabled when reduced motion is enabled; otherwise users with reduced motion will still get the bubble expansion animation.
This issue also appears on line 882 of the same file.
| import { triggerRevealAnimation } from '../chatContentParts/chatTodoListWidget.js'; | ||
| import { ChatInputPartWidgetsRegistry, IChatInputPartWidget } from './chatInputPartWidgets.js'; | ||
| import './media/chatStatusWidget.css'; | ||
|
|
There was a problem hiding this comment.
These helpers are imported from chatTodoListWidget.ts, which pulls in the todo widget implementation and its dependencies. Since the animation helpers are now used by multiple unrelated widgets, please move them into a small dedicated module (e.g. chatWidgetAnimations.ts) to avoid unnecessary coupling/bundle cost and to keep the todo widget file focused.
| element.classList.add('chat-above-input-collapse'); | ||
| element.addEventListener('animationend', () => { | ||
| element.style.display = 'none'; | ||
| element.classList.remove('chat-above-input-collapse'); | ||
| }, { once: true }); |
There was a problem hiding this comment.
The animationend handler here will run for any animation that ends on the element. If the element is re-shown (reveal animation started) before the collapse animation ends, this handler can fire on the reveal animation’s animationend and incorrectly set display: none. Guard on AnimationEvent.animationName (or otherwise cancel/remove any pending collapse handler) before hiding.
This issue also appears on line 501 of the same file.
| let exitGhost: HTMLElement | undefined; | ||
| if (shouldAnimate) { | ||
| exitGhost = dom.$('.todo-title-exit-ghost'); | ||
| for (const child of Array.from(titleElement.childNodes)) { | ||
| exitGhost.appendChild(child.cloneNode(true)); |
There was a problem hiding this comment.
This clones all existing title children into the exit ghost. If an update happens while a previous .todo-title-exit-ghost is still present, the ghost itself will be cloned and can cause visual artifacts. Consider removing any existing .todo-title-exit-ghost before cloning, or skipping the flip animation while one is in progress.
| const bubbleWidth = item.renderedParts?.[0]?.domNode?.offsetWidth; | ||
| const bubbleWidthPct = bubbleWidth ? Math.round(bubbleWidth / rowContainer.offsetWidth * 100) : 60; | ||
|
|
||
| this.inputContainer = dom.$('.chat-edit-input-container'); | ||
| this.inputContainer.style.setProperty('--chat-edit-bubble-width', `${bubbleWidthPct}%`); |
There was a problem hiding this comment.
bubbleWidthPct divides by rowContainer.offsetWidth without guarding for 0. In cases where the row is not measurable (e.g. temporarily hidden), this can produce Infinity%/NaN% and break the CSS animation. Guard for rowContainer.offsetWidth > 0 before dividing and fall back to the default when it isn’t.
video incoming sorry github isn't letting me upload