Add chat pet component fixtures - #329460
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9a5064b-06cf-4444-99ae-be62859a1c46
| view.setResisting(true); | ||
| break; | ||
| } | ||
| if (options.position === 'left') { |
There was a problem hiding this comment.
AI Review: RenderingSpeechBubbleLeft sets position: 'left', which moves the pet to the left edge. The speech bubble only flips left when the pet is near the right boundary, so this fixture never exercises the state its name promises. Could this place the pet at the right edge instead (or be renamed if the current layout is intentional)?
| const sources = getSpeechSpriteSources(this._variant, this._options.resourceBaseUrl); | ||
| const source = this._motionReduced ? sources.reducedMotion : sources.animated; | ||
| if (!isChatPetImageSource(this._speechBubble.image, source.url)) { | ||
| this._speechAnimation.clear(); |
There was a problem hiding this comment.
AI Review: When the speech source changes, this clears the disposable but leaves _activeSpeechAnimation pointing at the previous source until the replacement image loads. Because whenReady() only checks that this field is defined, it can resolve immediately and miss a subsequent load error. Please clear _activeSpeechAnimation synchronously here and add coverage for speaking-state variant reload readiness.
Justin Chen (justschen)
left a comment
There was a problem hiding this comment.
awesome! as mentioned, gonna hold off while we hash out the last bit of the animations, but okay to merge next week once we finalize how we want the pet to work
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
There was a problem hiding this comment.
Review details
Suppressed comments (5)
src/vs/workbench/test/browser/componentFixtures/fixtureUtils.ts:393
- The schema now supports
enableAnimationsByDefault, but the render path still usesparseFixtureInput(context.input)(which defaultsenableAnimationstofalsewhen the property is missing). Ifcontext.inputdoesn’t already include schema defaults, fixtures that setenableAnimationsByDefault: truemay still initialize with animations disabled. Consider parsing via this Zod schema (so defaults are applied) or updatingparseFixtureInputto accept/derive the default value.
function createFixtureInputSchema(enableAnimationsByDefault: boolean) {
return z.object({
reverseStylesheets: z.boolean().default(false).describe('Reverse the order of the bundled CSS documents to surface cascade-order dependencies.'),
reverseStylesheetsRange: z.object({
fromIndex: z.number(),
toIndex: z.number(),
}).optional().describe('Reverse the bundled CSS documents in this half-open index range.'),
enableAnimations: z.boolean().default(enableAnimationsByDefault).describe('Enable CSS animations and transitions.'),
outputTimeTrace: z.boolean().default(false).describe('Return the render\'s virtual-time trace as its output.'),
outputStylesheetFiles: z.boolean().default(false).describe('Return the bundled stylesheet files as the render output.'),
});
}
src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.ts:543
- String-concatenating
resourceBaseUrlandresourcecan produce malformed URLs (e.g. double slashes whenresourceBaseUrlends with/, or awkward results ifresourceBaseUrlis a full URL). Consider normalizing (trim trailing slash) or using a URI/path join helper soresourceBaseUrlcan be safely configured for fixtures and future callers.
function resolveChatPetResource(resource: AppResourcePath, resourceBaseUrl: string | undefined): string {
return resourceBaseUrl === undefined ? FileAccess.asBrowserUri(resource).toString(true) : `${resourceBaseUrl}/${resource}`;
}
src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.ts:1140
- This now clears/redraws the canvas on every scheduled tick, even when
frame.frameIndexhasn’t changed. The previous implementation avoided unnecessary draws by tracking the current frame index. Reintroducing a simplelastFrameIndexcheck (per sprite animation) would reduce CPU/GPU work during long-running animations, especially when frame delays are small or when the timer fires slightly early/late.
private _drawAnimationFrame(source: ChatPetSpriteSource, sprite: ChatPetSpriteElement, elapsed: number): ChatPetAnimationFrame {
const context = sprite.canvas.getContext('2d');
const frame = getChatPetAnimationFrame(source.frameDurations, elapsed, this._options.loopAnimations ? Infinity : source.iterations);
if (!context) {
return frame;
}
context.imageSmoothingEnabled = false;
context.clearRect(0, 0, CHAT_PET_SOURCE_SIZE, CHAT_PET_SOURCE_SIZE);
context.drawImage(
sprite.image,
frame.frameIndex * CHAT_PET_SOURCE_SIZE,
0,
CHAT_PET_SOURCE_SIZE,
CHAT_PET_SOURCE_SIZE,
0,
0,
CHAT_PET_SOURCE_SIZE,
CHAT_PET_SOURCE_SIZE
);
return frame;
}
src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.ts:1020
- The new error doesn’t include enough context to diagnose which render triggered it (e.g. pending state, variant, and/or whether it was the speech bubble vs main sprite). Consider including
this._pendingState,this._variant, and the relevant source URL/type in the error message to make fixture failures and telemetry/debugging actionable.
private _onImageError(image: HTMLImageElement): void {
this._renderError = new Error(`Failed to load chat pet resource: ${image.src}`);
this._onDidRender.fire();
}
src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.ts:794
- The new externally-controlled animation mode (pausing/resuming sprite + CSS animations based on
animationTime) is complex and can regress easily. Please add targeted tests that: (1) assert animations are paused and scrubbed whenanimationTimeis set, (2) assert_resumeAnimations()restarts animations whenanimationTimebecomesundefined, and (3) validateonDidCompleteAnimationbehavior in controlled mode (completion reported once per completion).
this._register(autorun(reader => {
const animationTime = this._animationTime.read(reader);
if (animationTime === undefined) {
if (this._animationControlled) {
this._animationControlled = false;
this._resumeAnimations();
}
return;
}
this._animationControlled = true;
this._spriteAnimation.clear();
this._speechAnimation.clear();
this._renderControlledAnimation(this._activeSpriteAnimation, animationTime);
this._renderControlledAnimation(this._activeSpeechAnimation, animationTime);
this._updateCssAnimationTime(animationTime);
}));
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
- Review effort level: Lite
Summary
chatPetWidget.tsTesting
npm run typecheck-clientnpm run transpile-client.\scripts\test.bat --run src\vs\workbench\contrib\chat\test\browser\widget\chatPetWidget.test.ts(19 passing)