diff --git a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx index 078db6f7..057f4e52 100644 --- a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx +++ b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx @@ -198,6 +198,35 @@ function WebhookUrlField({ ); } +// Hover-only Radix tooltips never open on touch devices (no pointer hover), +// so the ``disabled_tooltip`` explaining why a field is locked was invisible on +// mobile. This wrapper makes the info icon also toggle the tooltip on tap while +// keeping hover behavior on desktop. +function DisabledTooltipIcon({ text }: { text: string }) { + const [open, setOpen] = useState(false); + return ( + + + + + + {text} + + + ); +} + export default function DynamicFormComponent({ itemConfigList, onSubmit, @@ -550,18 +579,7 @@ export default function DynamicFormComponent({ ? extractI18nObject(config.disabled_tooltip) : ''; const renderDisabledTooltipIcon = () => - disabledTooltip ? ( - - - - - - - {disabledTooltip} - - - - ) : null; + disabledTooltip ? : null; // Webhook URL fields are display-only; render outside of form binding if (config.type === 'webhook-url') { diff --git a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx index fc0a6812..863c2202 100644 --- a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx +++ b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx @@ -427,8 +427,9 @@ export default function PipelineFormComponent({ // 1. Box sandbox is unavailable, or // 2. the deployment pins all pipelines to a fixed scope via // ``system.limitation.force_box_session_id_template`` (SaaS). - const boxScopeForced = - !!systemInfo.limitation?.force_box_session_id_template; + const forcedBoxTemplate = + systemInfo.limitation?.force_box_session_id_template || ''; + const boxScopeForced = !!forcedBoxTemplate; const stageSystemContext = stage.name === 'local-agent' ? { @@ -437,6 +438,24 @@ export default function PipelineFormComponent({ } : undefined; + // When the deployment pins every pipeline to a fixed sandbox scope (SaaS + // ``force_box_session_id_template``), the Sandbox Scope selector is locked. + // The runtime already overrides the scope on every exec, but the stored + // pipeline value can be anything (e.g. the per-chat default), which would + // make the locked selector display a scope that is NOT the one actually in + // effect. Coerce the displayed/saved value to the forced template so the UI + // truthfully reflects runtime behavior. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const stageInitialValues: Record = + (form.watch(formName) as Record)?.[stage.name] || {}; + const effectiveInitialValues = + stage.name === 'local-agent' && boxScopeForced + ? { + ...stageInitialValues, + 'box-session-id-template': forcedBoxTemplate, + } + : stageInitialValues; + return ( @@ -450,10 +469,7 @@ export default function PipelineFormComponent({ )?.[stage.name] || {} - } + initialValues={effectiveInitialValues} onSubmit={(values) => { handleDynamicFormEmit(formName, stage.name, values); }}