Finish Phase 22: the half of Petal that works with the tunnel down
Grammar lite, the false-friend list, the daily invitation and the offline miscollocations — the four remaining §5–§6 items, all client-side and all alive on a box that cannot reach the model. The offline collocations forced a schema change. `type` had been doubling as the answer to "which engine found this" — `mechanics` meant offline — and that stops being true the moment an offline rule proposes a collocation. Migration 0013 adds `source` (llm | local) and every pass now scopes its DELETE by engine; without it the coach silently wiped every offline chunk on the page. Existing rows backfill by type, so a pre-0013 collocation row is claimed as the coach's, which it was: the offline list did not exist yet. The rule pack is hand-curated rather than mined, and the entries left out are the point — `married with` is wrong until "married with children", `arrive to` wants at or in depending on the noun. A pack running on every keystroke must not correct correct writing. Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
This commit is contained in:
@@ -13,6 +13,11 @@ interface Props {
|
||||
editTick: number
|
||||
acceptTick: number
|
||||
text: string
|
||||
// The open document is still empty — the one state the daily writing
|
||||
// invitation is offered in.
|
||||
blankPage: boolean
|
||||
// Called when she takes the invitation up, with the English prompt.
|
||||
onAcceptInvitation: (prompt: string) => void
|
||||
}
|
||||
|
||||
// Emoji placeholder per mood, used for any mood a companion has no Lottie for.
|
||||
@@ -30,17 +35,38 @@ const STORAGE_KEY = 'petal.companion'
|
||||
// useCompanion and shows a Mandarin-first speech bubble for cheers, tips, and
|
||||
// break reminders. Clicking the mascot opens a picker to switch companions
|
||||
// (the choice persists in localStorage).
|
||||
export function PetalCompanion({ wordCount, saveStatus, llmDown, editTick, acceptTick, text }: Props) {
|
||||
export function PetalCompanion({
|
||||
wordCount,
|
||||
saveStatus,
|
||||
llmDown,
|
||||
editTick,
|
||||
acceptTick,
|
||||
text,
|
||||
blankPage,
|
||||
onAcceptInvitation,
|
||||
}: Props) {
|
||||
const t = usePack()
|
||||
const { mood, bubble, dismiss, holdBubble, releaseBubble } = useCompanion({
|
||||
const {
|
||||
mood,
|
||||
bubble,
|
||||
dismiss,
|
||||
holdBubble,
|
||||
releaseBubble,
|
||||
acceptInvite,
|
||||
declineInvite,
|
||||
setInviteHandler,
|
||||
} = useCompanion({
|
||||
wordCount,
|
||||
saveStatus,
|
||||
llmDown,
|
||||
editTick,
|
||||
acceptTick,
|
||||
text,
|
||||
blankPage,
|
||||
})
|
||||
|
||||
useEffect(() => setInviteHandler(onAcceptInvitation), [setInviteHandler, onAcceptInvitation])
|
||||
|
||||
const [companionId, setCompanionId] = useState<string>(
|
||||
() => readPref(STORAGE_KEY) || DEFAULT_COMPANION,
|
||||
)
|
||||
@@ -179,6 +205,36 @@ export function PetalCompanion({ wordCount, saveStatus, llmDown, editTick, accep
|
||||
>
|
||||
{bubble.en}
|
||||
</p>
|
||||
|
||||
{/* The daily invitation's two answers. "Not today" is a real button
|
||||
sitting level with the other one, not a small grey escape — a no
|
||||
that has to be hunted for isn't much of a no. */}
|
||||
{bubble.invite && (
|
||||
<div className="mt-3 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
acceptInvite(bubble.invite!.prompt)
|
||||
}}
|
||||
className="rounded-full px-3.5 py-1.5 text-sm font-bold"
|
||||
style={{ background: 'var(--color-accent)', color: 'var(--color-plum)' }}
|
||||
>
|
||||
{t.companion.inviteAccept}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
declineInvite()
|
||||
}}
|
||||
className="rounded-full px-3.5 py-1.5 text-sm font-semibold"
|
||||
style={{ background: 'var(--color-surface-alt)', color: 'var(--color-plum)' }}
|
||||
>
|
||||
{t.companion.inviteDecline}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user