diff --git a/src/web-ui/public/agent-companion-pets/openbitfun-girl/README.md b/src/web-ui/public/agent-companion-pets/openbitfun-girl/README.md new file mode 100644 index 0000000000..09533ba2a3 --- /dev/null +++ b/src/web-ui/public/agent-companion-pets/openbitfun-girl/README.md @@ -0,0 +1,30 @@ +# Feiling (绯翎) + +![Idle, wave, and pointer-look preview](preview.gif) + +Feiling is an original chibi adaptation of the OpenBitFun Girl character, with a +small, softly oval face, a compact black-and-ivory bell skirt, and tiny legs with +rounded shoes. Her name combines crimson and feather imagery to evoke Bifang. +Silver hair and OpenBitFun ornaments preserve the character's identity: both the +outer outline and the inner opening of each logo are rounded six-sided hexagons. +The artwork was created with AI image generation from the character illustration, +the official brand mark, and the requested short-legged proportions. + +The built-in picker lists this pet immediately after the default blue-golden +cat. Feiling keeps the `openbitfun-girl` package ID and paths so saved selections +continue to work after the display-name change. The earlier OpenBitFun Bifang +mascot remains a separate preset. + +## Sprite contract + +- `spriteVersionNumber: 2`; lossless WebP with transparency. +- Atlas: 1536 × 2288 pixels, 8 columns × 11 rows, 192 × 208 pixels per cell. +- Rows 0–8: idle, running-right, running-left, waving, jumping, failed, waiting, + working (`running`), and review. Frame counts: 6, 8, 8, 4, 5, 8, 6, 6, 6. +- Rows 9–10: 16 look directions clockwise from up in 22.5-degree steps. +- The asymmetric hair ornament stays on the character's left; leftward and + rightward running were authored separately. + +The package uses the existing shared pet renderer and adds no host command, +protocol change, or configuration migration. `pet.json` can also be used to +import the package as a v2 pet. diff --git a/src/web-ui/public/agent-companion-pets/openbitfun-girl/pet.json b/src/web-ui/public/agent-companion-pets/openbitfun-girl/pet.json new file mode 100644 index 0000000000..fb2207a495 --- /dev/null +++ b/src/web-ui/public/agent-companion-pets/openbitfun-girl/pet.json @@ -0,0 +1,7 @@ +{ + "id": "openbitfun-girl", + "displayName": "绯翎", + "description": "Feiling, a silver-haired short-legged companion with a softly oval face and hollow rounded-hexagon ornaments.", + "spriteVersionNumber": 2, + "spritesheetPath": "spritesheet.webp" +} diff --git a/src/web-ui/public/agent-companion-pets/openbitfun-girl/preview.gif b/src/web-ui/public/agent-companion-pets/openbitfun-girl/preview.gif new file mode 100644 index 0000000000..c64de5479b Binary files /dev/null and b/src/web-ui/public/agent-companion-pets/openbitfun-girl/preview.gif differ diff --git a/src/web-ui/public/agent-companion-pets/openbitfun-girl/spritesheet.webp b/src/web-ui/public/agent-companion-pets/openbitfun-girl/spritesheet.webp new file mode 100644 index 0000000000..3dbef10848 Binary files /dev/null and b/src/web-ui/public/agent-companion-pets/openbitfun-girl/spritesheet.webp differ diff --git a/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.test.ts b/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.test.ts index acdeaca685..019d2e33bb 100644 --- a/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.test.ts +++ b/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; +import girlManifest from '../../../../public/agent-companion-pets/openbitfun-girl/pet.json'; const invoke = vi.fn(); @@ -46,4 +47,24 @@ describe('AgentCompanionPetService built-in presets', () => { }); expect(invoke).not.toHaveBeenCalled(); }); + + it('lists Feiling second and resolves its packaged v2 layout without host access', async () => { + const { listAgentCompanionPets, resolveAgentCompanionPet } = await import('./AgentCompanionPetService'); + const pets = await listAgentCompanionPets(); + const girl = pets[1]; + + expect(pets.slice(0, 3).map(pet => pet.id)).toEqual([ + 'blue-golden', 'openbitfun-girl', 'deepseek-goldwhale', + ]); + expect(girl).toMatchObject({ + ...girlManifest, + source: 'preset', + spritesheetPath: `/agent-companion-pets/${girlManifest.id}/${girlManifest.spritesheetPath}`, + spritesheetMimeType: 'image/webp', + }); + const resolved = await resolveAgentCompanionPet(girl); + expect(resolved.src).toBe(girl.spritesheetPath); + expect(resolved.layout).toMatchObject({ version: 2, columns: 8, rows: 11, supportsLook: true }); + expect(invoke).not.toHaveBeenCalled(); + }); }); diff --git a/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.ts b/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.ts index 8a14b8aaea..3382c4ea75 100644 --- a/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.ts +++ b/src/web-ui/src/infrastructure/config/services/AgentCompanionPetService.ts @@ -66,6 +66,16 @@ const BUILTIN_PETS: AgentCompanionPetSelection[] = [ { ...DEFAULT_AGENT_COMPANION_PET, }, + { + id: 'openbitfun-girl', + displayName: BUILTIN_PET_DISPLAY_NAMES.openbitfunGirl, + description: 'Feiling, a silver-haired short-legged companion with a softly oval face and hollow rounded-hexagon ornaments.', + source: 'preset', + packagePath: `${BUILTIN_PET_BASE}/openbitfun-girl`, + spritesheetPath: `${BUILTIN_PET_BASE}/openbitfun-girl/spritesheet.webp`, + spritesheetMimeType: 'image/webp', + spriteVersionNumber: 2, + }, { id: 'deepseek-goldwhale', displayName: BUILTIN_PET_DISPLAY_NAMES.goldWhale, diff --git a/src/web-ui/src/infrastructure/config/services/agentCompanionBuiltinPetMetadata.json b/src/web-ui/src/infrastructure/config/services/agentCompanionBuiltinPetMetadata.json index 311ed0ee16..f331ad79c9 100644 --- a/src/web-ui/src/infrastructure/config/services/agentCompanionBuiltinPetMetadata.json +++ b/src/web-ui/src/infrastructure/config/services/agentCompanionBuiltinPetMetadata.json @@ -1,6 +1,7 @@ { "displayNames": { "blueGolden": "困困", + "openbitfunGirl": "绯翎", "goldWhale": "大肥鱼", "gugugaga": "咕咕嘎嘎", "jiyi": "吉伊"