协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
chat-pet-sprite-creation【开发与工程】Use when creating or changing VS Code chat pet sprite art, sprite sheets, state animations, eye treatments, Stable/Insiders variants, or pet transitions under src/vs/workbench/contrib/chat/browser/widget/media/chatPet.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Create pet sprites that feel like one continuous character rather than separate drawings. Start from the current pet body, preserve its visual anchor, and make every difference intentional.
src/vs/workbench/contrib/chat/browser/widget/media/chatPet/src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.tssrc/vs/workbench/contrib/chat/browser/widget/media/chatPet.csssrc/vs/workbench/contrib/chat/test/browser/widget/chatPetWidget.test.tsRead the current implementation before drawing. Existing dimensions and timings may have changed since this skill was written.
This skill distills the complete pet PR history. When rules conflict, use this precedence:
In particular, animation cleanup PR #330399 supersedes the old revive-sign flow and several earlier sprite/timing assumptions.
| PR | Durable learning |
|---|---|
| #327063 | The pet is one semantic button; sprite canvases, images, eyes, and effects are visual-only and aria-hidden. Resolve asset URLs through FileAccess. |
| #327412 | Model reactions as explicit states. Do not immediately repeat ordinary random reactions; sample rare transformations separately. |
| #327588 | Double-buffer sprite images and swap only after the pending image loads. Never blank the active sprite while a new source is loading. |
| #327696 | Stable/Insiders is a typed, persisted appearance choice. Keep variant naming and geometry systematic. |
| #327714 | Animated art is a horizontal sprite sheet plus a deliberate static reduced-motion PNG. Timing is runtime data, not image metadata. |
| #328334 | Persistent behavior such as “on the run” belongs in service state. Multi-phase animations advance from completion signals, not guessed delays. |
| #328480 | A drag release must not also trigger a click reaction. Gestures that share pointer events need explicit suppression/ownership. |
| #328530 | Waking consumes the interaction that woke the pet; it must not also trigger a random reaction. Reduced motion may skip the wake transition. |
| #329121 | Coalesce gaze updates, pause sprite timers while the document is hidden, and resume from elapsed time rather than replaying missed frames. |
| #329347 | Keep pixel-art pose changes in sprite frames and spatial motion in layout/CSS/physics. Complete falls from transition signals. |
| #329729 | Face the interaction before starting a reaction. Give each transient state an explicit lifetime and use real completion events for chained phases. |
| #329852 | Pointer and keyboard gestures need equivalent outcomes. Reduced motion preserves the state change while removing the travel animation. |
| #329867 | Attention animations are bounded; clapping stops after the confirmation-attention window even if the confirmation remains pending. |
| #330160 | Eyes are a composited runtime layer with tracking/blinking modes. Rapid facing changes can trigger a separate dizzy state. Wide art must respect live bounds. |
| #330275 | Physics uses the body footprint, bounded frame steps, and explicit impact moments. Recompute geometry when layout changes during flight. |
| #330399 | Latest animation contract: wide sleep/wake art, frame-aware blink composition, fixed-orientation decorations, and reverse despawn → forward respawn using one effect sheet. |
The canonical art unit is one logical pixel:
8×8 source pixels4×4 CSS pixels at the standard 96×96 source → 48×48 display scaleCore silhouettes, facial features, props, and repeated details must use the same logical-pixel scale. Do not make a new object from smaller or larger blocks simply to fit more detail.
Effects may deliberately subdivide the logical pixel—for example, small stars, bubbles, or curved motion—but the subdivision must:
Use nearest-neighbor rendering only. Keep transparent backgrounds and hard pixel edges. Do not introduce blur, feathered alpha, color interpolation, or accidental off-grid scaling.
Never redraw the pet body from memory.
This preserves:
The first and last key poses must transition cleanly to idle or rendering without a body-position, silhouette, baseline, or scale pop.
The default source canvas is 96×96, or 12×12 logical pixels.
Use a larger canvas only when content intentionally extends outside the canonical body box:
Do not center the body inside a wider frame. Keep the body in the canonical 96×96 region and let the prop/effect overhang. Declare every nonstandard source dimension in chatPetWidget.ts and update wide-sprite boundary handling.
The runtime collision and movement box belongs to the pet body, not to a decorative overhang.
The standard open eye is 1×2 logical pixels:
8×16 source pixels4×8 CSS pixelsUse a different eye shape only when the state communicates a special expression, such as sleep, dizzy, love, worry, or impact.
The DOM eye layer has two independent modes:
States that use runtime gaze must use eye-less *-tracking-96 assets. States that use DOM blinking, such as typing, button press, or love in PR #330399, must also leave the corresponding pupil area available for the overlay.
When adding or changing a DOM-eye state:
doesChatPetStateTrackCursor when appropriate.doesChatPetStateBlink(state, frameIndex) when appropriate.Never combine baked pupils with the runtime eye overlay.
States with a fixed expression should bake the eyes into the art and opt out of cursor tracking. Preserve the standard eye position unless the expression intentionally changes it.
Stable and Insiders are palette variants of the same animation.
They must have identical:
Create one geometry master and apply the two established palettes. Do not redraw variants independently.
Review the two variants side by side. Any shape difference is a bug unless the product explicitly requires it.
Author one canonical facing direction and let the runtime mirror it for the other direction.
Test both directions because mirroring also changes where wide sprites overhang. Keep enough room for boundary correction near the left and right edges.
Decorations that must retain their screen orientation—such as text, musical notation, or directional symbols—must not be baked into a layer that the runtime mirrors. Model them as fixed-orientation decorations or render them separately.
Each animated state needs both variants of:
buddy-<state>-<variant>-<height>.pngbuddy-<state>-<variant>-<height>.spritesheet.pngThe sprite sheet contract is:
sheet width = frame width × frame count
sheet height = frame height
Every frame uses the same rectangle. Never shift frame boundaries or add per-frame padding.
Choose a meaningful static pose that communicates the state without motion. Do not assume the first animation frame is automatically the best reduced-motion fallback.
Wearable accessories use a body-owned attachment rig and one palette-independent atlas per appearance under media/chatPet/accessories/. Do not export copies for individual runtime states or frames. Body animation metadata owns attachment movement; an accessory atlas only contains the few canonical shapes needed when the body geometry changes.
The default atlas is 256×192, divided into 64×64 cells. A hat whose supplied silhouette genuinely needs the full 12-logical-pixel body width may declare atlasCellSize: 96 and use a 384×288 atlas with 96×96 cells. Do not choose the wider tier merely to add detail; both tiers still use the same whole 8×8 logical pixels.
columns: upright | sleeping | impact | splat
row 0: head back layers
row 1: head front layers
row 2: right-eye front layers
An appearance may leave any cell transparent. For example, a plain hat leaves the eye row empty, while a combined Top Hat & Monocle appearance uses both head rows and the eye row. The airborne rig pose reuses the upright column. Add another canonical column only when an existing pose plus body-owned translation cannot preserve the intended silhouette.
Attachment tracks live in chatPetAccessoryRig.ts. Each track uses compressed frame spans with a canonical pose and independent head/right-eye anchors. A slot is omitted while a body-authored expression, prop, or complete silhouette replacement owns the same geometry, such as dizzy eyes, sunglasses, or the rare icon transformation. When frames inside one sheet bake different facing directions, the track marks those frames so the head accessory mirrors with the body before the whole pet's outer facing transform is applied. Update the body-owned track only when body geometry changes. Adding an ordinary accessory must not require editing tracks or renderer code.
Head-slot anchors include a one-logical-pixel wear offset so hats overlap the top of the head rather than resting on its silhouette. The love animation suppresses head accessories because its transformed head/antenna silhouette is the reaction. Treat slot visibility and fit as body-owned behavior, not per-hat exceptions.
Appearances that tuck the antennae under a larger hat opt into coversAntennae. The compositor uses body-owned occlusion bounds before drawing the hat front, so the behavior follows animation tracks and mirroring without painting body-colored cover pixels into each atlas.
Directional head accessories are authored for the canonical right-facing body and mirror with the complete pet canvas. A hat with directional structure should stay visually balanced over the head while expressing facing through a restrained one-logical-pixel cue in its brim, visor, lean, nose guard, or other asymmetric detail; do not shift the whole silhouette far off-center. Make asymmetric parts read correctly after mirroring. Identity-bound eye accessories may opt out of mirroring and use a direction-specific eye anchor so they remain on the same eye; the Top Hat & Monocle is the reference. Fixed-eye anchors must include the body origin shift for wide frames (frameWidth - 96). Review every appearance in both directions with the facing fixture.
Static reduced-motion art may represent a later frame of its animated sheet. Keep getChatPetReducedMotionRigFrame() aligned with the representative body frame; do not assume rig frame zero merely because the static PNG has one image frame.
Author every wearable part on the same 8×8 logical-pixel grid as the body. Brims, crowns, bands, rims, chains, highlights, and shadows must all be composed from whole aligned logical pixels; do not use diagonal polygons or source-pixel stair steps to imply curves. The small-effect exception does not apply to wearable accessories. Use the current Stable body sheets as geometry guides and validate that every alpha value is fully transparent or fully opaque. The runtime mirrors layers with the body; fixed-orientation body decorations are restored without erasing accessory pixels.
When changing the accessory source at runtime, keep the current composite visible until the replacement image loads and passes exact dimension validation. Redraw the current frame without restarting the body animation. A failed or malformed accessory source falls back to the body alone and must not repeatedly retry.
Animate key poses, not noise.
Frame duration is part of the art. Define it in the matching *_FRAME_DURATIONS array in chatPetWidget.ts; do not encode or infer timing from the PNG.
Keep the transient-state duration consistent with the intended loops or one-shot playback. Explicitly choose whether the sheet:
If one sheet is reused in reverse, as with respawn → despawn:
PR #330399 is the reference pattern: the respawn burst plays backward to despawn at the bottom, then forward to respawn at the top. The old revive-sign interstitial is superseded.
Use sprite frames for changes to the pet or prop silhouette. Use CSS transforms or the physics loop for:
Do not redraw every translated/rotated pose into the sheet. Conversely, do not use a CSS transform to fake a body deformation that should be readable in the pixel art.
Advance multi-phase behavior from deterministic completion signals:
animationend for CSS keyframestransitionend/transitioncancel for fallsDo not guess a completion time with an unrelated timeout.
Sprite timing uses elapsed-time scheduling at frame boundaries, not a display-refresh polling loop. A late callback recalculates the correct frame and shortens the next delay.
Review the sprite in the same environment where it runs:
Pixels intended to pass behind the platform must be clipped or layered behind it. Do not leave body/effect pixels visible below an occluding platform.
Movement should preserve the body anchor until physics intentionally moves the whole pet. Decorative frame size must not change the physics target.
The rendered state has a deliberate priority. Busy/task states must not be accidentally hidden by decorative reactions. Preserve the current order in getChatPetBaseState and the transient-state guards in getChatPetRenderedState.
Interaction rules:
Rare interactions should be sampled independently from the ordinary non-repeating pool so adding a common reaction does not silently change an easter egg's probability.
Keep the current rendering architecture:
getAttribute('src'); .src may normalize VS Code resource URLs.imageSmoothingEnabled = false and image-rendering: pixelated.requestAnimationFrame for physics, but cap integration steps to prevent tunneling after a slow frame.When adding a state, update every applicable surface:
ChatPetState.getChatPetSpriteName.getChatPetFrameDurations.getSpriteSources.Do not add art without completing the runtime contract.
Keep the visuals silent: images, canvases, eyes, and effects use empty alt text and aria-hidden. The button owns the accessible label, focusability, and localized status() announcements. Hidden, dead, or non-interactive states must leave the tab order.
8×8 source-pixel logical unit.1×2 logical pixels.aria-hidden; the button owns semantics and tab order.ChatPetWidget tests cover state names, exact timings, geometry, state priority, and reduced motion.256×192) or wide (384×288) cell tier and the documented column/row contract.8×8 logical-pixel grid.Use the chat/chatPetAccessoryRig/chatPetAccessoryRig/CriticalPoses component fixture to review critical animated and reduced-motion poses, and AllAccessoriesFacing to compare every appearance in both directions.
Run the focused unit tests using the repository's unit-tests skill. At minimum, run the ChatPetWidget test suite.
Prefer exporting pure helpers for timing, geometry, random selection, and state precedence, then test them directly. Use fake timers for scheduler behavior. Assert exact frame-duration arrays so art and runtime timing cannot drift independently.
The public showcase lives in the sibling vscode-chat-pet repository. Use its update-pet-previews skill after production sprites are final.
When replacing an existing APNG, prefer a new versioned filename in the README so GitHub and browser caches cannot keep showing the previous animation.