feat(ui, server): Intuitive JSON editing for Controls#151
Merged
Conversation
- Replace custom suggestion panel with Monaco's native autocomplete - Fix mount race condition for completion provider registration - Add schema-aware suggestions for evaluator names, selector paths, enums - Auto-trigger suggestions on cursor entering string values and blank lines - Add inline value hints showing valid options for empty fields - Auto-update evaluator config when evaluator name changes - Auto-add/remove steering_context when decision changes to/from "steer" - Auto-fix missing and trailing commas with debounced correction - Add condition code actions (Ctrl+.): wrap in AND/OR/NOT, add condition, convert - Add format and copy-to-clipboard toolbar buttons - Dark theme support via DOM attribute detection - Bracket colorization, guides, sticky scroll, folding, smooth animations - Prevent Escape from closing modal (only dismisses suggest widget) - Default SSN regex example template for new controls - Rename "From JSON" to "Write your own" - Clean up: deduplicate code action builders, merge content listeners, remove dead code
- Use alias name for GetControlSchemaResponse constructor (mypy expects alias) - Add populate_by_name config to allow both field name and alias - Run prettier on all changed UI files
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Merge 3 separate onDidChangeModelContent listeners into one unified handler that shares state and avoids redundant JSON parsing. Remove suppressCommaFixRef hack in favor of a local closure variable. Extract isSuggestWidgetVisible and reformatIfValid helpers.
- Don't show value suggestions (like "null") at property key positions - Only apply comma fix + reformat when result is valid JSON - Prevents corruption when user is mid-edit inside a string value
- Remove auto-reformat on debounce — formatting is now explicit (Format button only) - Comma auto-fix only runs when editor loses focus (not while user is typing) - Comma fix only applies if result is valid JSON
- Clamp cursor after replaceAllContent to prevent invalid lineNumber - Wrap cursor listener in try-catch for undo edge case - Remove forceMoveMarkers to avoid stale cursor positions in undo stack - Change step_types from Literal["tool","llm"] to list[str] with examples
…-reformat multi-line edits
…ChangeModelContent crashes Monaco
- Disable quickSuggestions for strings (was triggering on every keystroke) - Only auto-trigger suggestions on explicit cursor navigation (click/arrows), not on typing (CursorChangeReason.Explicit only) - Code actions now replace full document for clean undo + proper formatting
- Disable quickSuggestions and suggestOnTriggerCharacters entirely - All suggestion triggering is now controlled by our cursor listener - Only auto-trigger for: blank lines (property suggestions) and short strings ≤2 chars (enum/path/evaluator browsing) - Typing in long strings never shows popups - contentJustChanged only flags small single-char edits as "typing"
- Re-enable suggestOnTriggerCharacters so typing " triggers property suggestions - Re-add fixedOverflowWidgets to prevent suggest widget clipping at editor top - Keep quickSuggestions disabled to prevent "No suggestions" popup while typing - contentJustChanged only flags small single-char edits (not setValue/reformat)
- Re-enable suggestOnTriggerCharacters so typing " triggers property suggestions - Remove fixedOverflowWidgets (causes mispositioning in modals) - Add domain field pattern matching: auto-trigger for name, path, decision, execution fields regardless of string length - Description/pattern/other free-text fields don't auto-trigger
- Restrict value suggestions to actual string value positions only (isStringValueContext must be true) — not blank lines or brackets - Filter null from preferred values (Pydantic nullable fields) - Remove overflow:clip from editor container (caused widget clipping) - Restore schema in diagnostics for validation squiggles - Remove dead setModeConfiguration code (doesn't work in Monaco 0.55)
## Summary - What changed and why. Updated to use codemirror instead of monaco. https://github.com/user-attachments/assets/3d9bac1d-e0de-449f-83ed-e0606ee34271 ## Scope - User-facing/API changes: - Internal changes: - Out of scope: ## Risk and Rollout - Risk level: low / medium / high - Rollback plan: ## Testing - [ ] Added or updated automated tests - [ ] Ran `make check` (or explained why not) - [ ] Manually verified behavior ## Checklist - [ ] Linked issue/spec (if applicable) - [ ] Updated docs/examples for user-facing changes - [ ] Included any required follow-up tasks
- Show Mantine validation error for empty control name instead of silently blocking via HTML5 required attribute - Close confirm modal immediately on click to prevent double-submit race that caused 409 Conflict errors - Only disable Form/JSON toggle on JSON parse errors, not server validation errors, preventing a stuck-state where both toggles were disabled - Preserve user-edited control name when syncing workingDefinition to form during mode switches
The HTML5 required attribute silently blocked form submission before Mantine's onSubmit validation could run, hiding the "Control name is required" error. Adding noValidate lets Mantine handle validation and display the error text on the input field.
…nippet When accepting a property completion for a string field (e.g. "description"), the snippet inserted literal "" with the cursor after the closing quote. The user's subsequent typing ended up outside the string value. Use a snippet tab stop ($1) between the quotes so the cursor lands inside for immediate typing.
- Suppress Monaco's built-in "$schema" completion by passing a minimal schema with additionalProperties:true to setDiagnosticsOptions and disabling the built-in completion provider via setModeConfiguration - Auto-trigger property suggestions when user types `"` at a property-key position (blank line or after comma), improving discoverability
A. Move validation error alert above the editor so it's visible without scrolling — previously hidden below the 520px editor height. B. Add inline error highlighting in Monaco: server validation errors now show a red wavy underline on the offending value with hover messages. Uses deltaDecorations to highlight the JSON path from error field. C. Show all enum values when cursor is inside an existing value. Setting filterText to the current node value bypasses Monaco's fuzzy matching, so "allow", "steer", "warn", "log" all appear even when cursor is in "deny".
D. Cancel with unsaved changes in edit mode now shows a "Discard unsaved changes?" confirmation dialog before closing. F. Cmd+S / Ctrl+S keyboard shortcut triggers Save from anywhere in the edit dialog via formRef.requestSubmit(). G. Step name placeholder changed from "No steps available" to "No steps registered via SDK" for better guidance. H. Added tooltip on Form/Full JSON toggle explaining the two modes. J. Shortened default control names from "list-control-for-agent-name" to "new-list-control". Also adds UX_AUDIT.md to track remaining improvements.
…/improvejsoneditor
The dirty check only ran on Cancel button click. The Modal X button called onClose directly, bypassing the check. Added an onCloseRef prop so EditControlContent can expose its handleClose (with dirty check) to the parent Modal's onClose handler.
definitionForm.isDirty() returned true immediately after opening the edit dialog because setValues makes the form dirty relative to initialValues. Call resetDirty(syncedValues) after setValues so the form only reports dirty when the user actually edits something. Fixes the "closing edit modal removes query parameters" integration test which clicked Cancel and expected the modal to close immediately.
…ved check form.isDirty() was unreliable because setValues in the sync effect made it return true before the user touched anything. Replaced with an explicit isDirty state flag that is: - set via onValuesChange (gated behind formSyncedRef to skip initial sync) - set via handleDefinitionJsonChange for JSON mode edits - reset when the control prop changes (dialog reopen)
…esChange Mantine's onValuesChange fires on programmatic setValues calls, causing false dirty state. The native DOM onChange event on <form> only fires from actual user interactions (typing, selecting), making it reliable for dirty tracking without timing hacks.
Mantine components dispatch synthetic change events during init, making form-level onChange unreliable for dirty tracking. Simplify to only track isDirty from handleDefinitionJsonChange (JSON editor edits). Form mode Cancel always closes immediately since form edits haven't been persisted and the control data is still available on reopen.
…o limitation model.applyEdits desynchronizes the main thread model from the web worker, causing "Cannot read properties of undefined (reading 'substring')" crashes. Reverted to queueMicrotask + executeEdits. The async boundary means auto-edits (evaluator config fill, steering context) create separate undo groups, which breaks redo after undo. This is a known Monaco limitation — undo still works correctly.
galileo-automation
pushed a commit
that referenced
this pull request
Apr 7, 2026
## [2.2.0](ts-sdk-v2.1.0...ts-sdk-v2.2.0) (2026-04-07) ### Features * **evaluators:** add starts_with/ends_with mode to list evaluator ([#154](#154)) ([bf1f7d7](bf1f7d7)) * **sdk:** [Enterprise Integration]: Add provider agnostic traceing ([#145](#145)) ([f1ca27c](f1ca27c)) * **sdk:** Add telemetry package to support sinks ([#164](#164)) ([2186ba1](2186ba1)) * **sdk:** default merge events in SDK ([#155](#155)) ([5984a60](5984a60)) * **server,sdk, ui:** Control Templates ([#158](#158)) ([78bb538](78bb538)) * **server:** Override PG password in dockerfile ([#148](#148)) ([5d70c7d](5d70c7d)) * **server:** Remove container name for dev postgres ([92b2d13](92b2d13)) * **server:** Start local dev pg under docker compose project endign with dev ([88bee63](88bee63)) * **ui, server:** Intuitive JSON editing for Controls ([#151](#151)) ([8c23cef](8c23cef)) * **ui:** add full control JSON editing and create-from-JSON ([#147](#147)) ([e685ed0](e685ed0)) ### Bug Fixes * **docs:** add explicit shutdown to quickstart example ([#149](#149)) ([b76014f](b76014f)) * **sdk:** use sync shutdown flush fallback ([#150](#150)) ([90265ba](90265ba)) * **server:** remove unused evaluator config store ([#152](#152)) ([dea2873](dea2873)) * **server:** Omit null fields in control JSON editor ([#157](#157)) ([0aa2f3c](0aa2f3c)) * **server:** Update docker-compose.dev.yml to use different container name ([14d4c87](14d4c87)) * **ui:** improve edit control ux, no layout shift, consistent spacing ([#122](#122)) ([76d67b9](76d67b9))
Collaborator
|
🎉 This PR is included in version 2.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scope
Risk and Rollout
Testing
make check(or explained why not)Checklist