🧩 style: Agent Side Panel Layout and Consistency Fixes (#12676)

* style: Update padding in ActionsPanel, ModelPanel, and AdvancedPanel for improved layout

* Adjusted padding in ActionsPanel, ModelPanel, and AdvancedPanel components to enhance visual consistency and layout.
* Changed `py-4` to `pt-2` in the main container of each panel to reduce vertical spacing and improve overall design aesthetics.

* style: Update text size in MCPTool component for improved accessibility

* Changed text size in the MCPTool component to `text-sm` for better readability and consistency across the UI.
* This adjustment enhances the user experience by ensuring that text is appropriately sized for various display settings.

* style: Enhance layout and accessibility in ActionsInput, ActionsPanel, and AgentPanel components

* Updated the layout in ActionsInput to improve flex properties and ensure better responsiveness.
* Refined the structure of ActionsPanel for a more consistent visual hierarchy and added accessibility features.
* Adjusted the AgentPanel form layout for improved usability and streamlined component integration.
* Changed text size in Dropdown component to `text-sm` for better readability across the UI.

* style: Refactor layout in ActionsInput component for improved responsiveness

* Updated the layout of the ActionsInput component to enhance flex properties and ensure better responsiveness.
* Adjusted the textarea styling for improved usability and consistency in design.
* Removed commented-out code related to example functionality to clean up the component structure.

* refactor: Simplify single line code detection in MarkdownComponents

* Introduced a new utility function `isSingleLineCode` to streamline the logic for determining if code is a single line.
* Updated references in the `MarkdownCode` and `MarkdownCodeNoExecution` components to use the new utility function for improved readability and maintainability.
* Enhanced the `processChildren` function in the Markdown editor to handle non-code elements more effectively.
This commit is contained in:
Danny Avila 2026-04-15 14:27:13 -04:00 committed by GitHub
parent edd4c6d60c
commit dd26a2fda5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 155 additions and 152 deletions

View file

@ -17,6 +17,16 @@ type TCodeProps = {
children: React.ReactNode;
};
const isSingleLineCode = (children: React.ReactNode): boolean => {
if (typeof children === 'string') {
return !children.includes('\n');
}
if (Array.isArray(children)) {
return children.every((child) => typeof child === 'string' && !child.includes('\n'));
}
return false;
};
export const code: React.ElementType = memo(function MarkdownCode({
className,
children,
@ -29,7 +39,7 @@ export const code: React.ElementType = memo(function MarkdownCode({
const lang = match && match[1];
const isMath = lang === 'math';
const isMermaid = lang === 'mermaid';
const isSingleLine = typeof children === 'string' && children.split('\n').length === 1;
const isSingleLine = isSingleLineCode(children);
const { getNextIndex, resetCounter } = useCodeBlockContext();
const blockIndex = useRef(getNextIndex(isMath || isMermaid || isSingleLine)).current;
@ -78,7 +88,7 @@ export const codeNoExecution: React.ElementType = memo(function MarkdownCodeNoEx
} else if (lang === 'mermaid') {
const content = typeof children === 'string' ? children : String(children);
return <Mermaid>{content}</Mermaid>;
} else if (typeof children === 'string' && children.split('\n').length === 1) {
} else if (isSingleLineCode(children)) {
return (
<code onDoubleClick={handleDoubleClick} className={className}>
{children}

View file

@ -44,6 +44,9 @@ const processChildren = (children: React.ReactNode): React.ReactNode => {
if (React.isValidElement(children)) {
const element = children as React.ReactElement<{ children?: React.ReactNode }>;
if (typeof element.type !== 'string' || element.type === 'code') {
return children;
}
if (element.props.children) {
return React.cloneElement(element, {
...element.props,

View file

@ -202,7 +202,7 @@ export default function ActionsInput({
return (
<>
<div className="">
<div className="flex min-h-0 flex-1 flex-col">
<div className="mb-1 flex flex-wrap items-center justify-between gap-4">
<label
htmlFor="schemaInput"
@ -210,31 +210,17 @@ export default function ActionsInput({
>
{localize('com_ui_schema')}
</label>
{/* TODO: Implement examples functionality
<div className="flex items-center gap-2">
<select
onChange={(e) => logger.log('actions', 'selecting example action', e.target.value)}
className="border-token-border-medium h-8 min-w-[100px] rounded-lg border bg-transparent px-2 py-0 text-sm"
>
<option value="label">{localize('com_ui_examples')}</option>
<option value="0">Weather (JSON)</option>
<option value="1">Pet Store (YAML)</option>
<option value="2">Blank Template</option>
</select>
</div>
*/}
</div>
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover mb-4 w-full overflow-hidden rounded-lg border ring-0">
<div className="relative">
<div className="border-token-border-medium bg-token-surface-primary hover:border-token-border-hover mb-4 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border ring-0">
<div className="relative flex min-h-0 flex-1 flex-col">
<textarea
id="schemaInput"
value={inputValue}
onChange={handleInputChange}
spellCheck="false"
placeholder={localize('com_ui_enter_openapi_schema')}
className="text-token-text-primary block h-96 w-full bg-transparent p-2 font-mono text-xs outline-none focus:ring-1 focus:ring-border-light"
className="text-token-text-primary block min-h-[12rem] flex-1 resize-y bg-transparent p-2 font-mono text-xs outline-none focus:ring-1 focus:ring-border-light"
/>
{/* TODO: format input button */}
</div>
{validationResult && validationResult.message !== 'OpenAPI spec is valid.' && (
<div className="border-token-border-light border-t p-2 text-red-500">
@ -270,11 +256,11 @@ export default function ActionsInput({
/>
</div>
</div>
<div className="flex items-center justify-end">
<div className="mt-auto flex items-center justify-end pt-2">
<button
disabled={!functions || !functions.length}
onClick={saveAction}
className="focus:shadow-outline mt-1 flex min-w-[100px] items-center justify-center rounded bg-green-500 px-4 py-2 font-semibold text-white hover:bg-green-400 focus:border-green-500 focus:outline-none focus:ring-0 disabled:bg-green-400"
className="focus:shadow-outline flex min-w-[100px] items-center justify-center rounded bg-green-500 px-4 py-2 font-semibold text-white hover:bg-green-400 focus:border-green-500 focus:outline-none focus:ring-0 disabled:bg-green-400"
type="button"
>
{getButtonContent()}

View file

@ -85,76 +85,81 @@ export default function ActionsPanel() {
return (
<FormProvider {...methods}>
<form className="h-full grow overflow-hidden">
<div className="h-full overflow-auto px-2 pb-12 text-sm">
<div className="relative flex flex-col items-center px-16 py-6 text-center">
<div className="absolute left-0 top-6">
<button
type="button"
className="btn btn-neutral relative"
onClick={() => {
setActivePanel(Panel.builder);
setAction(undefined);
}}
>
<div className="flex w-full items-center justify-center gap-2">
<ChevronLeft />
<div className="h-full overflow-auto px-2 text-sm">
<div className="flex min-h-full flex-col pb-3">
<div>
<div className="relative flex flex-col items-center px-16 pt-2 text-center">
<div className="absolute left-0 top-6">
<button
type="button"
className="btn btn-neutral relative"
onClick={() => {
setActivePanel(Panel.builder);
setAction(undefined);
}}
>
<div className="flex w-full items-center justify-center gap-2">
<ChevronLeft />
</div>
</button>
</div>
</button>
</div>
{!!action && (
<OGDialog>
<OGDialogTrigger asChild>
<div className="absolute right-0 top-6">
<button
type="button"
disabled={isEphemeralAgent(agent_id) || !action.action_id}
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
>
<TrashIcon className="text-red-500" />
</button>
</div>
</OGDialogTrigger>
<OGDialogTemplate
showCloseButton={false}
title={localize('com_ui_delete_action')}
className="max-w-[450px]"
main={
<Label className="text-left text-sm font-medium">
{localize('com_ui_delete_action_confirm')}
</Label>
}
selection={{
selectHandler: () => {
if (isEphemeralAgent(agent_id)) {
return showToast({
message: localize('com_agents_no_agent_id_error'),
status: 'error',
});
{!!action && (
<OGDialog>
<OGDialogTrigger asChild>
<div className="absolute right-0 top-6">
<button
type="button"
disabled={isEphemeralAgent(agent_id) || !action.action_id}
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
>
<TrashIcon className="text-red-500" />
</button>
</div>
</OGDialogTrigger>
<OGDialogTemplate
showCloseButton={false}
title={localize('com_ui_delete_action')}
className="max-w-[450px]"
main={
<Label className="text-left text-sm font-medium">
{localize('com_ui_delete_action_confirm')}
</Label>
}
deleteAgentAction.mutate({
action_id: action.action_id,
agent_id: agent_id || '',
});
},
selectClasses:
'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 transition-color duration-200 text-white',
selectText: localize('com_ui_delete'),
}}
/>
</OGDialog>
)}
selection={{
selectHandler: () => {
if (isEphemeralAgent(agent_id)) {
return showToast({
message: localize('com_agents_no_agent_id_error'),
status: 'error',
});
}
deleteAgentAction.mutate({
action_id: action.action_id,
agent_id: agent_id || '',
});
},
selectClasses:
'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 transition-color duration-200 text-white',
selectText: localize('com_ui_delete'),
}}
/>
</OGDialog>
)}
<div className="text-xl font-medium">{(action ? 'Edit' : 'Add') + ' ' + 'actions'}</div>
<div className="text-xs text-text-secondary">
{localize('com_assistants_actions_info')}
<div className="text-xl font-medium">
{(action ? 'Edit' : 'Add') + ' ' + 'actions'}
</div>
<div className="text-xs text-text-secondary">
{localize('com_assistants_actions_info')}
</div>
</div>
<ActionsAuth />
</div>
<div className="flex flex-1 flex-col">
<ActionsInput action={action} agent_id={agent_id} setAction={setAction} />
</div>
{/* <div className="text-sm text-text-secondary">
<a href="https://help.openai.com/en/articles/8554397-creating-a-gpt" target="_blank" rel="noreferrer" className="font-medium">Learn more.</a>
</div> */}
</div>
<ActionsAuth />
<ActionsInput action={action} agent_id={agent_id} setAction={setAction} />
</div>
</form>
</FormProvider>

View file

@ -23,8 +23,8 @@ export default function AdvancedPanel() {
);
return (
<div className="scrollbar-gutter-stable h-full min-h-[40vh] overflow-auto pb-12 text-sm">
<div className="advanced-panel relative flex flex-col items-center px-16 py-4 text-center">
<div className="mb-1 flex w-full flex-col gap-2 text-sm">
<div className="advanced-panel relative flex flex-col items-center px-16 pt-2 text-center">
<div className="absolute left-0 top-4">
<button
type="button"
@ -41,7 +41,7 @@ export default function AdvancedPanel() {
</div>
<div className="mb-2 mt-2 text-xl font-medium">{localize('com_ui_advanced_settings')}</div>
</div>
<div className="flex flex-col gap-4 px-2">
<div className="flex flex-col gap-4 px-2 pb-2">
<MaxAgentSteps />
<Controller
name="edges"

View file

@ -480,71 +480,70 @@ export default function AgentPanel() {
<FormProvider {...methods}>
<form
onSubmit={handleSubmit(onSubmit)}
className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 px-3 pb-3"
className="scrollbar-gutter-stable flex flex-1 flex-col px-3 pb-3"
aria-label="Agent configuration form"
>
<div className="flex w-full flex-wrap gap-2">
<div className="w-full">
<AgentSelect
createMutation={create}
agentQuery={agentQuery}
setCurrentAgentId={setCurrentAgentId}
// The following is required to force re-render the component when the form's agent ID changes
// Also maintains ComboBox Focus for Accessibility
selectedAgentId={agentQuery.isInitialLoading ? null : (current_agent_id ?? null)}
/>
<div className="flex-1">
<div className="flex w-full flex-wrap gap-2">
<div className="w-full">
<AgentSelect
createMutation={create}
agentQuery={agentQuery}
setCurrentAgentId={setCurrentAgentId}
selectedAgentId={agentQuery.isInitialLoading ? null : (current_agent_id ?? null)}
/>
</div>
{agent_id && (
<div className="flex w-full gap-2">
<Button
type="button"
variant="outline"
className="w-full justify-center"
onClick={() => {
reset(getDefaultAgentFormValues());
setCurrentAgentId(undefined);
}}
disabled={agentQuery.isInitialLoading}
aria-label={localize('com_ui_create_new_agent')}
>
<Plus className="mr-1 h-4 w-4" aria-hidden="true" />
{localize('com_ui_create_new_agent')}
</Button>
<Button
variant="submit"
disabled={isEphemeralAgent(agent_id) || agentQuery.isInitialLoading}
onClick={(e) => {
e.preventDefault();
handleSelectAgent();
}}
aria-label={localize('com_ui_select_agent')}
>
{localize('com_ui_select')}
</Button>
</div>
)}
</div>
{/* Create + Select Button */}
{agent_id && (
<div className="flex w-full gap-2">
<Button
type="button"
variant="outline"
className="w-full justify-center"
onClick={() => {
reset(getDefaultAgentFormValues());
setCurrentAgentId(undefined);
}}
disabled={agentQuery.isInitialLoading}
aria-label={localize('com_ui_create_new_agent')}
>
<Plus className="mr-1 h-4 w-4" aria-hidden="true" />
{localize('com_ui_create_new_agent')}
</Button>
<Button
variant="submit"
disabled={isEphemeralAgent(agent_id) || agentQuery.isInitialLoading}
onClick={(e) => {
e.preventDefault();
handleSelectAgent();
}}
aria-label={localize('com_ui_select_agent')}
>
{localize('com_ui_select')}
</Button>
{agentQuery.isInitialLoading && <AgentPanelSkeleton />}
{!canEditAgent && !agentQuery.isInitialLoading && (
<div className="flex h-[30vh] w-full items-center justify-center">
<div className="text-center">
<h2 className="text-token-text-primary m-2 text-xl font-semibold">
{localize('com_agents_not_available')}
</h2>
<p className="text-token-text-secondary">{localize('com_agents_no_access')}</p>
</div>
</div>
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.model && (
<ModelPanel models={models} providers={providers} setActivePanel={setActivePanel} />
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.builder && (
<AgentConfig />
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.advanced && (
<AdvancedPanel />
)}
</div>
{agentQuery.isInitialLoading && <AgentPanelSkeleton />}
{!canEditAgent && !agentQuery.isInitialLoading && (
<div className="flex h-[30vh] w-full items-center justify-center">
<div className="text-center">
<h2 className="text-token-text-primary m-2 text-xl font-semibold">
{localize('com_agents_not_available')}
</h2>
<p className="text-token-text-secondary">{localize('com_agents_no_access')}</p>
</div>
</div>
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.model && (
<ModelPanel models={models} providers={providers} setActivePanel={setActivePanel} />
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.builder && (
<AgentConfig />
)}
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.advanced && (
<AdvancedPanel />
)}
{canEditAgent && !agentQuery.isInitialLoading && (
<AgentFooter
createMutation={create}

View file

@ -102,7 +102,7 @@ export default function MCPTool({ serverInfo }: { serverInfo?: MCPServerInfo })
<Accordion type="single" value={accordionValue} onValueChange={setAccordionValue} collapsible>
<AccordionItem value={currentServerName} className="group relative w-full border-none">
<div
className="relative flex w-full items-center gap-1 rounded-lg p-1 hover:bg-surface-primary-alt"
className="relative flex w-full items-center gap-1 rounded-lg p-1 text-sm hover:bg-surface-primary-alt"
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
onFocus={() => setIsFocused(true)}

View file

@ -97,8 +97,8 @@ export default function ModelPanel({
};
return (
<div className="mb-1 flex h-full min-h-[50vh] w-full flex-col gap-2 text-sm">
<div className="model-panel relative flex flex-col items-center px-16 py-4 text-center">
<div className="mb-1 flex w-full flex-col gap-2 text-sm">
<div className="model-panel relative flex flex-col items-center px-16 pt-2 text-center">
<div className="absolute left-0 top-4">
<button
type="button"

View file

@ -102,7 +102,7 @@ const Dropdown: React.FC<DropdownProps> = ({
portal={portal}
store={selectProps}
className={cn(
'popover-ui z-40',
'popover-ui z-40 text-sm',
sizeClasses,
className,
'max-h-[80vh] overflow-y-auto',