/* Copyright 2026 Marimo. All rights reserved. */ import { useAtomValue } from "jotai"; import { get, has } from "lodash-es"; import { FolderCog2 } from "lucide-react"; import { useCallback } from "react"; import type { Control, ControllerRenderProps, FieldPath, FieldPathValue, } from "react-hook-form"; import { FormField } from "@/components/ui/form"; import { Tooltip } from "@/components/ui/tooltip"; import { configOverridesAtom, useUserConfig } from "@/core/config/config"; import type { UserConfig } from "@/core/config/config-schema"; import { Kbd } from "../ui/kbd"; export interface ConfigOverride { isOverridden: boolean; /** * The effective value: the project config value when overridden, otherwise * the user's own value. */ value: T; userValue: unknown; projectValue: unknown; } function resolveOverride({ userConfig, overrides, name, value, }: { userConfig: UserConfig; overrides: unknown; name: FieldPath; value: T; }): ConfigOverride { const userValue = get(userConfig, name); const projectValue = get(overrides as UserConfig, name); // A setting is overridden when the project config specifies the path at all // (so it stays locked even if the value equals the user's, or is `null`). const isOverridden = has(overrides, name); return { isOverridden, userValue, projectValue, value: isOverridden ? (projectValue as T) : value, }; } /** * Returns a function that resolves a form field against the project config * overrides (e.g. `pyproject.toml`). * * The returned function is a plain callback (not a hook), so it's safe to call * inside `FormField` render callbacks and loops. */ export function useConfigOverride(): ( name: FieldPath, value: T, ) => ConfigOverride { const [userConfig] = useUserConfig(); const overrides = useAtomValue(configOverridesAtom); return useCallback( (name: FieldPath, value: T): ConfigOverride => resolveOverride({ userConfig, overrides, name, value }), [userConfig, overrides], ); } /** * A `FormField` that resolves the field against the project config overrides. */ export function OverriddenFormField>({ control, name, disabled, render, }: { control: Control; name: TName; disabled?: boolean; render: (args: { field: ControllerRenderProps; override: ConfigOverride>; }) => React.ReactElement; }) { const getOverride = useConfigOverride(); return ( render({ field, // `field.value` is `FieldPathValue`, but the deep // conditional type doesn't reduce for the compiler, so we narrow it. override: getOverride(name, field.value) as ConfigOverride< FieldPathValue >, }) } /> ); } /** * Shared explanation shown in override tooltips, so the wording stays * consistent across the badge and the disabled-wrapper. */ const OverriddenExplanation = () => ( <>

This setting is overridden by the{" "} pyproject.toml config.

To change it, edit the project config{" "} pyproject.toml directly.

); /** * Wraps a component and shows a tooltip if the user config is overridden by the * project config. */ export const DisableIfOverridden = ({ name, children, }: { name: FieldPath; children: React.ReactNode; }) => { const { isOverridden } = useConfigOverride()(name, undefined); return isOverridden ? ( } >
{children}
) : ( children ); }; export const IsOverridden = ({ override, }: { override: ConfigOverride; }) => { if (!override.isOverridden) { return null; } return (

User value: {String(override.userValue)}
Project value: {String(override.projectValue)}

} > Overridden by pyproject.toml
); };