export default function ModelOverlay()

in codex-cli/src/components/model-overlay.tsx [27:165]


export default function ModelOverlay({
  currentModel,
  providers = {},
  currentProvider = "openai",
  hasLastResponse,
  onSelect,
  onSelectProvider,
  onExit,
}: Props): JSX.Element {
  const [items, setItems] = useState<Array<{ label: string; value: string }>>(
    [],
  );
  const [providerItems, _setProviderItems] = useState<
    Array<{ label: string; value: string }>
  >(Object.values(providers).map((p) => ({ label: p.name, value: p.name })));
  const [mode, setMode] = useState<"model" | "provider">("model");
  const [isLoading, setIsLoading] = useState<boolean>(true);

  // This effect will run when the provider changes to update the model list
  useEffect(() => {
    setIsLoading(true);
    (async () => {
      try {
        const models = await getAvailableModels(currentProvider);
        // Convert the models to the format needed by TypeaheadOverlay
        setItems(
          models.map((m) => ({
            label: m,
            value: m,
          })),
        );
      } catch (error) {
        // Silently handle errors - remove console.error
        // console.error("Error loading models:", error);
      } finally {
        setIsLoading(false);
      }
    })();
  }, [currentProvider]);

  // ---------------------------------------------------------------------------
  // If the conversation already contains a response we cannot change the model
  // anymore because the backend requires a consistent model across the entire
  // run.  In that scenario we replace the regular typeahead picker with a
  // simple message instructing the user to start a new chat.  The only
  // available action is to dismiss the overlay (Esc or Enter).
  // ---------------------------------------------------------------------------

  // Register input handling for switching between model and provider selection
  useInput((_input, key) => {
    if (hasLastResponse && (key.escape || key.return)) {
      onExit();
    } else if (!hasLastResponse) {
      if (key.tab) {
        setMode(mode === "model" ? "provider" : "model");
      }
    }
  });

  if (hasLastResponse) {
    return (
      <Box
        flexDirection="column"
        borderStyle="round"
        borderColor="gray"
        width={80}
      >
        <Box paddingX={1}>
          <Text bold color="red">
            Unable to switch model
          </Text>
        </Box>
        <Box paddingX={1}>
          <Text>
            You can only pick a model before the assistant sends its first
            response. To use a different model please start a new chat.
          </Text>
        </Box>
        <Box paddingX={1}>
          <Text dimColor>press esc or enter to close</Text>
        </Box>
      </Box>
    );
  }

  if (mode === "provider") {
    return (
      <TypeaheadOverlay
        title="Select provider"
        description={
          <Box flexDirection="column">
            <Text>
              Current provider:{" "}
              <Text color="greenBright">{currentProvider}</Text>
            </Text>
            <Text dimColor>press tab to switch to model selection</Text>
          </Box>
        }
        initialItems={providerItems}
        currentValue={currentProvider}
        onSelect={(provider) => {
          if (onSelectProvider) {
            onSelectProvider(provider);
            // Immediately switch to model selection so user can pick a model for the new provider
            setMode("model");
          }
        }}
        onExit={onExit}
      />
    );
  }

  return (
    <TypeaheadOverlay
      title="Select model"
      description={
        <Box flexDirection="column">
          <Text>
            Current model: <Text color="greenBright">{currentModel}</Text>
          </Text>
          <Text>
            Current provider: <Text color="greenBright">{currentProvider}</Text>
          </Text>
          {isLoading && <Text color="yellow">Loading models...</Text>}
          <Text dimColor>press tab to switch to provider selection</Text>
        </Box>
      }
      initialItems={items}
      currentValue={currentModel}
      onSelect={(selectedModel) =>
        onSelect(
          items?.map((m) => m.value),
          selectedModel,
        )
      }
      onExit={onExit}
    />
  );
}