import { useEffect, useState } from "react"; import { defineRenderer } from "@nativepi/extension-api"; import type { RendererContext } from "@nativepi/extension-api"; import { FieldError, SettingsSelectRow } from "@nativepi/extension-api/ui"; import { titleGeneratorProtocol, type TitleGeneratorState, } from "../types.ts"; function TitleGeneratorSetting({ context, }: { context: RendererContext; }) { const { call, on } = context.channel; const [state, setState] = useState(null); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; const apply = (value: TitleGeneratorState) => { if (!cancelled) setState(value); }; void call("state") .then(apply) .catch((reason: unknown) => { if (!cancelled) setError(reason instanceof Error ? reason.message : String(reason)); }); const off = on("changed", apply); return () => { cancelled = true; off(); }; }, [call, on]); const choose = (modelSetting: string) => { if (!state || saving || modelSetting === state.modelSetting) return; setSaving(true); setError(null); void call("set", { modelSetting }) .then(setState) .catch(() => setError("Unable to change the title model. Try again.")) .finally(() => setSaving(false)); }; return ( <> ({ value: key, label, }))} onChange={choose} disabled={!state || saving} /> {error ? {error} : null} ); } export default defineRenderer({ apiVersion: 1, protocol: titleGeneratorProtocol, settings: [ { id: "title-generator", heading: "Chat titles", description: "Pi creates a concise title from the first message. Choose a smaller model when you want that request to cost less.", render: (context) => , }, ], });