Files
accounted/components/agent/avatars.ts
T
Jakob Wennberg ec27228a8e style: remove em/en dashes repo-wide, add CLAUDE.md rule against them (#890)
Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.

Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.

Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.

Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 15:58:06 +02:00

43 lines
2.1 KiB
TypeScript

// Avatar registry for the specialized accountant agent.
//
// We use the dicebear "notionists" style: clean line illustrations that
// match the editorial monochrome brand without the cartoony feel of most
// avatar libraries. 8 hand-picked seeds give distinct faces without being
// overwhelming. The user picks one during Phase B review; the choice is
// persisted as agent_profiles.avatar_id.
//
// URLs are served by dicebear's free CDN. They're public SVGs derived from
// the seed only: no user data leaves gnubok. If we ever need fully offline
// generation, swap to @dicebear/core npm package and render server-side.
export interface AvatarOption {
id: string
label: string
url: string
}
// Build URL from seed. Public dicebear CDN. ?radius=50 rounds the bounding
// box; ?backgroundColor=transparent keeps the editorial paper-white feel.
function dicebearNotionists(seed: string): string {
return `https://api.dicebear.com/9.x/notionists/svg?seed=${encodeURIComponent(seed)}&radius=50&backgroundColor=f5f3ed`
}
// Eight neutral seeds: names chosen to produce visibly different faces.
// Labels are just for the picker tooltip; the user names the agent
// themselves in the adjacent text field.
export const AVATAR_OPTIONS: readonly AvatarOption[] = [
{ id: 'notionists-1', label: 'Linn', url: dicebearNotionists('linn-revisor-1') },
{ id: 'notionists-2', label: 'Erik', url: dicebearNotionists('erik-revisor-2') },
{ id: 'notionists-3', label: 'Maja', url: dicebearNotionists('maja-revisor-3') },
{ id: 'notionists-4', label: 'Anders', url: dicebearNotionists('anders-revisor-4') },
{ id: 'notionists-5', label: 'Karin', url: dicebearNotionists('karin-revisor-5') },
{ id: 'notionists-6', label: 'Johan', url: dicebearNotionists('johan-revisor-6') },
{ id: 'notionists-7', label: 'Eva', url: dicebearNotionists('eva-revisor-7') },
{ id: 'notionists-8', label: 'Per', url: dicebearNotionists('per-revisor-8') },
]
export function getAvatarUrl(avatarId: string | null | undefined): string | null {
if (!avatarId) return null
return AVATAR_OPTIONS.find((a) => a.id === avatarId)?.url ?? null
}