Add player character management feature
All checks were successful
CI / check (push) Successful in 45s
CI / build-image (push) Successful in 18s

Persistent player character templates (name, AC, HP, color, icon) with
full CRUD, bestiary-style search to add PCs to encounters with pre-filled
stats, and color/icon visual distinction in combatant rows. Also stops
the stat block panel from auto-opening when adding a creature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-12 18:11:08 +01:00
parent 768e7a390f
commit 91703ddebc
38 changed files with 3055 additions and 96 deletions

View File

@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import { deletePlayerCharacter } from "../delete-player-character.js";
import type { PlayerCharacter } from "../player-character-types.js";
import { playerCharacterId } from "../player-character-types.js";
import { isDomainError } from "../types.js";
const id1 = playerCharacterId("pc-1");
const id2 = playerCharacterId("pc-2");
function makePC(overrides?: Partial<PlayerCharacter>): PlayerCharacter {
return {
id: id1,
name: "Aragorn",
ac: 16,
maxHp: 120,
color: "green",
icon: "sword",
...overrides,
};
}
describe("deletePlayerCharacter", () => {
it("deletes an existing character", () => {
const result = deletePlayerCharacter([makePC()], id1);
if (isDomainError(result)) throw new Error(result.message);
expect(result.characters).toHaveLength(0);
});
it("returns error for not-found id", () => {
const result = deletePlayerCharacter([makePC()], id2);
expect(isDomainError(result)).toBe(true);
if (isDomainError(result)) {
expect(result.code).toBe("player-character-not-found");
}
});
it("emits PlayerCharacterDeleted event", () => {
const result = deletePlayerCharacter([makePC()], id1);
if (isDomainError(result)) throw new Error(result.message);
expect(result.events).toHaveLength(1);
expect(result.events[0].type).toBe("PlayerCharacterDeleted");
});
it("preserves other characters when deleting one", () => {
const pc1 = makePC({ id: id1, name: "Aragorn" });
const pc2 = makePC({ id: id2, name: "Legolas" });
const result = deletePlayerCharacter([pc1, pc2], id1);
if (isDomainError(result)) throw new Error(result.message);
expect(result.characters).toHaveLength(1);
expect(result.characters[0].name).toBe("Legolas");
});
it("event includes deleted character name", () => {
const result = deletePlayerCharacter([makePC()], id1);
if (isDomainError(result)) throw new Error(result.message);
const event = result.events[0];
if (event.type !== "PlayerCharacterDeleted") throw new Error("wrong event");
expect(event.name).toBe("Aragorn");
});
});