mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-24 13:26:08 +00:00
fix(web): show forced sandbox scope + make disabled tooltip tap-friendly
When a SaaS deployment pins every pipeline to a fixed sandbox scope via system.limitation.force_box_session_id_template, the Sandbox Scope selector was correctly locked but still displayed the pipeline's stored value (e.g. the per-chat default), misrepresenting the scope that the runtime actually enforces on every exec. Coerce the displayed/saved value to the forced template so the locked selector truthfully shows the active scope (e.g. Global). Also fix the disabled_tooltip being invisible on touch devices: hover-only Radix tooltips never open without a pointer, so the explanation of why the field is locked could not be read on mobile. Wrap the info icon so a tap toggles the tooltip while desktop hover still works.
This commit is contained in:
@@ -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 (
|
||||||
|
<TooltipProvider delayDuration={100}>
|
||||||
|
<Tooltip open={open} onOpenChange={setOpen}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={text}
|
||||||
|
className="inline-flex shrink-0"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setOpen((v) => !v);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Info className="h-3.5 w-3.5 text-muted-foreground cursor-help shrink-0" />
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">{text}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function DynamicFormComponent({
|
export default function DynamicFormComponent({
|
||||||
itemConfigList,
|
itemConfigList,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@@ -550,18 +579,7 @@ export default function DynamicFormComponent({
|
|||||||
? extractI18nObject(config.disabled_tooltip)
|
? extractI18nObject(config.disabled_tooltip)
|
||||||
: '';
|
: '';
|
||||||
const renderDisabledTooltipIcon = () =>
|
const renderDisabledTooltipIcon = () =>
|
||||||
disabledTooltip ? (
|
disabledTooltip ? <DisabledTooltipIcon text={disabledTooltip} /> : null;
|
||||||
<TooltipProvider delayDuration={100}>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Info className="h-3.5 w-3.5 text-muted-foreground cursor-help shrink-0" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent className="max-w-xs">
|
|
||||||
{disabledTooltip}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
// Webhook URL fields are display-only; render outside of form binding
|
// Webhook URL fields are display-only; render outside of form binding
|
||||||
if (config.type === 'webhook-url') {
|
if (config.type === 'webhook-url') {
|
||||||
|
|||||||
@@ -427,8 +427,9 @@ export default function PipelineFormComponent({
|
|||||||
// 1. Box sandbox is unavailable, or
|
// 1. Box sandbox is unavailable, or
|
||||||
// 2. the deployment pins all pipelines to a fixed scope via
|
// 2. the deployment pins all pipelines to a fixed scope via
|
||||||
// ``system.limitation.force_box_session_id_template`` (SaaS).
|
// ``system.limitation.force_box_session_id_template`` (SaaS).
|
||||||
const boxScopeForced =
|
const forcedBoxTemplate =
|
||||||
!!systemInfo.limitation?.force_box_session_id_template;
|
systemInfo.limitation?.force_box_session_id_template || '';
|
||||||
|
const boxScopeForced = !!forcedBoxTemplate;
|
||||||
const stageSystemContext =
|
const stageSystemContext =
|
||||||
stage.name === 'local-agent'
|
stage.name === 'local-agent'
|
||||||
? {
|
? {
|
||||||
@@ -437,6 +438,24 @@ export default function PipelineFormComponent({
|
|||||||
}
|
}
|
||||||
: undefined;
|
: 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<string, any> =
|
||||||
|
(form.watch(formName) as Record<string, any>)?.[stage.name] || {};
|
||||||
|
const effectiveInitialValues =
|
||||||
|
stage.name === 'local-agent' && boxScopeForced
|
||||||
|
? {
|
||||||
|
...stageInitialValues,
|
||||||
|
'box-session-id-template': forcedBoxTemplate,
|
||||||
|
}
|
||||||
|
: stageInitialValues;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={stage.name}>
|
<Card key={stage.name}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -450,10 +469,7 @@ export default function PipelineFormComponent({
|
|||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
<DynamicFormComponent
|
<DynamicFormComponent
|
||||||
itemConfigList={stage.config}
|
itemConfigList={stage.config}
|
||||||
initialValues={
|
initialValues={effectiveInitialValues}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(form.watch(formName) as Record<string, any>)?.[stage.name] || {}
|
|
||||||
}
|
|
||||||
onSubmit={(values) => {
|
onSubmit={(values) => {
|
||||||
handleDynamicFormEmit(formName, stage.name, values);
|
handleDynamicFormEmit(formName, stage.name, values);
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user