Simplify character dossier fields, add ShowPronouns setting
CI / build-and-push (push) Successful in 51s
CI / deploy (push) Successful in 9s

Collapse Want/Need into Motivation and Internal/External Conflict into
Conflict, drop Arc summary field, and move tags below open questions on
the character page. Gate Pronouns display behind new UiSettings:ShowPronouns
config (default off).
This commit is contained in:
James Wampler
2026-08-19 15:33:14 -07:00
parent bbd5e66777
commit 45afc980d3
18 changed files with 1425 additions and 147 deletions
+5
View File
@@ -23,11 +23,13 @@ import type {
NovelSummary,
TagReferences,
TagSummary,
UiSettings,
User,
} from './types'
export const keys = {
me: ['me'] as const,
uiSettings: ['ui-settings'] as const,
members: (novelId: string) => ['novels', novelId, 'members'] as const,
novels: ['novels'] as const,
genres: ['genres'] as const,
@@ -46,6 +48,9 @@ export const keys = {
importJob: (id: string) => ['imports', id] as const,
}
export const useUiSettings = () =>
useQuery({ queryKey: keys.uiSettings, queryFn: () => api.get<UiSettings>('/api/ui-settings') })
export const useMe = () =>
useQuery({
queryKey: keys.me,
+6 -5
View File
@@ -49,6 +49,10 @@ export interface User {
globalRole: GlobalRole
}
export interface UiSettings {
showPronouns: boolean
}
export interface NovelMember {
userId: string
email: string
@@ -197,11 +201,8 @@ export interface Character {
appearance: string | null
personality: string | null
backstory: string | null
want: string | null
need: string | null
internalConflict: string | null
externalConflict: string | null
arcSummary: string | null
motivation: string | null
conflict: string | null
voice: string | null
notes: string | null
aliases: string[]
@@ -9,6 +9,7 @@ import {
useNovel,
useRemoveRelationship,
useTags,
useUiSettings,
useUnlinkCharacterIdentity,
useUpdateCharacter,
} from '../api/hooks'
@@ -79,6 +80,7 @@ function CharacterSheet({
canDelete: boolean
}) {
const navigate = useNavigate()
const { data: uiSettings } = useUiSettings()
const { data: allTags } = useTags(novelId)
const { data: allCharacters } = useCharacters(novelId)
const { data: chapters } = useChapters(novelId)
@@ -125,12 +127,14 @@ function CharacterSheet({
<div className="grid gap-4 sm:grid-cols-3">
<AutoField label="Age" value={character.age} onCommit={(age) => patch({ age })} readOnly={!canWrite} />
<AutoField
label="Pronouns"
value={character.pronouns}
onCommit={(pronouns) => patch({ pronouns })}
readOnly={!canWrite}
/>
{uiSettings?.showPronouns && (
<AutoField
label="Pronouns"
value={character.pronouns}
onCommit={(pronouns) => patch({ pronouns })}
readOnly={!canWrite}
/>
)}
<AutoField
label="Occupation"
value={character.occupation}
@@ -139,13 +143,7 @@ function CharacterSheet({
/>
</div>
<div className="mt-5 grid gap-4 sm:grid-cols-2">
<TagEditor
label="Tags"
tags={character.tags}
suggestions={allTags?.map((t) => t.name) ?? []}
onChange={(tags) => canWrite && patch({ tags })}
/>
<div className="mt-5">
<AliasEditor
label="Also known as"
aliases={character.aliases}
@@ -156,42 +154,20 @@ function CharacterSheet({
<div className="mt-6 grid gap-4 lg:grid-cols-2">
<AutoField
label="Wants"
value={character.want}
label="Motivation"
value={character.motivation}
multiline
rows={3}
placeholder="What they are consciously chasing."
onCommit={(want) => patch({ want })}
placeholder="What they consciously want, and what they need instead."
onCommit={(motivation) => patch({ motivation })}
readOnly={!canWrite}
/>
<AutoField
label="Needs"
value={character.need}
label="Conflict"
value={character.conflict}
multiline
rows={3}
placeholder="What the story will make them face instead."
onCommit={(need) => patch({ need })}
readOnly={!canWrite}
/>
<AutoField
label="Internal conflict"
value={character.internalConflict}
multiline
onCommit={(internalConflict) => patch({ internalConflict })}
readOnly={!canWrite}
/>
<AutoField
label="External conflict"
value={character.externalConflict}
multiline
onCommit={(externalConflict) => patch({ externalConflict })}
readOnly={!canWrite}
/>
<AutoField
label="Arc"
value={character.arcSummary}
multiline
onCommit={(arcSummary) => patch({ arcSummary })}
placeholder="The war inside them, and what in the world opposes them."
onCommit={(conflict) => patch({ conflict })}
readOnly={!canWrite}
/>
<AutoField
@@ -304,6 +280,15 @@ function CharacterSheet({
canDelete={canDelete}
/>
<section id="character-tags" className="card mt-6 p-5">
<TagEditor
label="Tags"
tags={character.tags}
suggestions={allTags?.map((t) => t.name) ?? []}
onChange={(tags) => canWrite && patch({ tags })}
/>
</section>
{confirmingDelete && (
<ConfirmModal
title="Delete character"