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..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 @@ -150,9 +150,13 @@ 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", + // 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: "min-h-[48px] px-2 border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]", footerClassName: "px-2", @@ -198,9 +202,11 @@ 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; 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-playground-ui/src/components/adapters/VariableControlAdapter.tsx b/web/packages/agenta-playground-ui/src/components/adapters/VariableControlAdapter.tsx index 0a9ce04c2c..ab14a9075d 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,11 @@ 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`) 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 85ceca76ef..7dd863d058 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( @@ -210,16 +219,24 @@ 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. 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", + 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. className={cn( - "agenta-chat-message-editor relative", + alignTextWithRole && "agenta-chat-message-editor", + "relative", flexLayouts.column, gapClasses.xs, "rounded-md", diff --git a/web/packages/agenta-ui/src/Editor/Editor.tsx b/web/packages/agenta-ui/src/Editor/Editor.tsx index 2b3378c302..c6b12194aa 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,16 @@ const EditorInner = forwardRef( }, [codeOnly, editor, effectiveValue, hydrateRichTextFromControlledValue]) return ( -
+
{ } 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"]