sanity-plugin-jev-fields 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["isRecord","isRecord","isRecord","LEVELS","isRecord","\"Authorization\""],"sources":["../src/content.ts","../src/context.tsx","../src/look.tsx","../src/names.ts","../src/kinds/kind.ts","../src/kinds/choice.tsx","../src/i18n.ts","../src/kinds/noul.tsx","../src/kinds/score.tsx","../src/kinds/index.ts","../src/signals.ts","../src/answers.ts","../src/secrets.tsx","../src/ui.tsx","../src/evaluate.ts","../src/lifecycle.ts","../src/useSignals.ts","../src/AttachedInput.tsx","../src/JevTool.tsx","../src/index.tsx"],"sourcesContent":["/** Flattens a field's value into the plain text Jev evaluates. */\nexport function toText(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (Array.isArray(value)) {\n // Portable Text blocks become paragraphs; other arrays become lines.\n const separator = value.some(isBlock) ? '\\n\\n' : '\\n'\n return value\n .map(toText)\n .filter((text) => text !== '')\n .join(separator)\n }\n if (isRecord(value)) {\n if (isBlock(value)) return value.children.map((child) => child.text ?? '').join('')\n if (value._type === 'slug') return toText(value.current)\n return Object.entries(value)\n .filter(([key]) => !key.startsWith('_'))\n .map(([, child]) => toText(child))\n .filter((text) => text !== '')\n .join('\\n')\n }\n return ''\n}\n\nfunction isBlock(value: unknown): value is {_type: 'block'; children: {text?: string}[]} {\n return isRecord(value) && value._type === 'block' && Array.isArray(value.children)\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\n/** FNV-1a: a small, stable fingerprint; collisions only cost a skipped re-evaluation. */\nexport function fingerprint(input: unknown): string {\n const text = JSON.stringify(input)\n let hash = 0x811c9dc5\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i)\n hash = Math.imul(hash, 0x01000193)\n }\n return (hash >>> 0).toString(16).padStart(8, '0')\n}\n\n/** Jev reads at most this many tokens of state (plus the longest question) per request. */\nexport const STATE_TOKEN_LIMIT = 32_000\n\n/** A rough token count: about four characters per token for English text. */\nexport const estimateTokens = (text: string) => Math.ceil(text.length / 4)\n\n/** True when a field's text is close enough to Jev's limit that evaluations may be refused. */\nexport const nearTokenLimit = (text: string) => estimateTokens(text) > STATE_TOKEN_LIMIT * 0.875\n","import {createContext, useContext, useEffect, useMemo, useState, type ReactNode} from 'react'\nimport {set, type FieldProps, type Path} from 'sanity'\n\nimport type {JevPluginConfig} from './types'\n\ntype Write = (value: unknown) => void\n\ninterface JevFormContext {\n config: JevPluginConfig\n /** Writers for answer fields, by path. Each answer field registers its own `onChange`. */\n writers: Map<string, Write>\n}\n\nconst Context = createContext<JevFormContext | null>(null)\n\nexport const pathKey = (path: Path) => JSON.stringify(path)\n\n/** Wraps a document form, so attached fields can reach the plugin config and answer fields. */\nexport function JevFormProvider({\n config,\n children,\n}: {\n config: JevPluginConfig\n children: ReactNode\n}) {\n const [writers] = useState(() => new Map<string, Write>())\n const value = useMemo(() => ({config, writers}), [config, writers])\n return <Context.Provider value={value}>{children}</Context.Provider>\n}\n\nexport function useJevForm(): JevFormContext {\n const context = useContext(Context)\n if (!context) throw new Error('Jev signals need the jev() plugin in sanity.config.')\n return context\n}\n\n/**\n * Field component for answer fields: shows nothing (answers appear on the attached field) and\n * registers the field's own `onChange`, so the attached field can store answers through it.\n */\nexport function AnswerField(props: FieldProps) {\n const {writers} = useJevForm()\n const key = pathKey(props.path)\n const onChange = props.inputProps.onChange\n useEffect(() => {\n writers.set(key, (value) => onChange(set(value)))\n return () => {\n writers.delete(key)\n }\n }, [writers, key, onChange])\n return null\n}\n","import {Box} from '@sanity/ui'\n\nexport type Tone = 'critical' | 'caution' | 'positive' | 'primary' | 'default'\n\nexport const NEUTRAL_COLOR = 'hsl(230 80% 66%)'\nexport const MUTED_COLOR = 'hsl(215 12% 55%)'\nconst TRACK_COLOR = 'var(--card-border-color)'\n\nexport const clamp = (value: number) => Math.min(1, Math.max(0, value))\nexport const capitalize = (text: string) => text.charAt(0).toUpperCase() + text.slice(1)\n\n/** Red at 0, amber in the middle, green at 1. */\nexport function trafficColor(fraction: number) {\n return `hsl(${Math.round(clamp(fraction) * 120)} 70% 52%)`\n}\n\nexport function trafficTone(fraction: number): Tone {\n if (fraction < 1 / 3) return 'critical'\n if (fraction < 2 / 3) return 'caution'\n return 'positive'\n}\n\n/** A thin rounded bar. `fraction` is 0–1. */\nexport function Bar({fraction, color, label}: {fraction: number; color: string; label: string}) {\n const percent = Math.round(clamp(fraction) * 100)\n return (\n <Box\n flex={1}\n // A native <meter> can't hold the styled fill below, so this keeps role=\"meter\".\n // oxlint-disable-next-line jsx-a11y/prefer-tag-over-role\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={percent}\n aria-label={label}\n style={{height: 4, borderRadius: 4, overflow: 'hidden', background: TRACK_COLOR}}\n >\n <div\n style={{\n width: `${percent}%`,\n height: '100%',\n borderRadius: 4,\n background: color,\n transition: 'width 400ms ease, background-color 400ms ease',\n }}\n />\n </Box>\n )\n}\n","/** Schema type names. Stored as `_type` on every value, so frontends query them: keep stable. */\nexport const TYPE_NAMES = {\n noul: 'jev.noul',\n score: 'jev.score',\n choice: 'jev.choice',\n choiceProbability: 'jev.choiceProbability',\n} as const\n\nexport type JevTypeName = (typeof TYPE_NAMES)['noul' | 'score' | 'choice']\n","import type {ReactNode} from 'react'\nimport {defineField} from 'sanity'\n\nimport {isRecord} from '../content'\nimport type {JevAnswer, JevQuestion} from '../evaluate'\nimport type {Tone} from '../look'\nimport type {ChoiceValue} from './choice'\nimport type {NoulValue} from './noul'\nimport type {ScoreValue} from './score'\n\nexport interface SignalBase {\n /** Chip and detail heading. Defaults to the signal's key, e.g. `readable` → \"Readable\". */\n title?: string\n /** The question Jev answers about the attached field. */\n instructions: string\n}\n\nexport interface EvaluatedValue {\n evaluatedAt?: string\n /** The model that answered, as AI Gateway names it, e.g. `typesafe-ai/jev`. */\n model?: string\n /** Fingerprint of the evaluated content and question, used to detect stale results. */\n sourceHash?: string\n}\n\n/** Bounds on a probability (Noul, 0–1) or a score (0 to the top criterion). @public */\nexport interface RangeRule {\n atLeast?: number\n atMost?: number\n}\n\n/** `warn` gives a warning, `require` an error that blocks publishing. */\nexport type RuleLevel = 'warn' | 'require'\n\n/** A stored answer of any kind, as written to its answer field. */\nexport type StoredValue = NoulValue | ScoreValue | ChoiceValue\n\n/** How high a noul's probability is, shown as a translated badge. */\nexport type Level = 'low' | 'medium' | 'high'\n\n/** A stored answer as an editor sees it. */\nexport interface Reading {\n value: StoredValue\n chip: {text: string; color: string}\n /** Tone of the detail card: only answers worth acting on colour it. */\n tone: Tone\n /** Right of the detail heading: a badge, or what the answer means. */\n aside?: {badge: string; tone: Tone} | {level: Level; tone: Tone} | {note: string}\n /** Body of the detail card. */\n body: ReactNode\n}\n\n/** A signal bound to its kind (Noul, Score or Choice): everything that differs between them. */\nexport interface Kind {\n /** Schema type of the field that stores the answer, e.g. `jev.score`. */\n typeName: string\n /** The question sent to Jev. Absent when the signal's config can't be asked: see `problem`. */\n question?: JevQuestion\n problem?: string\n /** The value to store for Jev's answer to `question`, without the bookkeeping fields. */\n toStored(answer: JevAnswer): StoredValue\n /** Reads a stored value. Anything that isn't a complete answer of this kind is unanswered. */\n read(stored: unknown): Reading | undefined\n /**\n * What is wrong with a stored answer, judged by the signal's `warn` or `require` rule.\n * Nothing when the rule holds, the signal has no such rule, or there is no answer.\n */\n check(level: RuleLevel, stored: unknown, title: string): string | undefined\n}\n\nexport const round = (value: number, digits = 4) => Math.round(value * 10 ** digits) / 10 ** digits\n\nexport function mismatch(): never {\n throw new Error('Jev answered a different kind of question.')\n}\n\nexport const bookkeepingFields = [\n defineField({name: 'evaluatedAt', type: 'datetime', readOnly: true}),\n defineField({name: 'model', type: 'string', readOnly: true}),\n defineField({name: 'sourceHash', type: 'string', hidden: true}),\n]\n\n/** The stored value as a record of the given `_type`, or nothing. */\nexport function storedOf(stored: unknown, typeName: string): Record<string, unknown> | undefined {\n return isRecord(stored) && stored._type === typeName ? stored : undefined\n}\n\n/** The bookkeeping fields a stored value has. */\nexport function bookkeepingOf(stored: Record<string, unknown>): EvaluatedValue {\n return {\n evaluatedAt: text(stored.evaluatedAt),\n model: text(stored.model),\n sourceHash: text(stored.sourceHash),\n }\n}\n\nexport const text = (value: unknown) => (typeof value === 'string' ? value : undefined)\nexport const number = (value: unknown) => (typeof value === 'number' ? value : undefined)\n\n/** Why a range rule is invalid for values from `min` to `max`, if it is. */\nexport function rangeRuleProblem(\n rules: Partial<Record<RuleLevel, RangeRule>>,\n min: number,\n max: number,\n): string | undefined {\n for (const level of ['warn', 'require'] as const) {\n const rule = rules[level]\n if (!rule) continue\n for (const bound of ['atLeast', 'atMost'] as const) {\n const value = rule[bound]\n if (value !== undefined && (typeof value !== 'number' || value < min || value > max)) {\n return `\\`${level}.${bound}\\` must be a number from ${min} to ${max}.`\n }\n }\n }\n return undefined\n}\n\n/** How `value` breaks a range rule, e.g. `below 60%`, using `format` for both numbers. */\nexport function rangeViolation(\n value: number,\n rule: RangeRule | undefined,\n format: (value: number) => string,\n): string | undefined {\n if (rule?.atLeast !== undefined && value < rule.atLeast) return `below ${format(rule.atLeast)}`\n if (rule?.atMost !== undefined && value > rule.atMost) return `above ${format(rule.atMost)}`\n return undefined\n}\n","import {Box, Flex, Stack, Text} from '@sanity/ui'\nimport {defineArrayMember, defineField, defineType} from 'sanity'\n\nimport {isRecord} from '../content'\nimport {AnswerField} from '../context'\nimport {Bar, capitalize, MUTED_COLOR, NEUTRAL_COLOR} from '../look'\nimport {TYPE_NAMES} from '../names'\nimport {\n bookkeepingFields,\n bookkeepingOf,\n mismatch,\n number,\n round,\n storedOf,\n text,\n type EvaluatedValue,\n type Kind,\n type RuleLevel,\n type SignalBase,\n} from './kind'\n\n/** @public */\nexport interface ChoiceSignal extends SignalBase {\n type: 'choice'\n /** Two to 255 options: option name → what it means. */\n criteria: Record<string, string>\n /** Warn unless the answer is one of these options, e.g. `{oneOf: ['formal', 'casual']}`. */\n warn?: ChoiceRule\n /** Block publishing unless the answer is one of these options. */\n require?: ChoiceRule\n}\n\n/** Options an answer must be one of. @public */\nexport interface ChoiceRule {\n oneOf: string[]\n}\n\n/** Why a choice signal can't be asked, if it can't. */\nfunction choiceProblem(signal: ChoiceSignal, options: string[]): string | undefined {\n if (options.length < 2) return 'A choice signal needs at least two options.'\n if (options.length > 255) return 'A choice signal allows at most 255 options.'\n for (const level of ['warn', 'require'] as const satisfies RuleLevel[]) {\n const rule = signal[level]\n if (!rule) continue\n if (!Array.isArray(rule.oneOf) || rule.oneOf.length === 0) {\n return `\\`${level}.oneOf\\` must list at least one option.`\n }\n const unknown = rule.oneOf.find((option) => !options.includes(option))\n if (unknown !== undefined)\n return `\\`${level}.oneOf\\` names \"${unknown}\", which is not an option.`\n }\n return undefined\n}\n\n/** One option from a named set. @public */\nexport const choice = (signal: Omit<ChoiceSignal, 'type'>): ChoiceSignal => ({\n type: 'choice',\n ...signal,\n})\n\ninterface ChoiceProbability {\n _key: string\n _type?: 'jev.choiceProbability'\n option?: string\n probability?: number\n}\n\n/** @public */\nexport interface ChoiceValue extends EvaluatedValue {\n _type?: 'jev.choice'\n choice?: string\n /** The model's confidence in this choice (0–1). */\n confidence?: number\n probabilities?: ChoiceProbability[]\n}\n\nexport const choiceSchemaTypes = [\n // A top-level type rather than an inline array member, so GraphQL can deploy it.\n defineType({\n name: TYPE_NAMES.choiceProbability,\n title: 'Jev choice probability',\n type: 'object',\n fields: [\n defineField({name: 'option', type: 'string'}),\n defineField({name: 'probability', type: 'number'}),\n ],\n }),\n defineType({\n name: TYPE_NAMES.choice,\n title: 'Jev choice',\n type: 'object',\n components: {field: AnswerField},\n fields: [\n defineField({name: 'choice', type: 'string', readOnly: true}),\n defineField({name: 'confidence', type: 'number', readOnly: true}),\n defineField({\n name: 'probabilities',\n type: 'array',\n readOnly: true,\n of: [defineArrayMember({type: TYPE_NAMES.choiceProbability})],\n }),\n ...bookkeepingFields,\n ],\n }),\n]\n\nexport function bindChoice(signal: ChoiceSignal): Kind {\n const criteria = signal.criteria ?? {}\n const problem = choiceProblem(signal, Object.keys(criteria))\n\n return {\n typeName: TYPE_NAMES.choice,\n question: !problem\n ? {type: 'choice', instructions: signal.instructions, criteria: signal.criteria}\n : undefined,\n problem,\n\n toStored(answer) {\n if (answer.type !== 'choice') return mismatch()\n return {\n _type: TYPE_NAMES.choice,\n choice: answer.choice,\n confidence: round(answer.confidence ?? answer.probabilities[answer.choice] ?? 0),\n // Index keys can't collide, unlike keys derived from option names.\n probabilities: Object.keys(criteria).map((option, index) => ({\n _key: `option-${index}`,\n _type: TYPE_NAMES.choiceProbability,\n option,\n probability: round(answer.probabilities[option] ?? 0),\n })),\n }\n },\n\n read(stored) {\n const record = storedOf(stored, TYPE_NAMES.choice)\n if (!record || typeof record.choice !== 'string') return undefined\n const probabilities = Array.isArray(record.probabilities)\n ? record.probabilities.filter(isRecord).map((entry): ChoiceProbability => ({\n _key: String(entry._key),\n _type: TYPE_NAMES.choiceProbability,\n option: text(entry.option),\n probability: number(entry.probability),\n }))\n : []\n const meaning = criteria[record.choice]\n return {\n value: {\n ...bookkeepingOf(record),\n _type: TYPE_NAMES.choice,\n choice: record.choice,\n confidence: number(record.confidence),\n probabilities,\n },\n chip: {text: capitalize(record.choice), color: NEUTRAL_COLOR},\n tone: 'default',\n aside: meaning ? {note: meaning} : undefined,\n body: <ChoiceBody signal={signal} choice={record.choice} probabilities={probabilities} />,\n }\n },\n\n check(level, stored, title) {\n const record = storedOf(stored, TYPE_NAMES.choice)\n const rule = signal[level]\n if (!record || typeof record.choice !== 'string' || !rule?.oneOf) return undefined\n if (rule.oneOf.includes(record.choice)) return undefined\n return `${title} is \"${record.choice}\", not ${rule.oneOf.map((option) => `\"${option}\"`).join(' or ')}.`\n },\n }\n}\n\nfunction ChoiceBody({\n signal,\n choice,\n probabilities,\n}: {\n signal: ChoiceSignal\n choice: string\n probabilities: ChoiceProbability[]\n}) {\n const options = Object.keys(signal.criteria)\n const nameWidth = `${Math.max(...options.map((name) => name.length), 4) + 2}ch`\n return (\n <Stack gap={3}>\n {options.map((option) => {\n const probability = probabilities.find((entry) => entry.option === option)?.probability ?? 0\n const winner = option === choice\n const percent = Math.round(probability * 100)\n return (\n <Flex key={option} align=\"center\" gap={3}>\n <Box style={{width: nameWidth}}>\n <Text size={1} weight={winner ? 'semibold' : 'regular'} muted={!winner}>\n {capitalize(option)}\n </Text>\n </Box>\n <Bar\n fraction={probability}\n color={winner ? NEUTRAL_COLOR : MUTED_COLOR}\n label={`${option}: ${percent}%`}\n />\n <Box style={{minWidth: '4ch', textAlign: 'right'}}>\n <Text size={1} muted={!winner}>\n {percent}%\n </Text>\n </Box>\n </Flex>\n )\n })}\n </Stack>\n )\n}\n","import {defineLocaleResourceBundle} from 'sanity'\n\n/** The plugin's translation namespace: `useTranslation(JEV_NAMESPACE)`. */\nexport const JEV_NAMESPACE = 'jev' as const\n\n/**\n * Every string an editor sees, in US English. Other locales can add a bundle for the `jev`\n * namespace with the same keys. Config problems and validation messages stay in English: they\n * are for schema authors, and Sanity's validation has no translation hook.\n */\nexport const resources = {\n 'strip.label': 'Jev',\n 'strip.set-up': 'Set up Jev',\n 'strip.evaluate-all': 'Evaluate all signals now',\n 'chip.empty': '–',\n 'chip.error': 'Error',\n 'chip.out-of-date': 'Out of date',\n\n 'detail.empty-field': 'Add content to this field to evaluate it.',\n 'detail.not-evaluated': 'Not evaluated yet.',\n 'detail.out-of-date': 'Out of date: the field changed since this was evaluated.',\n 'detail.update-key': 'Update API key',\n 'detail.try-again': 'Try again',\n 'field.too-long':\n 'This field is about {{tokens}}k tokens long. Jev reads at most {{limit}}k per question, so longer text may be refused.',\n\n 'error.auth': 'AI Gateway rejected the API key. Check the key in the Jev tool.',\n 'error.invalid': 'AI Gateway rejected the question: {{detail}}',\n 'error.busy': 'AI Gateway is busy. Try again in a moment.',\n 'error.failed': 'AI Gateway responded with {{status}}.',\n 'error.failed-with-detail': 'AI Gateway responded with {{status}}: {{detail}}',\n 'error.unexpected': 'AI Gateway returned no answer.',\n\n 'noul.level.low': 'Low',\n 'noul.level.medium': 'Medium',\n 'noul.level.high': 'High',\n 'noul.yes': 'Yes',\n 'score.summary': '{{meaning}} (score {{score}} of {{max}}).',\n 'score.next': 'To move up: {{next}}.',\n\n 'key-dialog.title': 'AI Gateway API key',\n 'key-dialog.label': 'API key',\n 'key-dialog.description':\n 'Create one in the Vercel dashboard under AI Gateway → API Keys, ideally with a spend limit. It is stored in this dataset, so Studio users and API tokens that can read the dataset can see it.',\n 'key-dialog.cancel': 'Cancel',\n 'key-dialog.save': 'Save',\n 'key-dialog.saving': 'Saving…',\n 'key-dialog.save-failed': 'Could not save the key: {{error}}',\n\n 'tool.title': 'Jev',\n 'tool.intro': 'Jev signals are answered by TypeSafe’s Jev model through Vercel AI Gateway.',\n 'tool.key.heading': 'AI Gateway API key',\n 'tool.key.status.config': 'From plugin config',\n 'tool.key.status.checking': 'Checking',\n 'tool.key.status.set': 'Set',\n 'tool.key.status.not-set': 'Not set',\n 'tool.key.from-transport':\n 'Requests go through the transport in the plugin config, so no key is needed here.',\n 'tool.key.from-config':\n 'The key comes from apiKey in the plugin config. A key stored here is ignored.',\n 'tool.key.ends-with': 'Key ending in <Code>{{last4}}</Code>',\n 'tool.key.changed': 'Last changed {{date}}',\n 'tool.key.checking': 'Checking for a stored key…',\n 'tool.key.none': 'No key is stored, so Jev signals cannot evaluate yet.',\n 'tool.key.set': 'Set key',\n 'tool.key.change': 'Change key',\n 'tool.key.remove': 'Remove key',\n 'tool.key.remove-confirm': 'Remove the key? Jev signals stop evaluating until a new one is set.',\n 'tool.key.remove-cancel': 'Cancel',\n 'tool.key.remove-confirm-button': 'Remove',\n 'tool.key.remove-failed': 'Could not remove the key: {{error}}',\n 'tool.test.run': 'Test connection',\n 'tool.test.running': 'Testing…',\n 'tool.test.passed': 'Connection works. {{model}} answered.',\n 'tool.visibility.heading': 'Who can see the key',\n 'tool.visibility.body':\n 'The key is stored in this dataset in the document <Code>secrets.jev</Code>. It is not public, but anyone who can read the dataset can see it: every Studio user, and every API token with read access, such as a frontend’s read or preview token. It is also included in dataset exports and backups. Use a dedicated key with a spend limit. If editors and tokens must never see the key, set a <Code>transport</Code> that sends requests through your own server.',\n} as const\n\n/** A key of the plugin's translations. */\nexport type JevTranslationKey = keyof typeof resources\n\nexport const jevLocaleBundle = defineLocaleResourceBundle({\n locale: 'en-US',\n namespace: JEV_NAMESPACE,\n resources,\n})\n","import {Flex, Stack, Text} from '@sanity/ui'\nimport {defineField, defineType, useTranslation} from 'sanity'\n\nimport {AnswerField} from '../context'\nimport {JEV_NAMESPACE} from '../i18n'\nimport {Bar, trafficColor, trafficTone, type Tone} from '../look'\nimport {TYPE_NAMES} from '../names'\nimport {\n bookkeepingFields,\n bookkeepingOf,\n mismatch,\n rangeRuleProblem,\n rangeViolation,\n round,\n storedOf,\n type EvaluatedValue,\n type Kind,\n type Level,\n type RangeRule,\n type SignalBase,\n} from './kind'\n\n/** @public */\nexport interface NoulSignal extends SignalBase {\n type: 'noul'\n /** What a \"yes\" means. Shown when the probability is 50% or higher. */\n true: string\n /** What a \"no\" means. Shown when the probability is below 50%. */\n false: string\n /** Short phrase after the percentage, e.g. \"likely to read easily\". */\n label?: string\n /** Warn when the probability is outside these bounds (0–1), e.g. `{atLeast: 0.6}`. */\n warn?: RangeRule\n /** Block publishing when the probability is outside these bounds (0–1). */\n require?: RangeRule\n}\n\n/** A yes/no signal: the chip shows the probability that the answer is yes. @public */\nexport const noul = (signal: Omit<NoulSignal, 'type'>): NoulSignal => ({type: 'noul', ...signal})\n\n/** @public */\nexport interface NoulValue extends EvaluatedValue {\n _type?: 'jev.noul'\n /** Probability (0–1) that the answer is yes. */\n probability?: number\n}\n\nexport const noulSchemaTypes = [\n defineType({\n name: TYPE_NAMES.noul,\n title: 'Jev yes/no',\n type: 'object',\n components: {field: AnswerField},\n fields: [\n defineField({name: 'probability', type: 'number', readOnly: true}),\n ...bookkeepingFields,\n ],\n }),\n]\n\nconst LEVELS: Partial<Record<Tone, Level>> = {critical: 'low', caution: 'medium', positive: 'high'}\n\nexport function bindNoul(signal: NoulSignal): Kind {\n const problem =\n !signal.true?.trim() || !signal.false?.trim()\n ? 'A noul signal needs both `true` and `false` text.'\n : rangeRuleProblem(signal, 0, 1)\n return {\n typeName: TYPE_NAMES.noul,\n // Jev's API and the Gateway call a noul \"boolean\".\n question: !problem\n ? {\n type: 'boolean',\n instructions: signal.instructions,\n criteria: {true: signal.true, false: signal.false},\n }\n : undefined,\n problem,\n\n toStored(answer) {\n if (answer.type !== 'boolean') return mismatch()\n return {_type: TYPE_NAMES.noul, probability: round(answer.probability)}\n },\n\n read(stored) {\n const record = storedOf(stored, TYPE_NAMES.noul)\n if (!record || typeof record.probability !== 'number') return undefined\n const probability = record.probability\n const tone = trafficTone(probability)\n return {\n value: {...bookkeepingOf(record), _type: TYPE_NAMES.noul, probability},\n chip: {text: `${Math.round(probability * 100)}%`, color: trafficColor(probability)},\n tone: tone === 'positive' ? 'default' : tone,\n aside: {level: LEVELS[tone] ?? 'medium', tone},\n body: <NoulBody signal={signal} probability={probability} />,\n }\n },\n\n check(level, stored, title) {\n const record = storedOf(stored, TYPE_NAMES.noul)\n if (!record || typeof record.probability !== 'number') return undefined\n const percent = (value: number) => `${Math.round(value * 100)}%`\n const broken = rangeViolation(record.probability, signal[level], percent)\n return broken && `${title} is ${percent(record.probability)}, ${broken}.`\n },\n }\n}\n\nfunction NoulBody({signal, probability}: {signal: NoulSignal; probability: number}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const percent = Math.round(probability * 100)\n return (\n <Stack gap={4}>\n <Flex align=\"baseline\" gap={2}>\n <Text size={4} weight=\"medium\">\n {percent}%\n </Text>\n {signal.label && (\n <Text size={1} muted>\n {signal.label}\n </Text>\n )}\n </Flex>\n <Bar\n fraction={probability}\n color={trafficColor(probability)}\n label={`${signal.title ?? t('noul.yes')}: ${percent}%`}\n />\n <Text size={1} muted>\n {probability >= 0.5 ? signal.true : signal.false}\n </Text>\n </Stack>\n )\n}\n","import {Flex, Stack, Text} from '@sanity/ui'\nimport {defineField, defineType, useTranslation} from 'sanity'\n\nimport {AnswerField} from '../context'\nimport {JEV_NAMESPACE} from '../i18n'\nimport {Bar, capitalize, NEUTRAL_COLOR, trafficColor, trafficTone, type Tone} from '../look'\nimport {TYPE_NAMES} from '../names'\nimport {\n bookkeepingFields,\n bookkeepingOf,\n mismatch,\n number,\n rangeRuleProblem,\n rangeViolation,\n round,\n storedOf,\n text,\n type EvaluatedValue,\n type Kind,\n type RangeRule,\n type SignalBase,\n} from './kind'\n\n/** @public */\nexport interface ScoreSignal extends SignalBase {\n type: 'score'\n /**\n * Two to ten criteria, lowest first. Text before a colon becomes the short label,\n * e.g. `'anecdotal: personal experience only'`.\n */\n criteria: string[]\n /**\n * `traffic` (default): red at the bottom, green at the top.\n * `reverse`: green at the bottom, for scales where lower is better (e.g. risk).\n * `neutral`: one colour, for scales that aren't good or bad (e.g. reading level).\n */\n colors?: 'traffic' | 'reverse' | 'neutral'\n /** Warn when the score is outside these bounds, as criterion positions: `{atLeast: 2}`. */\n warn?: RangeRule\n /** Block publishing when the score is outside these bounds. */\n require?: RangeRule\n}\n\n/** A position on an ordered scale. @public */\nexport const score = (signal: Omit<ScoreSignal, 'type'>): ScoreSignal => ({\n type: 'score',\n ...signal,\n})\n\n/** @public */\nexport interface ScoreValue extends EvaluatedValue {\n _type?: 'jev.score'\n /** Interpolated position on the scale, from 0 to `max`. */\n score?: number\n /** Index of the top criterion (number of criteria minus one). */\n max?: number\n /** Short label of the nearest criterion. */\n label?: string\n /** The model's confidence in this score (0–1). */\n confidence?: number\n}\n\nexport const scoreSchemaTypes = [\n defineType({\n name: TYPE_NAMES.score,\n title: 'Jev score',\n type: 'object',\n components: {field: AnswerField},\n fields: [\n defineField({name: 'score', type: 'number', readOnly: true}),\n defineField({name: 'max', type: 'number', readOnly: true}),\n defineField({name: 'label', type: 'string', readOnly: true}),\n defineField({name: 'confidence', type: 'number', readOnly: true}),\n ...bookkeepingFields,\n ],\n }),\n]\n\n/** `'anecdotal: personal experience'` → `'anecdotal'`; criteria without a colon are used as-is. */\nexport const shortLabel = (criterion: string | undefined) => criterion?.split(':')[0].trim() ?? ''\n\n/** `'anecdotal: personal experience'` → `'personal experience'`; without a colon, the whole text. */\nexport function meaningOf(criterion: string | undefined): string {\n if (!criterion) return ''\n const colon = criterion.indexOf(':')\n return (colon === -1 ? criterion : criterion.slice(colon + 1)).trim()\n}\n\n/**\n * How full each segment of a score bar is, 0–1. Segment `i` stands for reaching criterion\n * `i`: a score of 0.8 fills the first segment and 80% of the second.\n */\nexport function segmentFills(score: number, count: number): number[] {\n return Array.from({length: count}, (_, index) => Math.min(1, Math.max(0, score - index + 1)))\n}\n\n/** Colour and tone of a score, from the criterion it is nearest to. */\nfunction scoreLook(signal: ScoreSignal, value: number, max: number) {\n if (signal.colors === 'neutral') return {color: NEUTRAL_COLOR, tone: 'primary' as Tone}\n const fraction = max > 0 ? Math.round(value) / max : 0\n const good = signal.colors === 'reverse' ? 1 - fraction : fraction\n return {color: trafficColor(good), tone: trafficTone(good)}\n}\n\nexport function bindScore(signal: ScoreSignal): Kind {\n const {criteria} = signal\n const problem =\n !Array.isArray(criteria) || criteria.length < 2\n ? 'A score signal needs at least two criteria.'\n : criteria.length > 10\n ? 'A score signal allows at most ten criteria.'\n : rangeRuleProblem(signal, 0, criteria.length - 1)\n return {\n typeName: TYPE_NAMES.score,\n question: problem ? undefined : {type: 'score', instructions: signal.instructions, criteria},\n problem,\n\n toStored(answer) {\n if (answer.type !== 'score') return mismatch()\n const max = criteria.length - 1\n const value = Math.min(max, Math.max(0, answer.score))\n return {\n _type: TYPE_NAMES.score,\n score: round(value),\n max,\n label: shortLabel(criteria[Math.round(value)]),\n ...(typeof answer.confidence === 'number' && {confidence: round(answer.confidence)}),\n }\n },\n\n read(stored) {\n const record = storedOf(stored, TYPE_NAMES.score)\n if (!record || typeof record.score !== 'number' || typeof record.max !== 'number') {\n return undefined\n }\n const {score: value, max} = record\n const label = text(record.label)\n const look = scoreLook(signal, value, max)\n return {\n value: {\n ...bookkeepingOf(record),\n _type: TYPE_NAMES.score,\n score: value,\n max,\n label,\n confidence: number(record.confidence),\n },\n chip: {text: `${value.toFixed(1)}/${max}`, color: look.color},\n tone: look.tone === 'critical' || look.tone === 'caution' ? look.tone : 'default',\n aside: {\n badge: capitalize(label ?? shortLabel(criteria?.[Math.round(value)])),\n tone: look.tone,\n },\n body: <ScoreBody signal={signal} score={value} max={max} color={look.color} />,\n }\n },\n\n check(level, stored, title) {\n const record = storedOf(stored, TYPE_NAMES.score)\n if (!record || typeof record.score !== 'number') return undefined\n // `0.6 (anecdotal)`: a position on the scale and the criterion nearest to it.\n const describe = (position: number) => {\n const label = shortLabel(criteria[Math.round(position)])\n const number = Number.isInteger(position) ? String(position) : position.toFixed(1)\n return label ? `${number} (${label})` : number\n }\n const broken = rangeViolation(record.score, signal[level], describe)\n return broken && `${title} is ${describe(record.score)}, ${broken}.`\n },\n }\n}\n\nfunction ScoreBody({\n signal,\n score,\n max,\n color,\n}: {\n signal: ScoreSignal\n score: number\n max: number\n color: string\n}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const nearest = Math.round(score)\n const fills = segmentFills(score, signal.criteria.length)\n const next = signal.criteria[nearest + 1]\n return (\n <Stack gap={4}>\n <Flex gap={1}>\n {signal.criteria.map((criterion, index) => (\n // Criteria may repeat, so their position is the key.\n // oxlint-disable-next-line react/no-array-index-key\n <Stack key={index} gap={2} flex={1}>\n <Bar\n fraction={fills[index]}\n color={color}\n label={`${index}: ${shortLabel(criterion)}`}\n />\n <Text\n size={1}\n weight={index === nearest ? 'medium' : 'regular'}\n muted={index > nearest}\n style={index < nearest ? {color} : undefined}\n textOverflow=\"ellipsis\"\n >\n {index} · {capitalize(shortLabel(criterion))}\n </Text>\n </Stack>\n ))}\n </Flex>\n <Text size={1} muted>\n {t('score.summary', {\n meaning: capitalize(meaningOf(signal.criteria[nearest])),\n score: score.toFixed(1),\n max,\n })}\n {next ? ` ${t('score.next', {next: meaningOf(next)})}` : ''}\n </Text>\n </Stack>\n )\n}\n","import type {JevSignal} from '../signals'\nimport {bindChoice, choiceSchemaTypes} from './choice'\nimport {mismatch, type Kind} from './kind'\nimport {bindNoul, noulSchemaTypes} from './noul'\nimport {bindScore, scoreSchemaTypes} from './score'\n\nexport type {Kind, RangeRule, Reading, RuleLevel, StoredValue} from './kind'\nexport {choice, type ChoiceRule, type ChoiceSignal, type ChoiceValue} from './choice'\nexport {noul, type NoulSignal, type NoulValue} from './noul'\nexport {score, type ScoreSignal, type ScoreValue} from './score'\n\n/** Where every kind stores its answers. */\nexport const KIND_SCHEMA_TYPES = [...noulSchemaTypes, ...scoreSchemaTypes, ...choiceSchemaTypes]\n\n/** Binds a signal to its kind. The one place that tells Noul, Score and Choice apart. */\nexport function kindOf(signal: JevSignal): Kind {\n const kind = bind(signal)\n if (signal.instructions?.trim()) return kind\n return {...kind, question: undefined, problem: 'Add instructions to this signal.'}\n}\n\nfunction bind(signal: JevSignal): Kind {\n switch (signal.type) {\n case 'noul':\n return bindNoul(signal)\n case 'score':\n return bindScore(signal)\n case 'choice':\n return bindChoice(signal)\n default:\n // Config from plain JavaScript can name any type.\n return {\n typeName: 'unknown',\n problem: 'Unknown signal type.',\n toStored: mismatch,\n read: () => undefined,\n check: () => undefined,\n }\n }\n}\n","import type {ChoiceSignal, NoulSignal, ScoreSignal} from './kinds'\n\nexport {choice, noul, score, type ChoiceSignal, type NoulSignal, type ScoreSignal} from './kinds'\n\n/** @public */\nexport type JevSignal = NoulSignal | ScoreSignal | ChoiceSignal\n\n/** `readable` → `Readable`, `readingLevel` → `Reading level`. */\nexport function titleOf(key: string, signal: JevSignal): string {\n if (signal.title) return signal.title\n const words = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ')\n return words.charAt(0).toUpperCase() + words.slice(1).toLowerCase()\n}\n\n/** Signals attached to a field, keyed by the name of the field that stores each answer. */\nexport type JevSignals = Record<string, JevSignal>\n","import {defineField, type Rule, type SchemaTypeDefinition} from 'sanity'\n\nimport {kindOf, type RuleLevel} from './kinds'\nimport {TYPE_NAMES} from './names'\nimport {titleOf, type JevSignals} from './signals'\n\ntype Definition = Record<string, unknown>\n\nconst isRecord = (value: unknown): value is Definition =>\n !!value && typeof value === 'object' && !Array.isArray(value)\n\nexport function signalsOf(definition: unknown): JevSignals | undefined {\n if (!isRecord(definition) || !isRecord(definition.options)) return undefined\n const {jev} = definition.options\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- `options.jev` is typed for schema authors via declaration merging\n return isRecord(jev) ? (jev as JevSignals) : undefined\n}\n\n/**\n * Adds a field that stores each signal's answer next to every field with `options.jev`.\n * Plugins can't see the Studio's own schema types, so wrap them in `sanity.config`:\n *\n * ```ts\n * schema: {types: withJevAnswers(schemaTypes)}\n * ```\n * @public\n */\nexport function withJevAnswers<T extends SchemaTypeDefinition>(types: T[]): T[] {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- only `fields` are rewritten\n return types.map((type) => withAnswerFields(type) as T)\n}\n\n/** Rewrites `fields` (and inline object members) of a type or field definition, recursively. */\nfunction withAnswerFields(definition: unknown): unknown {\n if (!isRecord(definition)) return definition\n let result = definition\n\n if (Array.isArray(definition.of)) {\n result = {...result, of: definition.of.map(withAnswerFields)}\n }\n\n if (Array.isArray(definition.fields)) {\n // Existing field types by name, so answer fields declared already (or added by an earlier\n // call) are kept rather than duplicated.\n const existing = new Map(\n definition.fields.filter(isRecord).map((field) => [field.name, field.type] as const),\n )\n const fields = definition.fields.flatMap((field: unknown) => {\n const rewritten = withAnswerFields(field)\n const signals = signalsOf(field)\n if (!signals || !isRecord(field) || !isRecord(rewritten)) return [rewritten]\n return [withRules(rewritten, signals), ...answerFieldsFor(field, signals, existing)]\n })\n result = {...result, fields}\n }\n\n return result\n}\n\nfunction answerFieldsFor(field: Definition, signals: JevSignals, existing: Map<unknown, unknown>) {\n return Object.entries(signals).flatMap(([key, signal]) => {\n const type = TYPE_NAMES[signal.type]\n if (existing.get(key) === type) return []\n if (existing.has(key)) {\n throw new Error(\n `Jev: the signal \"${key}\" on \"${String(field.name)}\" needs a field called \"${key}\" to ` +\n 'store its answer, but that name is already taken. Rename the signal.',\n )\n }\n existing.set(key, type)\n return [\n defineField({\n name: key,\n title: titleOf(key, signal),\n type,\n // Same group and fieldset as the attached field, so the answer field is mounted (and\n // can store answers) whenever the attached field is. Its type renders nothing.\n group:\n typeof field.group === 'string' || Array.isArray(field.group) ? field.group : undefined,\n fieldset: typeof field.fieldset === 'string' ? field.fieldset : undefined,\n }),\n ]\n })\n}\n\nconst LEVELS: RuleLevel[] = ['warn', 'require']\n\n/**\n * Adds a validation rule to the attached field for each signal's `warn` and `require`, after\n * the field's own validation. Rules judge the stored answer, so they say nothing until there is\n * one.\n */\nfunction withRules(field: Definition, signals: JevSignals): Definition {\n const checks = Object.entries(signals).flatMap(([key, signal]) =>\n LEVELS.filter((level) => signal[level]).map((level) => ({key, signal, level})),\n )\n if (checks.length === 0) return field\n\n const own = field.validation\n const validation = (rule: Rule) => {\n const ownRules = (Array.isArray(own) ? own : own ? [own] : []).flatMap((entry: unknown) => {\n const result: unknown = typeof entry === 'function' ? entry(rule) : entry\n return Array.isArray(result) ? result : result ? [result] : []\n })\n const jevRules = checks.map(({key, signal, level}) => {\n const kind = kindOf(signal)\n const title = titleOf(key, signal)\n const custom = rule.custom((_value, context) => {\n const stored = isRecord(context.parent) ? context.parent[key] : undefined\n return kind.check(level, stored, title) ?? true\n })\n return level === 'require' ? custom.error() : custom.warning()\n })\n return [...ownRules, ...jevRules]\n }\n return {...field, validation}\n}\n","import {Box, Button, Card, Dialog, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {useEffect, useId, useState} from 'react'\nimport {useClient, useDocumentPreviewStore, useTranslation} from 'sanity'\n\nimport {JEV_NAMESPACE} from './i18n'\nimport type {JevPluginConfig} from './types'\n\n// Same document shape as @sanity/studio-secrets, so keys stored by either keep working.\nconst SECRETS_ID = 'secrets.jev'\nconst SECRETS_TYPE = 'pluginSecrets'\n\ninterface StoredKey {\n loading: boolean\n apiKey?: string\n /** When the secrets document last changed. */\n updatedAt?: string\n}\n\n/** The AI Gateway key stored in `secrets.jev`, kept up to date as it changes. */\nexport function useStoredKey(): StoredKey {\n const documentPreviewStore = useDocumentPreviewStore()\n const [stored, setStored] = useState<StoredKey>({loading: true})\n\n useEffect(() => {\n const subscription = documentPreviewStore\n .unstable_observeDocument(SECRETS_ID)\n .subscribe((document) => {\n const secrets = document?.secrets\n const apiKey =\n secrets && typeof secrets === 'object' && 'gatewayApiKey' in secrets\n ? secrets.gatewayApiKey\n : undefined\n setStored({\n loading: false,\n apiKey: typeof apiKey === 'string' && apiKey ? apiKey : undefined,\n updatedAt: document?._updatedAt,\n })\n })\n return () => subscription.unsubscribe()\n }, [documentPreviewStore])\n\n return stored\n}\n\n/** Stores a new key, or removes the stored one when `apiKey` is undefined. */\nexport function useSaveKey() {\n const client = useClient({apiVersion: '2025-02-19'})\n return async (apiKey: string | undefined) => {\n await client\n .transaction()\n .createIfNotExists({_id: SECRETS_ID, _type: SECRETS_TYPE})\n .patch(SECRETS_ID, (patch) =>\n apiKey ? patch.set({secrets: {gatewayApiKey: apiKey}}) : patch.unset(['secrets']),\n )\n .commit({tag: 'jev.secrets'})\n }\n}\n\nexport type KeySource =\n /** `transport` or `apiKey` in the plugin config: nothing to set up in the Studio. */\n {from: 'config'; apiKey?: string} | {from: 'secrets'; loading: boolean; apiKey?: string}\n\n/** Where the API key comes from: plugin config wins over the stored key. */\nexport function useKeySource(config: JevPluginConfig): KeySource {\n const stored = useStoredKey()\n if (config.transport || config.apiKey) return {from: 'config', apiKey: config.apiKey}\n return {from: 'secrets', loading: stored.loading, apiKey: stored.apiKey}\n}\n\n/** Dialog for entering or replacing the stored key. Closes once the key is saved. */\nexport function JevKeyDialog({onClose}: {onClose: () => void}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const saveKey = useSaveKey()\n const inputId = useId()\n const [value, setValue] = useState('')\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n async function save() {\n setSaving(true)\n setError(null)\n try {\n await saveKey(value.trim())\n onClose()\n } catch (saveError) {\n setSaving(false)\n setError(saveError instanceof Error ? saveError.message : String(saveError))\n }\n }\n\n return (\n <Dialog id=\"jev-key-dialog\" header={t('key-dialog.title')} onClose={onClose} width={1}>\n <Box\n as=\"form\"\n padding={4}\n onSubmit={(event) => {\n event.preventDefault()\n void save()\n }}\n >\n <Stack gap={4}>\n <Stack gap={3}>\n <Text as=\"label\" htmlFor={inputId} size={1} weight=\"semibold\">\n {t('key-dialog.label')}\n </Text>\n <Text size={1} muted>\n {t('key-dialog.description')}\n </Text>\n <TextInput\n id={inputId}\n type=\"password\"\n autoComplete=\"off\"\n value={value}\n disabled={saving}\n onChange={(event) => setValue(event.currentTarget.value)}\n />\n </Stack>\n\n {error && (\n <Card padding={3} radius={2} tone=\"critical\">\n <Text size={1}>{t('key-dialog.save-failed', {error})}</Text>\n </Card>\n )}\n\n <Flex gap={2} justify=\"flex-end\">\n <Button\n text={t('key-dialog.cancel')}\n mode=\"bleed\"\n disabled={saving}\n onClick={onClose}\n />\n <Button\n type=\"submit\"\n text={saving ? t('key-dialog.saving') : t('key-dialog.save')}\n tone=\"primary\"\n disabled={saving || value.trim() === ''}\n />\n </Flex>\n </Stack>\n </Box>\n </Dialog>\n )\n}\n","import {RefreshIcon} from '@sanity/icons/Refresh'\nimport {Badge, Box, Button, Card, Flex, Spinner, Stack, Text} from '@sanity/ui'\nimport {useTranslation} from 'sanity'\nimport {keyframes, styled} from 'styled-components'\n\nimport {JEV_NAMESPACE} from './i18n'\nimport type {SignalError} from './lifecycle'\nimport {MUTED_COLOR} from './look'\nimport type {SignalView} from './useSignals'\n\nexport type Translate = ReturnType<typeof useTranslation>['t']\n\n/** An evaluation error in the Studio's language; errors from outside the Gateway as they are. */\nexport function errorText(t: Translate, error: SignalError): string {\n switch (error.kind) {\n case 'auth':\n return t('error.auth')\n case 'invalid':\n return t('error.invalid', {detail: error.detail ?? error.status})\n case 'busy':\n return t('error.busy')\n case 'failed':\n return error.detail\n ? t('error.failed-with-detail', {status: error.status, detail: error.detail})\n : t('error.failed', {status: error.status})\n case 'unexpected':\n return t('error.unexpected')\n default:\n return error.message\n }\n}\n\nconst MONO = 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'\n\nconst pulse = keyframes`\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.25; }\n`\n\n/** The chip's status dot: 6px, pulsing while the signal evaluates so the chip never shifts. */\nconst Dot = styled.span<{$color: string; $pulse: boolean}>`\n flex: none;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${({$color}) => $color};\n animation: ${({$pulse}) => ($pulse ? pulse : 'none')} 1s ease-in-out infinite;\n`\n\nfunction Chip({\n signal,\n selected,\n onSelect,\n}: {\n signal: SignalView\n selected: boolean\n onSelect: () => void\n}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const {text, color} = signal.reading?.chip ?? {text: t('chip.empty'), color: undefined}\n const failed = !!signal.error || !!signal.problem\n return (\n <Card\n as=\"button\"\n type=\"button\"\n radius={2}\n paddingX={2}\n paddingY={2}\n onClick={onSelect}\n aria-pressed={selected}\n title={signal.stale ? t('chip.out-of-date') : undefined}\n style={{\n display: 'inline-flex',\n flex: 'none',\n width: 'auto',\n border: 0,\n cursor: 'pointer',\n // A quiet highlight, as in the design, rather than Sanity UI's solid \"selected\" blue.\n background: selected ? 'rgba(128, 128, 128, 0.16)' : undefined,\n }}\n >\n <Flex align=\"center\" gap={2}>\n <Dot\n aria-hidden\n $color={failed ? '#f03e2f' : (color ?? MUTED_COLOR)}\n $pulse={signal.loading}\n />\n {/* Title and value share one line of text, so they share a baseline whatever the font. */}\n <Text size={1}>\n {signal.title}\n <span\n style={{\n marginLeft: '0.6em',\n fontFamily: MONO,\n color: failed ? '#f03e2f' : color,\n opacity: signal.stale ? 0.5 : 1,\n }}\n >\n {failed ? t('chip.error') : text}\n </span>\n </Text>\n </Flex>\n </Card>\n )\n}\n\ninterface StripProps {\n signals: SignalView[]\n selected: string | null\n onSelect: (key: string | null) => void\n setup: 'ready' | 'loading' | 'missing'\n canRun: boolean\n loading: boolean\n onRunAll: () => void\n onSetUp: () => void\n}\n\n/** The row of signal chips under an attached field. */\nexport function SignalStrip({\n signals,\n selected,\n onSelect,\n setup,\n canRun,\n loading,\n onRunAll,\n onSetUp,\n}: StripProps) {\n const {t} = useTranslation(JEV_NAMESPACE)\n return (\n // Joins the input above: its bottom border becomes the divider between content and signals.\n <Card\n border\n padding={1}\n style={{\n borderTop: 0,\n marginTop: -2,\n borderBottomLeftRadius: 3,\n borderBottomRightRadius: 3,\n }}\n >\n <Flex align=\"center\" gap={2}>\n <Flex flex={1} align=\"center\" gap={1} wrap=\"wrap\">\n {signals.map((signal) => (\n <Chip\n key={signal.key}\n signal={signal}\n selected={selected === signal.key}\n onSelect={() => onSelect(selected === signal.key ? null : signal.key)}\n />\n ))}\n </Flex>\n {setup === 'missing' ? (\n <Button\n text={t('strip.set-up')}\n mode=\"ghost\"\n tone=\"primary\"\n fontSize={1}\n padding={2}\n onClick={onSetUp}\n />\n ) : (\n <Flex align=\"center\" gap={1}>\n <Text size={0} muted>\n {t('strip.label')}\n </Text>\n {loading ? (\n <Box padding={2}>\n <Spinner muted />\n </Box>\n ) : (\n <Button\n icon={RefreshIcon}\n mode=\"bleed\"\n fontSize={1}\n padding={2}\n title={t('strip.evaluate-all')}\n aria-label={t('strip.evaluate-all')}\n disabled={!canRun}\n onClick={onRunAll}\n />\n )}\n </Flex>\n )}\n </Flex>\n </Card>\n )\n}\n\ninterface DetailProps {\n signal: SignalView\n empty: boolean\n onRetry: () => void\n onUpdateKey?: () => void\n}\n\n/** Expanded view of the selected chip. */\nexport function SignalDetail({signal, empty, onRetry, onUpdateKey}: DetailProps) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const {reading} = signal\n const body = signal.problem ? (\n <Text size={1}>{signal.problem}</Text>\n ) : reading ? (\n reading.body\n ) : (\n <Text size={1} muted>\n {empty ? t('detail.empty-field') : t('detail.not-evaluated')}\n </Text>\n )\n const aside = !reading?.aside ? null : 'badge' in reading.aside ? (\n <Badge tone={reading.aside.tone}>{reading.aside.badge}</Badge>\n ) : 'level' in reading.aside ? (\n <Badge tone={reading.aside.tone}>{t(`noul.level.${reading.aside.level}`)}</Badge>\n ) : (\n <Text size={1} muted>\n {reading.aside.note}\n </Text>\n )\n\n return (\n <Card\n padding={4}\n radius={2}\n border\n tone={signal.problem ? 'critical' : (reading?.tone ?? 'default')}\n >\n <Stack gap={4}>\n <Flex align=\"center\" gap={3}>\n <Box flex={1}>\n <Text size={1} weight=\"semibold\">\n {signal.title}\n </Text>\n </Box>\n {aside}\n </Flex>\n {body}\n {signal.stale && !signal.loading && (\n <Text size={0} muted>\n {t('detail.out-of-date')}\n </Text>\n )}\n {signal.error && (\n <Card padding={3} radius={2} tone=\"critical\" border>\n <Flex align=\"center\" gap={3} wrap=\"wrap\">\n <Box flex={1}>\n <Text size={1}>{errorText(t, signal.error)}</Text>\n </Box>\n {signal.keyRejected && onUpdateKey ? (\n <Button\n text={t('detail.update-key')}\n mode=\"ghost\"\n fontSize={1}\n onClick={onUpdateKey}\n />\n ) : (\n <Button text={t('detail.try-again')} mode=\"ghost\" fontSize={1} onClick={onRetry} />\n )}\n </Flex>\n </Card>\n )}\n </Stack>\n </Card>\n )\n}\n","export const DEFAULT_MODEL = 'typesafe-ai/jev'\nexport const DEFAULT_ENDPOINT = 'https://ai-gateway.vercel.sh/v1/evaluate'\nexport const DEFAULT_TAGS = ['feature:jev-fields']\n\n// AI Gateway rejects requests with more than 10 tags or tags outside 1–64 characters.\nconst MAX_TAGS = 10\nconst MAX_TAG_LENGTH = 64\n\n// Overloaded or rate limited: worth retrying. Everything else fails straight away.\nconst RETRY_STATUSES = new Set([429, 502, 503, 504, 529])\nconst DEFAULT_RETRY_DELAYS = [500, 1000, 2000]\nconst MAX_RETRY_AFTER_MS = 10_000\n\n/** @public */\nexport type JevQuestion =\n | {type: 'boolean'; instructions: string; criteria: {true: string; false: string}}\n | {type: 'score'; instructions: string; criteria: string[]}\n | {type: 'choice'; instructions: string; criteria: Record<string, string>}\n\n/** @public */\nexport type JevAnswer =\n | {type: 'boolean'; probability: number}\n | {type: 'score'; score: number; probabilities: Record<string, number>; confidence?: number}\n | {type: 'choice'; choice: string; probabilities: Record<string, number>; confidence?: number}\n\n/**\n * Body of a request to AI Gateway's `/v1/evaluate`. A `transport` receives it as-is, so a\n * proxy only has to forward it with an `Authorization` header.\n * @public\n */\nexport interface JevRequest {\n model: string\n state: string | Record<string, string>\n questions: {q: JevQuestion}\n providerOptions: {gateway: {tags: string[]}}\n}\n\n/**\n * Sends one request and resolves with the HTTP response, which must carry AI Gateway's\n * status and JSON body. Errors and retries are handled by the plugin either way.\n * @public\n */\nexport type JevTransport = (request: JevRequest, init: {signal?: AbortSignal}) => Promise<Response>\n\nexport type JevErrorKind = 'auth' | 'invalid' | 'busy' | 'failed' | 'unexpected'\n\n/**\n * An evaluation that failed. `message` is in English; the Studio shows a translated text built\n * from `kind`, `status` and `detail` (the Gateway's own explanation).\n */\nexport class JevError extends Error {\n kind: JevErrorKind\n status?: number\n detail?: string\n\n constructor(kind: JevErrorKind, message: string, status?: number, detail?: string) {\n super(message)\n this.name = 'JevError'\n this.kind = kind\n this.status = status\n this.detail = detail\n }\n}\n\n/** The default transport: calls AI Gateway directly from the browser with the API key. */\nexport function gatewayTransport(apiKey: string, endpoint = DEFAULT_ENDPOINT): JevTransport {\n return (request, {signal}) =>\n fetch(endpoint, {\n method: 'POST',\n signal,\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(request),\n })\n}\n\ninterface EvaluateArgs {\n transport: JevTransport\n model: string\n state: string | Record<string, string>\n question: JevQuestion\n tags: string[]\n signal?: AbortSignal\n /** Waits before each retry of an overloaded or rate-limited request. */\n retryDelays?: number[]\n}\n\ninterface EvaluateResponse {\n answers?: {q?: JevAnswer}\n model?: string\n error?: {message?: string} | string\n}\n\n/**\n * Asks the decision model a single typed question about the state. Resolves with the answer\n * and the model that gave it, as the Gateway names it.\n */\nexport async function evaluateQuestion({\n transport,\n model,\n state,\n question,\n tags,\n signal,\n retryDelays = DEFAULT_RETRY_DELAYS,\n}: EvaluateArgs): Promise<{answer: JevAnswer; model: string}> {\n const request: JevRequest = {\n model,\n state,\n questions: {q: question},\n providerOptions: {gateway: {tags: normalizeTags(tags)}},\n }\n\n const send = async (attempt: number): Promise<{answer: JevAnswer; model: string}> => {\n const response = await transport(request, {signal})\n const body: EvaluateResponse = await response.json().catch(() => ({}))\n\n if (response.ok) {\n const answer = body.answers?.q\n if (answer?.type !== question.type) {\n throw new JevError('unexpected', 'AI Gateway returned no answer.', response.status)\n }\n return {answer, model: body.model ?? model}\n }\n\n if (RETRY_STATUSES.has(response.status) && attempt < retryDelays.length) {\n await sleep(retryDelay(response, retryDelays[attempt]), signal)\n return send(attempt + 1)\n }\n throw toError(response.status, errorMessage(body))\n }\n\n return send(0)\n}\n\nfunction toError(status: number, message: string | undefined): JevError {\n if (status === 401 || status === 403) {\n return new JevError(\n 'auth',\n 'AI Gateway rejected the API key. Check the key in the Jev tool.',\n status,\n message,\n )\n }\n if (status === 400 || status === 422) {\n return new JevError(\n 'invalid',\n `AI Gateway rejected the question: ${message ?? status}`,\n status,\n message,\n )\n }\n if (RETRY_STATUSES.has(status)) {\n return new JevError('busy', 'AI Gateway is busy. Try again in a moment.', status, message)\n }\n const suffix = message ? `: ${message}` : ''\n return new JevError('failed', `AI Gateway responded with ${status}${suffix}`, status, message)\n}\n\nfunction errorMessage(body: EvaluateResponse): string | undefined {\n return (typeof body.error === 'string' ? body.error : body.error?.message) || undefined\n}\n\n/** Honours a short `Retry-After` (in seconds) when it asks for longer than our own backoff. */\nfunction retryDelay(response: Response, backoff: number): number {\n const seconds = Number(response.headers.get('retry-after'))\n if (!Number.isFinite(seconds) || seconds <= 0) return backoff\n return Math.min(Math.max(backoff, seconds * 1000), MAX_RETRY_AFTER_MS)\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n const timeout = setTimeout(resolve, ms)\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeout)\n reject(signal.reason)\n },\n {once: true},\n )\n })\n}\n\nexport function normalizeTags(tags: string[]): string[] {\n const cleaned = tags.map((tag) => tag.trim().slice(0, MAX_TAG_LENGTH)).filter(Boolean)\n return [...new Set(cleaned)].slice(0, MAX_TAGS)\n}\n","import {fingerprint} from './content'\nimport {evaluateQuestion, JevError, type JevErrorKind, type JevTransport} from './evaluate'\nimport {kindOf, type Kind, type Reading, type StoredValue} from './kinds'\nimport {titleOf, type JevSignal, type JevSignals} from './signals'\n\nexport interface Clock {\n now(): Date\n /** Calls back after `ms`. Returns a function that cancels the call. */\n after(ms: number, callback: () => void): () => void\n}\n\nconst realClock: Clock = {\n now: () => new Date(),\n after(ms, callback) {\n const timeout = setTimeout(callback, ms)\n return () => clearTimeout(timeout)\n },\n}\n\n/** Everything the lifecycle reads about one attached field. Passed whole on every update. */\nexport interface LifecycleInputs {\n signals: JevSignals\n /** The attached field's value, flattened to text. */\n state: string\n /** Stored answers, by signal key. */\n answers: Record<string, unknown>\n /** How to reach the Gateway. Without one, nothing is evaluated. */\n transport?: JevTransport\n /** Stores an answer in its signal's answer field. Throws if there is no such field. */\n store: (key: string, value: StoredValue) => void\n model: string\n /** Reporting tags for every request. Each request also gets a tag naming its signal. */\n tags: string[]\n /** Document type and field path the per-signal tag starts with, e.g. `['article', 'seo']`. */\n tagPath: string[]\n readOnly: boolean\n debounceMs: number\n}\n\nexport interface SignalView {\n key: string\n signal: JevSignal\n title: string\n /** Why the signal's config can't be asked, if it can't. */\n problem?: string\n /** The stored answer, as its kind reads it. Absent when the signal is unanswered. */\n reading?: Reading\n /** The stored answer's state, question or model has changed since it was evaluated. */\n stale: boolean\n /** Waiting to evaluate, or evaluating. */\n loading: boolean\n /** Why the last evaluation failed. Gateway errors also carry their kind, for translation. */\n error: SignalError | null\n /** The Gateway rejected the API key on the last evaluation. */\n keyRejected: boolean\n}\n\nexport interface SignalError {\n /** In English. Shown as-is for errors that don't come from the Gateway. */\n message: string\n kind?: JevErrorKind\n status?: number\n /** The Gateway's own explanation, if it gave one. */\n detail?: string\n}\n\nexport interface LifecycleSnapshot {\n signals: SignalView[]\n /** Evaluation is possible: there is a transport, content, and the field is editable. */\n canRun: boolean\n empty: boolean\n /** Any signal is loading. */\n loading: boolean\n}\n\nexport interface Lifecycle {\n update: (inputs: LifecycleInputs) => void\n /** Call just before handing a local edit to the form, so the resulting state counts as local. */\n localEdit: () => void\n /** Evaluates the given signals (all by default) now, whoever edited the field last. */\n run: (keys?: string[]) => void\n subscribe: (listener: () => void) => () => void\n getSnapshot: () => LifecycleSnapshot\n /** Cancels timers and requests. A later update starts over with the same inputs. */\n dispose: () => void\n}\n\ntype Status = {state: 'loading'; hash: string} | {state: 'error'; hash: string; error: SignalError}\n\ninterface Entry {\n key: string\n signal: JevSignal\n kind: Kind\n title: string\n /** Fingerprint of the state, question and model: what a stored answer must match to be fresh. */\n hash: string\n}\n\nconst EMPTY: LifecycleSnapshot = {signals: [], canRun: false, empty: true, loading: false}\n\n/**\n * The Evaluation lifecycle of the signals on one attached field. Signals evaluate on their own\n * shortly after a local edit, only while the field's state is one this Studio produced; work for\n * a fingerprint that has since changed is cancelled and its answer dropped.\n */\nexport function createLifecycle(clock: Clock = realClock): Lifecycle {\n let inputs: LifecycleInputs | undefined\n let entries: Entry[] = []\n /** The state the last local edit produced. Signals only evaluate on their own for it. */\n let localState: string | undefined\n let expectLocal = false\n\n // Requests, statuses and automatic attempts all belong to one fingerprint per signal.\n const requests = new Map<string, {controller: AbortController; hash: string}>()\n const statuses = new Map<string, Status>()\n const attempted = new Map<string, string>()\n\n let scheduled: {signature: string; cancel: () => void} | undefined\n let snapshot = EMPTY\n let dirty = true\n const listeners = new Set<() => void>()\n\n const canRun = (current: LifecycleInputs) =>\n !!current.transport && !current.readOnly && current.state.trim() !== ''\n\n function update(next: LifecycleInputs) {\n const previous = inputs\n inputs = next\n\n if (next.state !== previous?.state) {\n localState = expectLocal ? next.state : undefined\n expectLocal = false\n }\n\n if (\n !previous ||\n next.signals !== previous.signals ||\n next.state !== previous.state ||\n next.model !== previous.model\n ) {\n entries = Object.entries(next.signals).map(([key, signal]) => {\n const kind = kindOf(signal)\n return {\n key,\n signal,\n kind,\n title: titleOf(key, signal),\n hash: fingerprint([next.state, kind.question ?? null, next.model]),\n }\n })\n forgetObsoleteWork()\n dirty = true\n }\n\n if (\n previous &&\n (canRun(next) !== canRun(previous) ||\n entries.some(({key}) => next.answers[key] !== previous.answers[key]))\n ) {\n dirty = true\n }\n\n refresh()\n }\n\n function forgetObsoleteWork() {\n const hashes = new Map(entries.map(({key, hash}) => [key, hash]))\n for (const [key, request] of requests) {\n if (hashes.get(key) !== request.hash) {\n request.controller.abort()\n requests.delete(key)\n }\n }\n for (const [key, status] of statuses) {\n if (hashes.get(key) !== status.hash) statuses.delete(key)\n }\n for (const [key, hash] of attempted) {\n if (hashes.get(key) !== hash) attempted.delete(key)\n }\n }\n\n /** Signals that would evaluate on their own: unanswered or stale, and not tried yet. */\n function pendingKeys(current: LifecycleInputs, views: SignalView[]): string[] {\n if (!canRun(current) || localState !== current.state) return []\n return entries\n .filter((entry, index) => {\n const view = views[index]\n return (\n entry.kind.question &&\n (!view.reading || view.stale) &&\n attempted.get(entry.key) !== entry.hash\n )\n })\n .map(({key}) => key)\n }\n\n /** Rebuilds the snapshot and the debounce after anything they depend on changed. */\n function refresh() {\n const current = inputs\n if (!current || !dirty) return\n dirty = false\n\n const views = entries.map((entry): SignalView => {\n const reading = entry.kind.read(current.answers[entry.key])\n const status = statuses.get(entry.key)\n return {\n key: entry.key,\n signal: entry.signal,\n title: entry.title,\n problem: entry.kind.problem,\n reading,\n stale: !!reading && reading.value.sourceHash !== entry.hash,\n loading: status?.state === 'loading',\n error: status?.state === 'error' ? status.error : null,\n keyRejected: status?.state === 'error' && status.error.kind === 'auth',\n }\n })\n const pending = pendingKeys(current, views)\n // Waiting out the debounce counts as loading, so the spinner shows from the first keystroke.\n for (const view of views) if (pending.includes(view.key)) view.loading = true\n snapshot = {\n signals: views,\n canRun: canRun(current),\n empty: current.state.trim() === '',\n loading: views.some((view) => view.loading),\n }\n schedule(pending, current.debounceMs)\n for (const listener of listeners) listener()\n }\n\n function schedule(keys: string[], debounceMs: number) {\n const hashes = new Map(entries.map(({key, hash}) => [key, hash]))\n const signature = keys.map((key) => `${key}:${hashes.get(key)}`).join('\\n')\n if (signature === scheduled?.signature) return\n scheduled?.cancel()\n scheduled = undefined\n if (!signature) return\n scheduled = {signature, cancel: clock.after(debounceMs, () => run(keys))}\n }\n\n function setStatus(key: string, status: Status | undefined) {\n if (status) statuses.set(key, status)\n else statuses.delete(key)\n dirty = true\n refresh()\n }\n\n function run(keys?: string[]) {\n const current = inputs\n if (!current?.transport || !canRun(current)) return\n const {transport, model, state} = current\n\n for (const entry of entries) {\n const question = entry.kind.question\n if ((keys && !keys.includes(entry.key)) || !question) continue\n\n requests.get(entry.key)?.controller.abort()\n const controller = new AbortController()\n requests.set(entry.key, {controller, hash: entry.hash})\n attempted.set(entry.key, entry.hash)\n setStatus(entry.key, {state: 'loading', hash: entry.hash})\n\n const tag = `${entry.kind.typeName}:${[...current.tagPath, entry.key].filter(Boolean).join('.')}`\n const evaluate = async () => {\n try {\n const result = await evaluateQuestion({\n transport,\n model,\n state,\n question,\n tags: [...current.tags, tag],\n signal: controller.signal,\n })\n if (controller.signal.aborted) return\n // The latest store, so a re-registered answer field still gets its answer.\n inputs?.store(entry.key, {\n ...entry.kind.toStored(result.answer),\n evaluatedAt: clock.now().toISOString(),\n model: result.model,\n sourceHash: entry.hash,\n })\n requests.delete(entry.key)\n setStatus(entry.key, undefined)\n } catch (error) {\n if (controller.signal.aborted) return\n requests.delete(entry.key)\n setStatus(entry.key, {\n state: 'error',\n hash: entry.hash,\n error:\n error instanceof JevError\n ? {\n message: error.message,\n kind: error.kind,\n status: error.status,\n detail: error.detail,\n }\n : {message: error instanceof Error ? error.message : String(error)},\n })\n }\n }\n void evaluate()\n }\n }\n\n return {\n update,\n localEdit() {\n expectLocal = true\n },\n run,\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n getSnapshot: () => snapshot,\n dispose() {\n scheduled?.cancel()\n scheduled = undefined\n // Requests cut short here were never answered, so they may run again.\n for (const [key, request] of requests) {\n request.controller.abort()\n statuses.delete(key)\n attempted.delete(key)\n }\n requests.clear()\n dirty = true\n },\n }\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from 'react'\nimport {useFormValue, type Path} from 'sanity'\n\nimport {isRecord} from './content'\nimport {pathKey, useJevForm} from './context'\nimport {DEFAULT_MODEL, DEFAULT_TAGS, gatewayTransport} from './evaluate'\nimport type {StoredValue} from './kinds'\nimport {createLifecycle} from './lifecycle'\nimport {useKeySource} from './secrets'\nimport type {JevSignals} from './signals'\n\nexport type {SignalView} from './lifecycle'\n\nconst NO_ANSWERS: Record<string, unknown> = {}\n\ninterface Args {\n signals: JevSignals\n /** Path of the attached field. Answers are stored next to it. */\n path: Path\n /** The attached field's value, flattened to text. */\n state: string\n readOnly: boolean\n}\n\n/** Connects the Evaluation lifecycle of one attached field to the Studio's form. */\nexport function useSignals({signals, path, state, readOnly}: Args) {\n const {config, writers} = useJevForm()\n const parentPath = useMemo(() => path.slice(0, -1), [path])\n\n const keySource = useKeySource(config)\n const apiKey = keySource.apiKey\n const transport = useMemo(\n () => config.transport ?? (apiKey ? gatewayTransport(apiKey, config.endpoint) : undefined),\n [config.transport, apiKey, config.endpoint],\n )\n const setup: 'ready' | 'loading' | 'missing' = transport\n ? 'ready'\n : keySource.from === 'secrets' && keySource.loading\n ? 'loading'\n : 'missing'\n\n const parent = useFormValue(parentPath)\n const documentType = useFormValue(['_type'])\n\n // Each answer is stored next to the attached field, in the field named after its signal.\n const store = useCallback(\n (key: string, value: StoredValue) => {\n const write = writers.get(pathKey([...parentPath, key]))\n if (!write) {\n throw new Error(\n `There is no \"${key}\" field to store this answer in. Wrap your schema ` +\n 'types with withJevAnswers() in sanity.config.',\n )\n }\n write(value)\n },\n [writers, parentPath],\n )\n\n // Array items have keyed path segments; collapse them so the tag names the field, not the item.\n const tagPath = useMemo(\n () => [\n typeof documentType === 'string' ? documentType : '',\n ...parentPath.map((segment) => (typeof segment === 'string' ? segment : '[]')),\n ],\n [documentType, parentPath],\n )\n\n const [lifecycle] = useState(() => createLifecycle())\n useLayoutEffect(() => {\n lifecycle.update({\n signals,\n state,\n answers: isRecord(parent) ? parent : NO_ANSWERS,\n transport,\n store,\n model: config.model ?? DEFAULT_MODEL,\n tags: config.tags ?? DEFAULT_TAGS,\n tagPath,\n readOnly,\n debounceMs: config.debounceMs ?? 500,\n })\n })\n useEffect(() => () => lifecycle.dispose(), [lifecycle])\n const snapshot = useSyncExternalStore(lifecycle.subscribe, lifecycle.getSnapshot)\n\n return {\n ...snapshot,\n setup,\n /** The key is stored with Studio secrets, so editors can set and change it here. */\n keyInSecrets: keySource.from === 'secrets',\n run: (key: string) => lifecycle.run([key]),\n runAll: () => lifecycle.run(),\n localEdit: lifecycle.localEdit,\n }\n}\n","import {Card, Stack, Text} from '@sanity/ui'\nimport {useState} from 'react'\nimport {useTranslation, type FormPatch, type InputProps, type PatchEvent} from 'sanity'\n\nimport {estimateTokens, nearTokenLimit, STATE_TOKEN_LIMIT, toText} from './content'\nimport {JEV_NAMESPACE} from './i18n'\nimport {JevKeyDialog} from './secrets'\nimport type {JevSignals} from './signals'\nimport {SignalDetail, SignalStrip} from './ui'\nimport {useSignals} from './useSignals'\n\ntype OnChange = (patch: FormPatch | FormPatch[] | PatchEvent) => void\n\n/** Renders a field with `options.jev` as usual, with its signal strip and details below. */\nexport function AttachedInput({signals, ...props}: InputProps & {signals: JevSignals}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const parentOnChange: OnChange = props.onChange\n const {value} = props\n const state = toText(value)\n const jev = useSignals({\n signals,\n path: props.path,\n state,\n readOnly: !!props.readOnly,\n })\n // Signals only evaluate on their own after an edit made here, never because someone\n // else's edit arrived, so opening a document never writes to it.\n const onChange: OnChange = (patch) => {\n jev.localEdit()\n parentOnChange(patch)\n }\n const inputProps: InputProps = {...props, onChange}\n\n const [selected, setSelected] = useState<string | null>(null)\n const [keyDialogOpen, setKeyDialogOpen] = useState(false)\n const selectedSignal = jev.signals.find((signal) => signal.key === selected)\n\n return (\n <Stack gap={2}>\n <div>\n {props.renderDefault(inputProps)}\n <SignalStrip\n signals={jev.signals}\n selected={selected}\n onSelect={setSelected}\n setup={jev.setup}\n canRun={jev.canRun}\n loading={jev.loading}\n onRunAll={jev.runAll}\n onSetUp={() => setKeyDialogOpen(true)}\n />\n </div>\n {nearTokenLimit(state) && (\n <Card padding={3} radius={2} tone=\"caution\" border>\n <Text size={1}>\n {t('field.too-long', {\n tokens: Math.round(estimateTokens(state) / 1000),\n limit: STATE_TOKEN_LIMIT / 1000,\n })}\n </Text>\n </Card>\n )}\n {selectedSignal && (\n <SignalDetail\n signal={selectedSignal}\n empty={jev.empty}\n onRetry={() => jev.run(selectedSignal.key)}\n onUpdateKey={jev.keyInSecrets ? () => setKeyDialogOpen(true) : undefined}\n />\n )}\n {keyDialogOpen && <JevKeyDialog onClose={() => setKeyDialogOpen(false)} />}\n </Stack>\n )\n}\n","import {Badge, Box, Button, Card, Container, Flex, Heading, Stack, Text} from '@sanity/ui'\nimport {useState, type ReactNode} from 'react'\nimport {Translate, useTranslation} from 'sanity'\n\nimport {DEFAULT_MODEL, DEFAULT_TAGS, evaluateQuestion, gatewayTransport, JevError} from './evaluate'\nimport {JEV_NAMESPACE} from './i18n'\nimport {kindOf} from './kinds'\nimport type {SignalError} from './lifecycle'\nimport {JevKeyDialog, useSaveKey, useStoredKey} from './secrets'\nimport {noul} from './signals'\nimport type {JevPluginConfig} from './types'\nimport {errorText} from './ui'\n\nconst Code = ({children}: {children?: ReactNode}) => <code>{children}</code>\n\n// One tiny yes/no question: costs a fraction of a cent.\nconst CONNECTION_TEST = kindOf(\n noul({\n instructions: 'Is this text a connection test?',\n true: 'It is a test',\n false: 'It is not a test',\n }),\n).question\n\ntype TestState =\n | {state: 'idle'}\n | {state: 'running'}\n | {state: 'passed'; model: string}\n | {state: 'failed'; error: SignalError}\n\nconst asSignalError = (error: unknown): SignalError =>\n error instanceof JevError\n ? {message: error.message, kind: error.kind, status: error.status, detail: error.detail}\n : {message: error instanceof Error ? error.message : String(error)}\n\n/** Studio tool for setting, checking and removing the stored AI Gateway key. */\nexport function JevTool({config}: {config: JevPluginConfig}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n const stored = useStoredKey()\n const saveKey = useSaveKey()\n const [settingsOpen, setSettingsOpen] = useState(false)\n const [confirmRemove, setConfirmRemove] = useState(false)\n const [test, setTest] = useState<TestState>({state: 'idle'})\n\n // Same precedence as the fields: transport, then `apiKey` in config, then the stored key.\n const fromConfig = !!(config.transport || config.apiKey)\n const apiKey = config.apiKey ?? stored.apiKey\n const transport =\n config.transport ?? (apiKey ? gatewayTransport(apiKey, config.endpoint) : undefined)\n\n async function testConnection() {\n if (!transport || !CONNECTION_TEST) return\n setTest({state: 'running'})\n try {\n const result = await evaluateQuestion({\n transport,\n model: config.model ?? DEFAULT_MODEL,\n state: 'This is a connection test.',\n question: CONNECTION_TEST,\n tags: [...(config.tags ?? DEFAULT_TAGS), 'jev:connection-test'],\n })\n setTest({state: 'passed', model: result.model})\n } catch (error) {\n setTest({state: 'failed', error: asSignalError(error)})\n }\n }\n\n return (\n <Container width={1} padding={4}>\n <Stack gap={5}>\n <Stack gap={3}>\n <Heading size={2}>{t('tool.title')}</Heading>\n <Text muted size={1}>\n {t('tool.intro')}\n </Text>\n </Stack>\n\n <Card padding={4} radius={2} border>\n <Stack gap={4}>\n <Flex align=\"center\" gap={3}>\n <Box flex={1}>\n <Heading size={0} as=\"h2\">\n {t('tool.key.heading')}\n </Heading>\n </Box>\n <KeyStatus fromConfig={fromConfig} loading={stored.loading} stored={stored.apiKey} />\n </Flex>\n\n {fromConfig ? (\n <Text size={1} muted>\n {config.transport ? t('tool.key.from-transport') : t('tool.key.from-config')}\n </Text>\n ) : stored.apiKey ? (\n <Stack gap={3}>\n <Text size={1}>\n <Translate\n t={t}\n i18nKey=\"tool.key.ends-with\"\n values={{last4: stored.apiKey.slice(-4)}}\n components={{Code}}\n />\n </Text>\n {stored.updatedAt && (\n <Text size={1} muted>\n {t('tool.key.changed', {date: new Date(stored.updatedAt).toLocaleString()})}\n </Text>\n )}\n </Stack>\n ) : (\n <Text size={1} muted>\n {stored.loading ? t('tool.key.checking') : t('tool.key.none')}\n </Text>\n )}\n\n <Flex gap={2} wrap=\"wrap\">\n {!fromConfig && (\n <Button\n text={stored.apiKey ? t('tool.key.change') : t('tool.key.set')}\n tone=\"primary\"\n disabled={stored.loading}\n onClick={() => setSettingsOpen(true)}\n />\n )}\n <Button\n text={test.state === 'running' ? t('tool.test.running') : t('tool.test.run')}\n mode=\"ghost\"\n disabled={!transport || test.state === 'running'}\n onClick={testConnection}\n />\n {!fromConfig && stored.apiKey && !confirmRemove && (\n <Button\n text={t('tool.key.remove')}\n mode=\"bleed\"\n tone=\"critical\"\n onClick={() => setConfirmRemove(true)}\n />\n )}\n </Flex>\n\n {confirmRemove && (\n <Card padding={3} radius={2} tone=\"critical\">\n <Flex align=\"center\" gap={2} wrap=\"wrap\">\n <Box flex={1}>\n <Text size={1}>{t('tool.key.remove-confirm')}</Text>\n </Box>\n <Button\n text={t('tool.key.remove-cancel')}\n mode=\"bleed\"\n onClick={() => setConfirmRemove(false)}\n />\n <Button\n text={t('tool.key.remove-confirm-button')}\n tone=\"critical\"\n onClick={async () => {\n setConfirmRemove(false)\n try {\n await saveKey(undefined)\n setTest({state: 'idle'})\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n setTest({\n state: 'failed',\n error: {message: t('tool.key.remove-failed', {error: message})},\n })\n }\n }}\n />\n </Flex>\n </Card>\n )}\n\n {test.state === 'passed' && (\n <Card padding={3} radius={2} tone=\"positive\">\n <Text size={1}>{t('tool.test.passed', {model: test.model})}</Text>\n </Card>\n )}\n {test.state === 'failed' && (\n <Card padding={3} radius={2} tone=\"critical\">\n <Text size={1}>{errorText(t, test.error)}</Text>\n </Card>\n )}\n </Stack>\n </Card>\n\n <Stack gap={3}>\n <Heading size={0} as=\"h2\">\n {t('tool.visibility.heading')}\n </Heading>\n <Text size={1} muted>\n <Translate t={t} i18nKey=\"tool.visibility.body\" components={{Code}} />\n </Text>\n </Stack>\n </Stack>\n\n {settingsOpen && (\n <JevKeyDialog\n onClose={() => {\n setSettingsOpen(false)\n setTest({state: 'idle'})\n }}\n />\n )}\n </Container>\n )\n}\n\nfunction KeyStatus({\n fromConfig,\n loading,\n stored,\n}: {\n fromConfig: boolean\n loading: boolean\n stored: string | undefined\n}) {\n const {t} = useTranslation(JEV_NAMESPACE)\n if (fromConfig) return <Badge tone=\"primary\">{t('tool.key.status.config')}</Badge>\n if (loading) return <Badge>{t('tool.key.status.checking')}</Badge>\n return stored ? (\n <Badge tone=\"positive\">{t('tool.key.status.set')}</Badge>\n ) : (\n <Badge tone=\"caution\">{t('tool.key.status.not-set')}</Badge>\n )\n}\n","import {SparklesIcon} from '@sanity/icons/Sparkles'\nimport {definePlugin} from 'sanity'\n\nimport {signalsOf} from './answers'\nimport {AttachedInput} from './AttachedInput'\nimport {JevFormProvider} from './context'\nimport {jevLocaleBundle} from './i18n'\nimport {JevTool} from './JevTool'\nimport {KIND_SCHEMA_TYPES} from './kinds'\nimport type {JevSignals} from './signals'\nimport type {JevPluginConfig} from './types'\n\nexport type * from './types'\nexport type {JevAnswer, JevQuestion, JevRequest, JevTransport} from './evaluate'\nexport type {ChoiceSignal, JevSignal, JevSignals, NoulSignal, ScoreSignal} from './signals'\nexport type {ChoiceRule, RangeRule} from './kinds'\nexport {choice, noul, score} from './signals'\nexport {withJevAnswers} from './answers'\nexport {JEV_NAMESPACE, type JevTranslationKey} from './i18n'\n\ndeclare module 'sanity' {\n interface BaseSchemaTypeOptions {\n /** Jev signals shown on this field, keyed by the name of the field storing each answer. */\n jev?: JevSignals\n }\n}\n\n/**\n * Jev signals for Sanity Studio: yes/no, score and choice questions attached to a field with\n * `options.jev`, answered by TypeSafe's Jev model through Vercel AI Gateway. Pair it with\n * `withJevAnswers(schemaTypes)` in `schema.types`.\n * @public\n */\nexport const jev = definePlugin<JevPluginConfig | void>((config) => {\n const pluginConfig: JevPluginConfig = config ?? {}\n\n return {\n name: 'jev',\n tools:\n pluginConfig.tool === false\n ? []\n : [\n {\n name: 'jev',\n title: 'Jev',\n icon: SparklesIcon,\n component: () => <JevTool config={pluginConfig} />,\n },\n ],\n form: {\n components: {\n input: (props) => {\n // The document's root input: gives attached fields the plugin config and a way to\n // reach the answer fields next to them.\n if (props.path.length === 0) {\n return (\n <JevFormProvider config={pluginConfig}>{props.renderDefault(props)}</JevFormProvider>\n )\n }\n const signals = signalsOf(props.schemaType)\n return signals ? (\n <AttachedInput {...props} signals={signals} />\n ) : (\n props.renderDefault(props)\n )\n },\n },\n },\n // Where answers are stored. Shown on the attached field, so their fields render nothing.\n schema: {types: KIND_SCHEMA_TYPES},\n i18n: {bundles: [jevLocaleBundle]},\n }\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,100 @@
1
1
  {
2
2
  "name": "sanity-plugin-jev-fields",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Jev signals for Sanity Studio: AI-evaluated yes/no, score and choice answers attached to a field, from TypeSafe's Jev model through Vercel AI Gateway",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-gateway",
8
+ "jev",
9
+ "sanity",
10
+ "sanity-plugin",
11
+ "typesafe"
12
+ ],
13
+ "homepage": "https://github.com/frederikvonsperling/sanity-plugin-jev-fields#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/frederikvonsperling/sanity-plugin-jev-fields/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Frederik von Sperling (https://frederikvonsperling.com)",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/frederikvonsperling/sanity-plugin-jev-fields.git"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "source": "./src/index.tsx",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "publishConfig": {
37
+ "exports": {
38
+ ".": "./dist/index.js",
39
+ "./package.json": "./package.json"
40
+ },
41
+ "access": "public",
42
+ "provenance": true
43
+ },
44
+ "scripts": {
45
+ "build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
46
+ "changeset": "changeset",
47
+ "format": "oxfmt",
48
+ "lint": "oxlint",
49
+ "prepublishOnly": "pnpm run build",
50
+ "record-fixtures": "node scripts/record-fixtures.ts",
51
+ "release": "pnpm build && node scripts/publish.ts",
52
+ "test": "vitest run",
53
+ "typecheck": "tsc --noEmit",
54
+ "version-packages": "changeset version",
55
+ "watch": "pkg-utils watch --strict"
56
+ },
57
+ "dependencies": {
58
+ "@sanity/icons": "^5.2.3",
59
+ "@sanity/ui": "^4.3.3"
60
+ },
61
+ "devDependencies": {
62
+ "@changesets/cli": "^3.0.3",
63
+ "@sanity/browserslist-config": "^1.0.5",
64
+ "@sanity/pkg-utils": "^13.0.6",
65
+ "@sanity/plugin-kit": "^10.0.11",
66
+ "@types/node": "^24.19.1",
67
+ "@types/react": "^19.3.0",
68
+ "oxfmt": "^0.72.0",
69
+ "oxlint": "^1.87.0",
70
+ "oxlint-tsgolint": "^7.0.2003",
71
+ "react": "^19.3.0",
72
+ "react-dom": "^19.3.0",
73
+ "sanity": "^6.18.0",
74
+ "styled-components": "^6.5.3",
75
+ "typescript": "^6.0.3",
76
+ "vitest": "^4.1.11"
77
+ },
78
+ "peerDependencies": {
79
+ "react": "^19.2",
80
+ "react-dom": "^19.2",
81
+ "sanity": "^6",
82
+ "styled-components": "^6.1"
83
+ },
84
+ "browserslist": "extends @sanity/browserslist-config",
85
+ "engines": {
86
+ "node": ">=20.19 <22 || >=22.12"
87
+ },
88
+ "packageManager": "pnpm@10.34.5",
89
+ "pnpm": {
90
+ "onlyBuiltDependencies": [
91
+ "esbuild"
92
+ ]
93
+ },
94
+ "sanityPlugin": {
95
+ "verifyPackage": {
96
+ "srcIndex": false,
97
+ "scripts": false
98
+ }
99
+ }
100
+ }