# Jsonata Editor URL: /docs/web/components/jsonata-editor A three-pane workbench for writing and evaluating JSONata expressions — a JSON input pane, an expression editor with IntelliSense, and a live result pane. **Demo:** ```tsx 'use client'; import { useState } from 'react'; import { JSONATA_SAMPLES, JsonataEditor, type JsonataAIMessageContext, type JsonataEditorOrientation } from '@docyrus/ui/components/jsonata-editor'; import { Label } from '@docyrus/ui/primitives/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@docyrus/ui/primitives/ui/select'; import { Switch } from '@docyrus/ui/primitives/ui/switch'; import { Settings2 } from 'lucide-react'; /* * Demo data with nested objects and an array of objects so the editor's * autocomplete surfaces paths like `customer.address.city` and * `orders[0].product` — type `customer.` or `orders[0].` to see them. */ const DEMO_INPUT = { customer: { id: 'CUS-4821', name: 'Acme Corporation', email: 'hello@acme.example', isActive: true, address: { street: '100 Main St', city: 'Springfield', country: 'USA', postalCode: '01234' } }, orders: [ { id: 'ORD-1001', product: 'Widget Pro', quantity: 3, price: 29.99, tags: ['featured', 'sale'] }, { id: 'ORD-1002', product: 'Gadget Lite', quantity: 1, price: 79.5, tags: ['new'] }, { id: 'ORD-1003', product: 'Sprocket', quantity: 5, price: 12.25, tags: [] } ], total: 261.97 }; const DEMO_EXPRESSION = '$sum(orders.(price * quantity))'; /** Canned demo replies — wire `onSendMessage` to a real backend in your app. */ async function demoAIReply(message: string, context: JsonataAIMessageContext): Promise { await new Promise(resolve => setTimeout(resolve, 400)); return [ `_(Demo response)_ I'd help you with: **${message}**.`, '', `Your current expression is \`${context.expression || '(empty)'}\`.`, 'Wire `aiAssistant.onSendMessage` to your LLM to get real answers.' ].join('\n'); } export function JsonataEditorDemo() { const [showPanel, setShowPanel] = useState(false); const [orientation, setOrientation] = useState
)} ); } ``` ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-jsonata-editor ``` **Dependencies:** - [jsonata](https://www.npmjs.com/package/jsonata) - [@uiw/react-codemirror](https://www.npmjs.com/package/@uiw/react-codemirror) - [@uiw/codemirror-extensions-langs](https://www.npmjs.com/package/@uiw/codemirror-extensions-langs) - [@codemirror/autocomplete](https://www.npmjs.com/package/@codemirror/autocomplete) - [@codemirror/language](https://www.npmjs.com/package/@codemirror/language) - [@codemirror/lint](https://www.npmjs.com/package/@codemirror/lint) - [@codemirror/state](https://www.npmjs.com/package/@codemirror/state) - [@codemirror/view](https://www.npmjs.com/package/@codemirror/view) - [@lezer/highlight](https://www.npmjs.com/package/@lezer/highlight) - [lucide-react](https://www.npmjs.com/package/lucide-react) ## Overview `JsonataEditor` is a self-contained workbench for the [JSONata](https://jsonata.org/) query and transformation language — modeled on the official JSONata Exerciser. It has three panes: | Pane | Role | |------|------| | **Input JSON** | A CodeMirror editor for the JSON document the expression runs against. | | **Expression** | A JSONata editor with syntax highlighting, autocomplete, hover docs and inline parse-error linting. | | **Result** | A read-only, live-updating view of the evaluated result (or the error). | Evaluation happens automatically as you type (debounced). Expressions are guarded by a timebox so an infinite loop or runaway recursion can't lock the page. ## Features - **Live evaluation** — debounced re-evaluation on every input or expression change. - **IntelliSense** — autocomplete with snippet expansion for ~60 built-in `$` functions, grouped by category. - **Hover documentation** — signatures, parameter docs and examples on hover. - **Inline linting** — parse errors are underlined in the expression editor. - **Timeboxed** — runaway recursion and infinite loops are aborted (`evaluationTimeout`). - **Variable bindings** — inject values accessible as `$name` inside the expression. - **Samples menu** — load ready-made input + expression pairs. - **AI Assistant drawer** — opt-in chat panel built on ai-elements that slides in from the left and wires to your own LLM via `onSendMessage`. - **Controlled or uncontrolled** — drive `expression` / `input` yourself or let the component own them. - **Flexible layout** — `horizontal` or `vertical`, with the input and result panes individually toggleable. - **Theme-aware** — follows the Docyrus light / dark theme. ## Usage ```tsx import { JsonataEditor } from "@docyrus/ui/components/jsonata-editor"; export function Example() { return ( console.log(result)} /> ); } ``` ### Controlled Drive both panes from your own state: ```tsx const [expression, setExpression] = useState(""); const [input, setInput] = useState("{}"); ``` ### Variable bindings Values passed via `bindings` are available inside the expression as `$name`: ```tsx ``` > Memoize the `bindings` object if it is dynamic — the editor reads it on each > evaluation but does not re-evaluate when only `bindings` changes. ## AI Assistant Pass an `aiAssistant` config to add an **AI Assistant** button to the toolbar. Clicking it slides a chat drawer in from the left of the editor, built on ai-elements (`Conversation`, `Message`). Wire `onSendMessage` to your LLM and return the reply (sync or async): ```tsx { const response = await fetch("/api/chat", { method: "POST", body: JSON.stringify({ message, expression, input, history }) }); const { reply } = await response.json(); return reply; }, suggestions: [ "Explain this expression", "Show me a $map example", "How do I filter an array?" ] }} /> ``` Pass `aiAssistant={true}` to mount the chat shell without a backend (useful while wiring one — messages are stored locally but no reply arrives). ## Standalone expression editor `JsonataCodeEditor` is the expression pane on its own — drop it into a form or an automation node config when you only need to capture a JSONata expression. ```tsx import { JsonataCodeEditor } from "@docyrus/ui/components/jsonata-editor"; const [value, setValue] = useState(""); ``` ## Headless evaluation `useJsonata` debounces and evaluates an expression against an already-parsed input value, with no UI: ```tsx import { useJsonata } from "@docyrus/ui/components/jsonata-editor"; function useTotal(order: unknown) { const state = useJsonata("$sum(items.price)", order); return state.status === "success" ? state.result : undefined; } ``` For a one-off evaluation outside React, use `evaluateJsonata`: ```tsx import { evaluateJsonata } from "@docyrus/ui/components/jsonata-editor"; const state = await evaluateJsonata("$count(items)", { items: [1, 2, 3] }); // → { status: "success", result: 3 } ``` ## EditorAgent integration Pair `useApplyJsonata` with `renderAiAssistant` to give an LLM a writable expression pane. The hook owns the editor's controlled state and ships a `applyJsonata` tool that the agent calls to commit a new expression — the tool also evaluates against the current input JSON and returns a result preview so the agent can self-correct. ```tsx import { EditorAgent } from '@docyrus/ui/components/editor-agent'; import { JsonataEditor, useApplyJsonata } from '@docyrus/ui/components/jsonata-editor'; export function JsonataPlayground({ client, user, agentId }) { const jsonata = useApplyJsonata(); const [aiOpen, setAiOpen] = useState(false); return ( ( )} /> ); } ``` The matching backend agent must register a tool named `applyJsonata` whose input schema mirrors `{ expression: string (required), input?: any, explanation?: string }`. ## API Reference ### JsonataEditor | Prop | Type | Default | Description | |------|------|---------|-------------| | `expression` | `string` | — | Controlled JSONata expression. | | `defaultExpression` | `string` | — | Initial expression for uncontrolled usage. | | `onExpressionChange` | `(expression: string) => void` | — | Fired whenever the expression changes. | | `input` | `string \| unknown` | — | Controlled JSON input — a string or any JSON-serializable value. | | `defaultInput` | `string \| unknown` | — | Initial JSON input for uncontrolled usage. | | `onInputChange` | `(input: string) => void` | — | Fired whenever the input text changes. | | `bindings` | `Record` | — | Variable bindings injected into `evaluate()` (accessible as `$name`). | | `onResult` | `(result: unknown) => void` | — | Fired after a successful evaluation. | | `onError` | `(error: JsonataEvaluationError) => void` | — | Fired when parsing the input or running the expression fails. | | `onEvaluate` | `(state: JsonataEvaluationState) => void` | — | Fired after every evaluation, regardless of outcome. | | `samples` | `JsonataSample[]` | — | Pre-defined input + expression pairs for the samples menu. | | `aiAssistant` | `boolean \| JsonataAIAssistantConfig` | — | When provided, mounts the built-in AI Assistant drawer on the left. Pass `true` for the UI shell only, or a config object with `onSendMessage` to wire a backend. | | `aiAssistantOpen` | `boolean` | — | Controlled open state for the AI Assistant drawer. When provided the editor stops managing the open state internally — pair with `onAiAssistantOpenChange`. | | `onAiAssistantOpenChange` | `(open: boolean) => void` | — | Fired when the AI Assistant toolbar button toggles the drawer. | | `renderAiAssistant` | `(ctx: IJsonataAiAssistantRenderContext) => ReactNode` | — | Replaces the built-in drawer body. When set, the toolbar button is shown even if `aiAssistant` is omitted, the drawer animates in as usual, and this render fn supplies the body. Use this to mount a custom agent (e.g. ``) inside the drawer. | | `showInput` | `boolean` | `true` | Show the JSON input pane. | | `showResult` | `boolean` | `true` | Show the result pane. | | `showToolbar` | `boolean` | `true` | Show the header toolbar. | | `title` | `string` | `'JSONata'` | Title shown in the toolbar. | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Pane arrangement. | | `debounceMs` | `number` | `300` | Debounce before evaluating, in ms. | | `evaluationTimeout` | `number` | `1000` | Evaluation timeout in ms. `0` disables the timebox. | | `readOnly` | `boolean` | `false` | Disables editing of both panes. | | `height` | `number \| string` | `'28rem'` | Overall editor height. | | `placeholder` | `string` | — | Placeholder shown in the empty expression pane. | | `className` | `string` | — | Root element className. | ### JsonataCodeEditor | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `string` | — | Current expression text. | | `onChange` | `(value: string) => void` | — | Fired on every edit. | | `readOnly` | `boolean` | `false` | Disables editing. | | `placeholder` | `string` | — | Placeholder shown when empty. | | `autoFocus` | `boolean` | `false` | Focus the editor on mount. | | `minHeight` | `string` | `'2.5rem'` | Minimum editor height. | | `maxHeight` | `string` | `'12rem'` | Maximum editor height. | | `height` | `string` | — | Fixed editor height — overrides `minHeight` / `maxHeight`. | | `lineNumbers` | `boolean` | `false` | Show line numbers. | | `lint` | `boolean` | `true` | Enable the parse-error linter. | | `basicSetup` | `BasicSetupOptions` | — | CodeMirror `basicSetup` overrides. | | `extensions` | `Extension[]` | — | Extra CodeMirror extensions appended after the JSONata language. | | `className` | `string` | — | Wrapper className. | ### useJsonata ```ts useJsonata(expression: string, input: unknown, options?: UseJsonataOptions): JsonataEvaluationState ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `debounceMs` | `number` | `300` | Debounce before evaluating, in ms. | | `enabled` | `boolean` | `true` | When `false`, evaluation is paused. | | `bindings` | `Record` | — | Variable bindings injected into `evaluate()`. | | `timeout` | `number` | `1000` | Evaluation timeout in ms. `0` disables the timebox. | | `maxDepth` | `number` | `500` | Maximum recursion depth before aborting. | ## Components | Component | Description | |-----------|-------------| | `JsonataEditor` | The three-pane input / expression / result workbench. | | `JsonataCodeEditor` | The standalone JSONata expression editor. | | `useApplyJsonata` | Hook that owns `expression` + `input` state and exposes the `applyJsonata` client-side tool for `` — the tool writes the agent's expression AND evaluates it, returning a result preview or a categorized error (input-empty / parse / evaluate). See **EditorAgent integration** below. | ## Type Exports | Type | Description | |------|-------------| | `JsonataEditorProps` | Props for `JsonataEditor`. | | `JsonataCodeEditorProps` | Props for `JsonataCodeEditor`. | | `JsonataEditorOrientation` | `'horizontal' \| 'vertical'`. | | `JsonataSample` | A `{ name, description?, input, expression }` sample pair. | | `JsonataAIAssistantConfig` | Config for the AI Assistant drawer — `onSendMessage`, `suggestions`, `title`, `defaultOpen`, `width`, `placeholder`, `emptyStateDescription`. | | `IJsonataAiAssistantRenderContext` | Argument passed to `renderAiAssistant` — `{ open, width, onClose, expression, input }`. | | `IUseApplyJsonataResult` | Return shape of `useApplyJsonata` — `{ expression, setExpression, input, setInput, tools }`. | | `JsonataAIMessageContext` | Context passed to `onSendMessage`: current `expression`, `input` and `history`. | | `JsonataChatMessage` | A `{ id, role: 'user' \| 'assistant', content }` chat entry. | | `JsonataEvaluationState` | Discriminated union describing an evaluation outcome. | | `JsonataEvaluationError` | Normalized parse / evaluation error (`phase`, `message`, `code?`, `position?`, `token?`). | | `JsonataErrorPhase` | `'input' \| 'parse' \| 'evaluate'`. | | `JsonataEvaluateOptions` | Options for `evaluateJsonata`. | | `UseJsonataOptions` | Options for the `useJsonata` hook. | | `JsonataFunction` | A built-in function descriptor used by IntelliSense. | | `JsonataFunctionParam` | A single parameter of a `JsonataFunction`. | ## Helpers | Export | Description | |--------|-------------| | `useJsonata` | Headless hook — debounced evaluation against a parsed input. | | `evaluateJsonata` | Compiles and runs an expression; never throws. | | `parseJsonInput` | Parses the JSON input text into a value or an error. | | `stringifyResult` | Pretty-prints an evaluation result for display. | | `jsonataExtensions` | CodeMirror extension bundle (language, autocomplete, hover, linter). | | `jsonataLanguage` | CodeMirror `LanguageSupport` for JSONata. | | `JSONATA_FUNCTIONS` | The built-in function catalog. | | `JSONATA_FUNCTION_MAP` | `Map` of function name → descriptor. | | `JSONATA_SAMPLES` | Ready-made sample input + expression pairs. |