import { LocalInput, LocalSelect } from "@quixos/camino-react/ui"; import { ColorPicker, FontPicker, NumberControl, Options, Switch, UnderlinePreview } from "./controls.js"; import type { ComponentType } from "react"; import type { AnnotationRender } from "./render-hooks.js"; import { configDefaults, type ConfigValue, type ConfigValues, type ConfigNode } from "./config-schema.js"; export type { ConfigValue, ConfigValues, ConfigField, ConfigNode } from "./config-schema.js"; export type AnnotatorConfigProps = { fields: ConfigNode[]; values: ConfigValues; disabled: boolean; change: (key: string, value: ConfigValue) => void; }; // Authors may install a purpose-built inline component here. The component edits // the same validated processor options as the generated controls; it does not // own persistence or model calls. Every control, including appearance, is opt-in. export const annotatorConfigViews: Record> = { pinyin: ({ fields, values, disabled, change }) => (
change("minimumLevel", event.target.value)} /> {Array.from({ length: 10 }, (_, level) => (
{Array.from({ length: 10 }, (_, level) => ( {level || "All"} ))}
{Number(values.minimumLevel ?? 2) ? `Above HSK ${values.minimumLevel ?? 2}` : "All words"} field.key !== "minimumLevel")} values={values} disabled={disabled} change={change} />
), }; export function configValues(raw: string): ConfigValues { try { const value = JSON.parse(raw); return value && typeof value === "object" && !Array.isArray(value) ? value : {}; } catch { return {}; } } export function configDefinition(raw: string): { fields: ConfigNode[]; description: string; cache?: { identity: string; inputs: string[]; previousRevisions?: string[] }; renders?: AnnotationRender[]; } { try { const value = JSON.parse(raw); return { fields: value.fields ?? [], description: value.description ?? "", cache: value.cache, renders: value.renders, }; } catch { return { fields: [], description: "" }; } } export const configFields = (raw: string) => configDefinition(raw).fields; export function CustomConfig({ processor, fields, values, change, disabled, }: AnnotatorConfigProps & { processor: string }) { const Custom = annotatorConfigViews[processor]; if (Custom) return ; return (
{fields.map((field) => field.kind === "group" ? (
{field.label} change(field.key, { ...configDefaults(field.fields), ...(typeof values[field.key] === "object" ? (values[field.key] as ConfigValues) : {}), [key]: value, }) } />
) : (
{field.label} {field.presentation === "font" ? ( change(field.key, value)} /> ) : field.kind === "select" ? ( ({ ...option, ...(field.presentation === "underline" ? { preview: } : {}), }))} disabled={disabled} change={(value) => change(field.key, value)} /> ) : field.kind === "toggle" ? ( change(field.key, value)} /> ) : field.kind === "color" ? ( change(field.key, value)} /> ) : field.kind === "number" ? ( change(field.key, value)} /> ) : ( change(field.key, e.target.value)} /> )}
), )}
); } // Creation suggestions only: these are never injected into a built annotator. export const fontFields: ConfigNode[] = [ { key: "fontFamily", label: "Annotation font", kind: "text", presentation: "font", default: "" }, { key: "fontSize", label: "Annotation font size", kind: "number", default: 13, min: 6, max: 72 }, ]; export const underlineFields: ConfigNode[] = [ { key: "underlinePattern", label: "Underline pattern", kind: "select", presentation: "underline", default: "solid", options: ["solid", "dashed", "squiggly"].map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1), })), }, ];