From f029b8469942d6838bfd3b6e0285472b5232d932 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 10 Jun 2026 13:02:35 +0200 Subject: [PATCH 1/3] fix(frontend): forward className in Editor noProvider mode and clean up workarounds The shared Editor dropped `className`/`editorClassName` when rendered with `noProvider` (it only applied the class on the EditorProvider path). Forward it to EditorInner and apply it to the editor container in that mode too. With the prop working, clean up the call sites that relied on the drop: - ChatMessageEditor: move the message text/placeholder alignment from the globals.css workaround to editorClassName; globals.css now only carries the antd role-button override that cannot go through the prop. - VariableControlAdapter: stop forwarding the generation-row container class as editorClassName (it is a container/cell-strip style that would strip the editor's border and gutter); it stays on the container only. - GenerationComparisonChatOutput: pad the editor body via editorClassName instead of the [&_.agenta-editor-wrapper] container hack. --- web/oss/src/styles/globals.css | 28 +++++-------------- .../GenerationComparisonChatOutput/index.tsx | 7 +++-- .../adapters/VariableControlAdapter.tsx | 9 +++++- .../components/ChatMessageEditor.tsx | 17 +++++++---- web/packages/agenta-ui/src/Editor/Editor.tsx | 14 +++++++++- 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/web/oss/src/styles/globals.css b/web/oss/src/styles/globals.css index bcf2162872..35ebc409b2 100644 --- a/web/oss/src/styles/globals.css +++ b/web/oss/src/styles/globals.css @@ -206,28 +206,14 @@ body { } } -/* Align the message text with the first character of the role label above it, - with the same symmetric horizontal inset on the role, the text, and the - placeholder. The single inset value lives in --ag-message-inline-pad. - - Notes: - - The role label is an antd Button whose default padding is wider than the - inset (Tailwind's px-2 on it loses to antd), so it is pinned with !important. - - The text is padded here in CSS rather than via an editor prop because - ChatMessageEditor renders the Editor with `noProvider`, a mode where - `className`/`editorClassName` is currently dropped (known bug, tracked - separately). JSON/code editors are excluded; they have a line-number gutter. */ -.agenta-chat-message-editor { - --ag-message-inline-pad: 8px; -} +/* Align the role label's first character with the message text below it. + The message text and placeholder are inset 8px via ChatMessageEditor's + editorClassName prop; this rule pins the role label to the same 8px. The role + label is an antd Button whose default padding is wider than 8px and ignores + Tailwind's px-2, so the override needs !important. Scoped to the message + editor via the .agenta-chat-message-editor marker class. */ .agenta-chat-message-editor .message-user-select { - padding-inline: var(--ag-message-inline-pad) !important; -} -.agenta-chat-message-editor .editor-input:not(.code-only) { - padding-inline: var(--ag-message-inline-pad); -} -.agenta-chat-message-editor .editor-placeholder { - left: var(--ag-message-inline-pad); + padding-inline: 8px !important; } /** Align the input search with the search box **/ diff --git a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx index 8bff03803b..24e99dcabd 100644 --- a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx +++ b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx @@ -150,8 +150,11 @@ const GenerationComparisonChatOutputCell = ({ allowFileUpload: true, }} messageProps={{ - className: - "!p-0 [&_.agenta-editor-wrapper]:!p-3 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", + className: "!p-0 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", + // Editor body padding now goes through the + // editorClassName prop (works after the noProvider + // className fix), replacing the previous + // `[&_.agenta-editor-wrapper]:!p-3` container hack. editorClassName: "!p-3", headerClassName: "min-h-[48px] px-2 border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]", diff --git a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx index 0a9ce04c2c..0018464a4d 100644 --- a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx +++ b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx @@ -549,7 +549,14 @@ const VariableControlAdapter: React.FC = ({ handleChange={handleChange} initialValue={effectiveValue} value={effectiveValue} - editorClassName={className} + // NOTE: the parent's `className` is a container/cell-strip style + // (e.g. `*:!border-none px-3`) and is applied to the container + // below. It must NOT be forwarded to the editor body, where it + // would strip the editor's own border and gutter (see the + // matching note on the code-editor branch above). It was + // previously passed as `editorClassName` but silently dropped by + // the `noProvider` className bug; now that the bug is fixed, the + // forward is removed so it stays a container-only style. placeholder={effectivePlaceholder} disabled={isEffectivelyDisabled} className={clsx( diff --git a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx index 85ceca76ef..9add5fe5a3 100644 --- a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx +++ b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx @@ -210,14 +210,21 @@ const ChatMessageEditorInner: React.FC = ({ // // Kaosiso QA 2026-06-02 (also reproduces in production). disableDebounce - editorClassName={editorClassName} + // Inset the message text and its placeholder by 8px so they line up + // with the role label above. Goes through editorClassName (the proper + // prop) now that the noProvider className bug is fixed. Code editors + // (JSON/tool) keep their own gutter, so they are excluded via + // `:not(.code-only)`. The role label is an antd button outside the + // editor and is aligned via the scoped rule in globals.css. + editorClassName={cn( + "[&_.editor-input:not(.code-only)]:px-2 [&_.editor-placeholder]:left-2", + editorClassName, + )} placeholder={placeholder} disabled={disabled} state={disabled ? "readOnly" : state} - // `agenta-chat-message-editor` is the styling hook used in globals.css - // to align the message text with the role label (see that file). The - // padding can't go through `editorClassName` because ChatMessageEditor - // renders the Editor with `noProvider`, where `className` is dropped. + // `agenta-chat-message-editor` scopes the role-label alignment rule in + // globals.css (the role is an antd button, not part of the editor). className={cn( "agenta-chat-message-editor relative", flexLayouts.column, diff --git a/web/packages/agenta-ui/src/Editor/Editor.tsx b/web/packages/agenta-ui/src/Editor/Editor.tsx index 2b3378c302..a4cae15880 100644 --- a/web/packages/agenta-ui/src/Editor/Editor.tsx +++ b/web/packages/agenta-ui/src/Editor/Editor.tsx @@ -173,6 +173,7 @@ const EditorInner = forwardRef( disableIndentationPlugin = false, useNativeCodeNodes = false, diffExtensionConfig, + className, ...rest }: EditorProps, ref, @@ -755,7 +756,17 @@ const EditorInner = forwardRef( }, [codeOnly, editor, effectiveValue, hydrateRichTextFromControlledValue]) return ( -
+
Date: Wed, 10 Jun 2026 13:03:23 +0200 Subject: [PATCH 2/3] fix(frontend): stop role-alignment inset stacking with comparison cell padding The comparison view renders messages with their own editor padding (!p-3 cells). The role-alignment inset added by ChatMessageEditor stacked on top of that, over-padding the message text, and the assistant output cell also double-padded (the [&_.agenta-editor-wrapper]:!p-3 hack plus editorClassName). Add an alignTextWithRole prop (default true) to ChatMessageEditor; the comparison view passes false so the inset and role pin are skipped there. Remove the leftover wrapper-padding hack so the comparison cells pad the editor once, via editorClassName. --- .../GenerationComparisonChatOutput/index.tsx | 18 +++++++++++----- .../components/ChatMessageEditor.tsx | 21 ++++++++++++++----- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx index 24e99dcabd..d673ed3f65 100644 --- a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx +++ b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx @@ -151,11 +151,14 @@ const GenerationComparisonChatOutputCell = ({ }} messageProps={{ className: "!p-0 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", - // Editor body padding now goes through the - // editorClassName prop (works after the noProvider - // className fix), replacing the previous + // Comparison cells set their own editor padding via + // editorClassName (works after the noProvider className + // fix), replacing the previous // `[&_.agenta-editor-wrapper]:!p-3` container hack. + // alignTextWithRole is off so the role-alignment inset + // does not stack on top of this padding. editorClassName: "!p-3", + alignTextWithRole: false, headerClassName: "min-h-[48px] px-2 border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]", footerClassName: "px-2", @@ -201,9 +204,14 @@ const GenerationComparisonChatOutputCell = ({ withControls={false} hideUserMessage messageProps={{ - className: - "!p-0 [&_.agenta-editor-wrapper]:!p-3 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", + // Padding via editorClassName only (the + // [&_.agenta-editor-wrapper]:!p-3 hack is removed so it does + // not double up now that editorClassName works), and + // alignTextWithRole off so the role-alignment inset does not + // stack on top. + className: "!p-0 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", editorClassName: "!p-3", + alignTextWithRole: false, headerClassName: "min-h-[48px] border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]", footerClassName: "px-2 !m-0", diff --git a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx index 9add5fe5a3..a0abbd5aae 100644 --- a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx +++ b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx @@ -74,6 +74,14 @@ export interface ChatMessageEditorProps { * up via an internal synchronizer mounted inside the EditorProvider. */ markdownView?: boolean + /** + * Inset the message text + placeholder so they line up with the role label + * above (the default tight message layout). Set to false for surfaces that + * apply their own editor padding (e.g. the comparison view's `!p-3` cells), + * where the alignment inset would stack on top and over-pad the text. + * @default true + */ + alignTextWithRole?: boolean } /** @@ -134,6 +142,7 @@ const ChatMessageEditorInner: React.FC = ({ onFocusChange, maxPasteChars = DEFAULT_MAX_TEXT_PASTE_CHARS, onPasteLimitExceeded, + alignTextWithRole = true, ...props }) => { const selectOptions = useMemo( @@ -215,18 +224,20 @@ const ChatMessageEditorInner: React.FC = ({ // prop) now that the noProvider className bug is fixed. Code editors // (JSON/tool) keep their own gutter, so they are excluded via // `:not(.code-only)`. The role label is an antd button outside the - // editor and is aligned via the scoped rule in globals.css. + // editor and is aligned via the `.agenta-chat-message-editor` rule in + // globals.css. Both are skipped when alignTextWithRole is false (e.g. + // the comparison view, which applies its own editor padding). editorClassName={cn( - "[&_.editor-input:not(.code-only)]:px-2 [&_.editor-placeholder]:left-2", + alignTextWithRole && + "[&_.editor-input:not(.code-only)]:px-2 [&_.editor-placeholder]:left-2", editorClassName, )} placeholder={placeholder} disabled={disabled} state={disabled ? "readOnly" : state} - // `agenta-chat-message-editor` scopes the role-label alignment rule in - // globals.css (the role is an antd button, not part of the editor). className={cn( - "agenta-chat-message-editor relative", + alignTextWithRole && "agenta-chat-message-editor", + "relative", flexLayouts.column, gapClasses.xs, "rounded-md", From 91c45854b7b9c60e4f6b368996c7c6f64a4fd7e1 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 10 Jun 2026 13:07:37 +0200 Subject: [PATCH 3/3] chore(frontend): document Editor className landing node and trim comment narration Review follow-ups for #4555: - Document on EditorProps.className that the class lands on a different node per mode (.agenta-rich-text-editor in provider mode, the .editor-container div with noProvider), so future noProvider toggles do not silently re-style a different box. - Trim comments that narrated the history of the noProvider bug down to the standing constraints. --- .../GenerationComparisonChatOutput/index.tsx | 17 ++++++----------- .../adapters/VariableControlAdapter.tsx | 11 ++++------- .../components/ChatMessageEditor.tsx | 13 ++++++------- web/packages/agenta-ui/src/Editor/Editor.tsx | 9 ++++----- web/packages/agenta-ui/src/Editor/types.d.ts | 9 +++++++++ 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx index d673ed3f65..429054e285 100644 --- a/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx +++ b/web/packages/agenta-playground-ui/src/components/ExecutionItemComparisonView/GenerationComparisonChatOutput/index.tsx @@ -151,12 +151,10 @@ const GenerationComparisonChatOutputCell = ({ }} messageProps={{ className: "!p-0 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", - // Comparison cells set their own editor padding via - // editorClassName (works after the noProvider className - // fix), replacing the previous - // `[&_.agenta-editor-wrapper]:!p-3` container hack. - // alignTextWithRole is off so the role-alignment inset - // does not stack on top of this padding. + // Comparison cells pad the editor body via + // editorClassName; alignTextWithRole is off so the + // role-alignment inset does not stack on top of this + // padding. editorClassName: "!p-3", alignTextWithRole: false, headerClassName: @@ -204,11 +202,8 @@ const GenerationComparisonChatOutputCell = ({ withControls={false} hideUserMessage messageProps={{ - // Padding via editorClassName only (the - // [&_.agenta-editor-wrapper]:!p-3 hack is removed so it does - // not double up now that editorClassName works), and - // alignTextWithRole off so the role-alignment inset does not - // stack on top. + // Padding via editorClassName only; alignTextWithRole off so + // the role-alignment inset does not stack on top. className: "!p-0 !mt-0 [&:nth-child(1)]:!mt-0 mt-2", editorClassName: "!p-3", alignTextWithRole: false, diff --git a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx index 0018464a4d..ab14a9075d 100644 --- a/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx +++ b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx @@ -550,13 +550,10 @@ const VariableControlAdapter: React.FC = ({ initialValue={effectiveValue} value={effectiveValue} // NOTE: the parent's `className` is a container/cell-strip style - // (e.g. `*:!border-none px-3`) and is applied to the container - // below. It must NOT be forwarded to the editor body, where it - // would strip the editor's own border and gutter (see the - // matching note on the code-editor branch above). It was - // previously passed as `editorClassName` but silently dropped by - // the `noProvider` className bug; now that the bug is fixed, the - // forward is removed so it stays a container-only style. + // (e.g. `*:!border-none px-3`) applied to the container below. It + // must NOT be forwarded as `editorClassName` — on the editor body + // it strips the editor's own border and gutter (see the matching + // note on the code-editor branch above). placeholder={effectivePlaceholder} disabled={isEffectivelyDisabled} className={clsx( diff --git a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx index a0abbd5aae..7dd863d058 100644 --- a/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx +++ b/web/packages/agenta-ui/src/ChatMessage/components/ChatMessageEditor.tsx @@ -220,13 +220,12 @@ const ChatMessageEditorInner: React.FC = ({ // Kaosiso QA 2026-06-02 (also reproduces in production). disableDebounce // Inset the message text and its placeholder by 8px so they line up - // with the role label above. Goes through editorClassName (the proper - // prop) now that the noProvider className bug is fixed. Code editors - // (JSON/tool) keep their own gutter, so they are excluded via - // `:not(.code-only)`. The role label is an antd button outside the - // editor and is aligned via the `.agenta-chat-message-editor` rule in - // globals.css. Both are skipped when alignTextWithRole is false (e.g. - // the comparison view, which applies its own editor padding). + // with the role label above. Code editors (JSON/tool) keep their own + // gutter, so they are excluded via `:not(.code-only)`. The role label + // is an antd button outside the editor, aligned by the + // `.agenta-chat-message-editor` rule in globals.css. Both insets are + // skipped when alignTextWithRole is false (e.g. the comparison view, + // which applies its own editor padding). editorClassName={cn( alignTextWithRole && "[&_.editor-input:not(.code-only)]:px-2 [&_.editor-placeholder]:left-2", diff --git a/web/packages/agenta-ui/src/Editor/Editor.tsx b/web/packages/agenta-ui/src/Editor/Editor.tsx index a4cae15880..c6b12194aa 100644 --- a/web/packages/agenta-ui/src/Editor/Editor.tsx +++ b/web/packages/agenta-ui/src/Editor/Editor.tsx @@ -759,11 +759,10 @@ const EditorInner = forwardRef(
diff --git a/web/packages/agenta-ui/src/Editor/types.d.ts b/web/packages/agenta-ui/src/Editor/types.d.ts index 83f2bfc9e5..adba72b8fe 100644 --- a/web/packages/agenta-ui/src/Editor/types.d.ts +++ b/web/packages/agenta-ui/src/Editor/types.d.ts @@ -26,6 +26,15 @@ export interface EditorProviderProps extends React.HTMLProps { } export interface EditorProps extends React.HTMLProps { + /** + * Lands on a different node per mode: in provider mode on the + * `.agenta-rich-text-editor` wrapper (an ancestor of the editor + * container), with `noProvider` on the `.editor-container` div itself. + * Descendant selectors (`[&_...]`) behave the same in both modes, but + * box-level utilities (padding, width, border) style a different element + * depending on the mode. + */ + className?: string disabled?: boolean id?: string initialEditorState?: LexicalEditor["_editorState"]