page.tsx still owns the chat main chain, but the first product surfaces now live in their own modules so later splits can land without editing the 4k-line Home. Source-lock tests follow the moved tokens; the orphan user-data contract is aligned and added to the quick gate. Co-authored-by: Cursor <cursoragent@cursor.com>
39 lines
1.6 KiB
TypeScript
39 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import { BirthTimeIntakeFields } from "@/components/birth-time-intake";
|
|
import { BirthLocationFields } from "@/components/birth-location-fields";
|
|
import { CharacterRemaining } from "@/components/character-remaining";
|
|
import { characterRemainingVisible } from "@/lib/character-remaining";
|
|
import { applyBirthTimeDraftPatch } from "@/lib/birth-time-intake-model";
|
|
import { invalidateCandidateAfterLocationChange } from "@/lib/home-profile";
|
|
import type { Profile } from "@/lib/home-types";
|
|
|
|
export function ProfileFields({ value, onChange, nameInputId }: { value: Profile; onChange: (profile: Profile) => void; nameInputId?: string }) {
|
|
const nameRemainingId = `${nameInputId ?? "profile-name"}-remaining`;
|
|
return (
|
|
<>
|
|
<label>
|
|
<span className="character-remaining-row">
|
|
如何称呼你
|
|
<CharacterRemaining id={nameRemainingId} length={value.name.length} maxLength={80} />
|
|
</span>
|
|
<input
|
|
id={nameInputId}
|
|
required
|
|
autoComplete="name"
|
|
maxLength={80}
|
|
placeholder="例如:林遥"
|
|
value={value.name}
|
|
aria-describedby={characterRemainingVisible(value.name.length, 80) ? nameRemainingId : undefined}
|
|
onChange={(event) => onChange({ ...value, name: event.target.value })}
|
|
/>
|
|
</label>
|
|
<BirthTimeIntakeFields value={value} onPatch={(patch) => onChange(applyBirthTimeDraftPatch(value, patch))} />
|
|
<BirthLocationFields
|
|
value={value}
|
|
onChange={(next) => onChange(invalidateCandidateAfterLocationChange(value, next))}
|
|
/>
|
|
</>
|
|
);
|
|
}
|