chainlit icon indicating copy to clipboard operation
chainlit copied to clipboard

Looking for ideas on how to do UI customizations

Open foragerr opened this issue 1 year ago • 3 comments
trafficstars

The 50% radius on this line is a bit of an annoyance for me, It clips starter icons that don't fit neatly in the middle

https://github.com/Chainlit/chainlit/blob/1c5098c0cb25c40a0c18721bb1389a0d0319032e/frontend/src/components/organisms/chat/welcomeScreen/starter.tsx#L57

Example: clipping

While there are workarounds like,

  • use smaller icons
  • use rounder icons

I would prefer to disable the rounded corners. This is a style choice and it doesn't make sense to make a change in the upstream project. What are my remaining choices?

  • Maintaining a fork for a small change like this feels sledge-hammer like.
  • Monkey patching this setting at build time is icky, and liable to break all the time.

Anyone have better ways to inject minor UI customizations into Chainlit UI?

foragerr avatar Sep 29 '24 17:09 foragerr

You can disable the 50% border-radius on starter icons in the starter.tsx file without forking the project or using monkey patching by utilizing Chainlit's custom CSS feature. Here's how you can do it:

  1. Create a Custom CSS File: Create a file named stylesheet.css in your public directory with the following content:

    img.starter-icon {
      border-radius: 0% !important;
    }
    
  2. Update the Configuration: Modify your .chainlit/config.toml file to include the path to your custom CSS file:

    [UI]
    custom_css = '/public/stylesheet.css'
    
  3. Ensure the Correct Class in starter.tsx: Make sure the img element in your starter.tsx file has the class starter-icon:

    return (
        <Button
          id={`starter-${starter.label.trim().toLowerCase().replaceAll(' ', '-')}`}
          fullWidth
          disabled={loading}
          color="inherit"
          sx={{
            border: (theme) => `1px solid ${theme.palette.divider}`,
            borderRadius: '1rem',
            height: 100,
            p: 2,
            textTransform: 'none',
            justifyContent: 'flex-start'
          }}
          onClick={onSubmit}
        >
          <Stack gap={0.5} flexGrow={1} height="100%">
            {starter.icon ? (
              <img
                className="starter-icon"
                src={
                  starter.icon?.startsWith('/public')
                    ? apiClient.buildEndpoint(starter.icon)
                    : starter.icon
                }
                alt={starter.label}
                height={20}
                width={20}
              />
            ) : (
              <Box sx={{ height: 20, width: 20 }} />
            )}
            <Typography
              sx={{
                overflow: 'hidden',
                textOverflow: 'ellipsis',
                display: '-webkit-box',
                WebkitLineClamp: '2',
                WebkitBoxOrient: 'vertical',
                fontSize: '14px'
              }}
              color="text.secondary"
              align="left"
            >
              {starter.label}
            </Typography>
          </Stack>
        </Button>
      );
    }
    
  4. Restart Your Application: After making these changes, restart your Chainlit application. The custom CSS will be applied, and the starter icons will no longer have rounded corners.

This approach allows you to make minor UI customizations without altering the upstream codebase or using cumbersome methods like forking or monkey patching [1][2].

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Sep 29 '24 17:09 dosubot[bot]

@dosu custom CSS is a good thought. But how do I add a class to that starte-icon element without forking or monkey patching?

foragerr avatar Sep 29 '24 17:09 foragerr

To add a class to the starter-icon element without forking or monkey patching, you can modify the img element directly within the Starter component and use a custom CSS stylesheet. Here is the updated code:

import { useCallback, useContext } from 'react';
import { v4 as uuidv4 } from 'uuid';

import { Box, Button, Stack, Typography } from '@mui/material';

import {
  ChainlitContext,
  IStarter,
  IStep,
  useAuth,
  useChatData
} from '@chainlit/react-client';
import { useChatInteract } from '@chainlit/react-client';

interface Props {
  starter: IStarter;
}

export default function Starter({ starter }: Props) {
  const apiClient = useContext(ChainlitContext);
  const { sendMessage } = useChatInteract();
  const { loading } = useChatData();
  const { user } = useAuth();

  const onSubmit = useCallback(async () => {
    const message: IStep = {
      threadId: '',
      id: uuidv4(),
      name: user?.identifier || 'User',
      type: 'user_message',
      output: starter.message,
      createdAt: new Date().toISOString()
    };

    sendMessage(message, []);
  }, [user, sendMessage, starter]);

  return (
    <Button
      id={`starter-${starter.label.trim().toLowerCase().replaceAll(' ', '-')}`}
      fullWidth
      disabled={loading}
      color="inherit"
      sx={{
        border: (theme) => `1px solid ${theme.palette.divider}`,
        borderRadius: '1rem',
        height: 100,
        p: 2,
        textTransform: 'none',
        justifyContent: 'flex-start'
      }}
      onClick={onSubmit}
    >
      <Stack gap={0.5} flexGrow={1} height="100%">
        {starter.icon ? (
          <img
            className="starter-icon"
            style={{ borderRadius: '50%' }}
            src={
              starter.icon?.startsWith('/public')
                ? apiClient.buildEndpoint(starter.icon)
                : starter.icon
            }
            alt={starter.label}
            height={20}
            width={20}
          />
        ) : (
          <Box sx={{ height: 20, width: 20 }} />
        )}
        <Typography
          sx={{
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            display: '-webkit-box',
            WebkitLineClamp: '2',
            WebkitBoxOrient: 'vertical',
            fontSize: '14px'
          }}
          color="text.secondary"
          align="left"
        >
          {starter.label}
        </Typography>
      </Stack>
    </Button>
  );
}

In this code, the className="starter-icon" attribute is added to the img element. To apply custom styling, you can create a custom CSS stylesheet and configure it in your .chainlit/config.toml file as follows:

[UI]
# This can either be a css file in your `public` dir or a URL
custom_css = '/public/stylesheet.css'

After updating the configuration, restart the application to apply your custom styling. This approach allows you to style the starter-icon class in your CSS without needing to fork or monkey patch the component [1][2].

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Sep 29 '24 17:09 dosubot[bot]

In the latest release, the UI is rewritten with tailwind which should help for customisation. Also the theme is should be more configurable

willydouhard avatar Jan 06 '25 11:01 willydouhard