Skip to content

Commit da75572

Browse files
committed
fix(ui): suppress "No suggestions" popup while typing in strings
- 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
1 parent 7bfa18d commit da75572

2 files changed

Lines changed: 39 additions & 32 deletions

File tree

‎ui/src/core/page-components/agent-detail/modals/edit-control/json-editor-language.ts‎

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,9 +1859,12 @@ function buildNodeTransformAction(
18591859
title: string,
18601860
transform: (parsed: unknown) => unknown
18611861
): import('monaco-editor').languages.CodeAction | null {
1862-
const nodeText = model
1863-
.getValue()
1864-
.substring(node.offset, node.offset + node.length);
1862+
// Parse the full document, apply the transform to the target node,
1863+
// then re-serialize the whole document. This produces a single edit
1864+
// that replaces the entire content with properly formatted JSON,
1865+
// making undo a clean single-step revert.
1866+
const fullText = model.getValue();
1867+
const nodeText = fullText.substring(node.offset, node.offset + node.length);
18651868
let parsed: unknown;
18661869
try {
18671870
parsed = JSON.parse(nodeText);
@@ -1872,9 +1875,25 @@ function buildNodeTransformAction(
18721875
const result = transform(parsed);
18731876
if (result === undefined) return null;
18741877

1875-
const newText = JSON.stringify(result, null, 2);
1876-
const startPos = model.getPositionAt(node.offset);
1877-
const endPos = model.getPositionAt(node.offset + node.length);
1878+
// Rebuild full document with the transformed node
1879+
const newNodeText = JSON.stringify(result);
1880+
const rawDoc =
1881+
fullText.substring(0, node.offset) +
1882+
newNodeText +
1883+
fullText.substring(node.offset + node.length);
1884+
1885+
let newText: string;
1886+
try {
1887+
newText = JSON.stringify(JSON.parse(rawDoc), null, 2);
1888+
} catch {
1889+
// Fallback: just replace the node
1890+
newText =
1891+
fullText.substring(0, node.offset) +
1892+
JSON.stringify(result, null, 2) +
1893+
fullText.substring(node.offset + node.length);
1894+
}
1895+
1896+
const fullRange = model.getFullModelRange();
18781897

18791898
return {
18801899
title,
@@ -1885,10 +1904,10 @@ function buildNodeTransformAction(
18851904
resource: model.uri,
18861905
textEdit: {
18871906
range: new monaco.Range(
1888-
startPos.lineNumber,
1889-
startPos.column,
1890-
endPos.lineNumber,
1891-
endPos.column
1907+
fullRange.startLineNumber,
1908+
fullRange.startColumn,
1909+
fullRange.endLineNumber,
1910+
fullRange.endColumn
18921911
),
18931912
text: newText,
18941913
},

‎ui/src/core/page-components/agent-detail/modals/edit-control/json-editor-view.tsx‎

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ if (typeof document !== 'undefined') {
7474
const EDITOR_OPTIONS: import('monaco-editor').editor.IStandaloneEditorConstructionOptions =
7575
{
7676
automaticLayout: true,
77-
quickSuggestions: { other: true, strings: true, comments: false },
77+
quickSuggestions: { other: true, strings: false, comments: false },
7878
suggestOnTriggerCharacters: true,
7979
wordBasedSuggestions: 'off',
8080
suggest: {
@@ -366,26 +366,9 @@ export const JsonEditorView = ({
366366

367367
const text = editor.getValue();
368368

369-
// Deferred reformat for multi-line edits (e.g., code actions like "Wrap in AND").
370-
// Must use queueMicrotask — editing inside onDidChangeModelContent crashes Monaco.
371-
const isMultiLineEdit =
372-
!e.isUndoing &&
373-
!e.isRedoing &&
374-
e.changes.length === 1 &&
375-
(e.changes[0].text.includes('\n') ||
376-
e.changes[0].range.endLineNumber >
377-
e.changes[0].range.startLineNumber);
378-
if (isMultiLineEdit) {
379-
queueMicrotask(() => {
380-
const current = editor.getValue();
381-
const formatted = tryFormat(current);
382-
if (formatted && formatted !== current) {
383-
isProgrammaticEdit = true;
384-
replaceAllContent(editor, formatted, 'auto-reformat');
385-
handleJsonChange(formatted);
386-
}
387-
});
388-
}
369+
// No auto-reformat here — code actions produce formatted JSON by replacing
370+
// the full document (see buildNodeTransformAction). Auto-reformatting would
371+
// create a second undo entry that breaks Ctrl+Z.
389372

390373
// Debounced hints
391374
if (hintTimer) window.clearTimeout(hintTimer);
@@ -443,7 +426,12 @@ export const JsonEditorView = ({
443426
if (!editor || !mounted) return;
444427

445428
let timeout: number | null = null;
446-
const disposable = editor.onDidChangeCursorPosition(() => {
429+
const disposable = editor.onDidChangeCursorPosition((e) => {
430+
// Skip cursor changes from typing — quickSuggestions handles those.
431+
// Only auto-trigger on navigation (click, arrow keys, etc.).
432+
// CursorChangeReason: NotSet=0, ContentFlush=1, RecoverFromMarkers=2, Explicit=3, Paste=4, Undo=5, Redo=6
433+
if (e.reason !== 3 /* Explicit */) return;
434+
447435
if (timeout) window.clearTimeout(timeout);
448436
timeout = window.setTimeout(() => {
449437
try {

0 commit comments

Comments
 (0)