Skip to content

Commit ab50405

Browse files
authored
Merge pull request #155 from docker/trungutt/rework-tile-top-bottom
Rework tile top and bottom
2 parents 80c2d99 + 4d0ad83 commit ab50405

File tree

6 files changed

+205
-77
lines changed

6 files changed

+205
-77
lines changed

src/extension/ui/package-lock.json

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/extension/ui/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"@tanstack/react-query": "^5.69.0",
1414
"ansi-to-html": "^0.7.2",
1515
"json-schema-library": "^10.0.0-rc7",
16+
"lodash-es": "^4.17.21",
1617
"react": "^18.2.0",
1718
"react-dom": "^18.2.0",
19+
"react-inlinesvg": "^4.2.0",
1820
"yaml": "^2.3.1"
1921
},
2022
"scripts": {
@@ -26,6 +28,7 @@
2628
"@docker/extension-api-client-types": "0.3.4",
2729
"@tanstack/react-query-devtools": "^5.69.0",
2830
"@types/jest": "^29.1.2",
31+
"@types/lodash-es": "^4.17.12",
2932
"@types/node": "^18.7.18",
3033
"@types/react": "^18.0.17",
3134
"@types/react-dom": "^18.0.6",
Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,78 @@
1-
import { Chip, Stack } from "@mui/material";
2-
import { CatalogItem } from "../../types/catalog";
3-
import { Hardware } from "@mui/icons-material";
1+
import { Chip, Stack, useTheme } from '@mui/material';
2+
import { styled } from '@mui/material/styles';
3+
import SVG from 'react-inlinesvg';
4+
5+
import { CatalogItem } from '../../types/catalog';
6+
import hammerIcon from './hammer.svg';
47

58
type BottomProps = {
6-
item: CatalogItem,
7-
needsConfiguration: boolean
8-
}
9+
item: CatalogItem;
10+
needsConfiguration: boolean;
11+
};
912

1013
const Bottom = ({ item }: BottomProps) => {
11-
return (
12-
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
13-
{<Chip sx={{ fontSize: '1.2em', p: '4px 8px' }} label={
14-
<Stack direction="row" alignItems="center" sx={{ fontSize: '1.2em' }}>
15-
<Hardware sx={{ fontSize: '1.2em', mr: '2px' }} />
16-
{item.tools?.length || 1}
17-
</Stack>
18-
} color="primary" />}
19-
{!item.tools?.length && !!item.prompts && <Chip label={`${item.prompts} prompt` + (item.prompts !== 1 ? 's' : '')} color="secondary" />}
20-
{!item.tools?.length && !item.prompts && item.resources?.length && <Chip label={`${item.resources.length} resource(s)`} color="success" />}
21-
</Stack>
22-
23-
)
14+
const theme = useTheme();
15+
16+
return (
17+
<Stack
18+
direction="row"
19+
spacing={2}
20+
sx={{
21+
alignItems: 'center',
22+
width: '100%',
23+
}}
24+
>
25+
<Chip
26+
icon={
27+
<HammerIcon
28+
color={theme.palette.primary.main}
29+
height={12}
30+
width={12}
31+
/>
32+
}
33+
label={`${item.tools.length} ${pluralize('tool', item.tools.length)}`}
34+
color="primary"
35+
/>
36+
{!item.tools?.length && !!item.prompts && (
37+
<Chip
38+
label={`${item.prompts} ${pluralize('prompt', item.prompts)}`}
39+
color="secondary"
40+
/>
41+
)}
42+
{!item.tools?.length && !item.prompts && item.resources?.length && (
43+
<Chip
44+
label={`${item.resources.length} ${pluralize(
45+
'resource',
46+
item.resources.length
47+
)}`}
48+
/>
49+
)}
50+
</Stack>
51+
);
52+
};
53+
54+
export default Bottom;
55+
56+
function pluralize(noun: string, count: number): string {
57+
return Math.abs(count) === 1 ? noun : `${noun}s`;
2458
}
2559

26-
export default Bottom;
60+
const StyledSVG = styled(SVG)((props) => {
61+
return {
62+
'& path': {
63+
fill: props.color,
64+
},
65+
};
66+
});
67+
68+
interface HammerIconProps {
69+
color: string;
70+
width: number;
71+
height: number;
72+
}
73+
74+
function HammerIcon({ color, width, height }: HammerIconProps) {
75+
return (
76+
<StyledSVG color={color} src={hammerIcon} width={width} height={height} />
77+
);
78+
}

src/extension/ui/src/components/tile/Index.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,28 @@ const Tile = ({ item, client }: TileProps) => {
145145
catalogItem={item}
146146
client={client}
147147
/>
148-
<Card sx={{ height: 150 }}>
148+
<Card>
149149
<CardActionArea
150+
sx={{ padding: 1.5 }}
150151
onClick={(e) => {
151152
if ((e.target as HTMLElement).tagName !== 'INPUT') {
152153
setShowConfigModal(true);
153154
}
154155
}}
155156
>
156-
<CardContent sx={{ paddingBottom: 0, paddingTop: 2 }}>
157-
<Stack direction="column" spacing={0}>
158-
<Top
159-
onToggleRegister={(checked) => {
160-
if (checked) {
161-
registerCatalogItem(item);
162-
} else {
163-
unregisterCatalogItem(item);
164-
}
165-
}}
166-
item={item}
167-
/>
157+
<Top
158+
onToggleRegister={(checked) => {
159+
if (checked) {
160+
registerCatalogItem(item);
161+
} else {
162+
unregisterCatalogItem(item);
163+
}
164+
}}
165+
item={item}
166+
/>
167+
<CardContent sx={(th) => ({ padding: th.spacing(2, 0, 0) })}>
168+
<Stack spacing={2} sx={{ alignItems: 'flex-start' }}>
168169
<Center item={item} />
169-
<Divider sx={{ marginBottom: 1 }} />
170170
<Bottom
171171
item={item}
172172
needsConfiguration={Boolean(unAssignedSecrets.length)}
Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,68 @@
1-
import { CardMedia, Stack, Switch, Tooltip, Typography } from "@mui/material";
2-
import { CatalogItemRichened } from "../../types/catalog";
1+
import { Avatar, CardHeader, Switch, Tooltip, Typography } from '@mui/material';
2+
import { capitalize } from 'lodash-es';
3+
4+
import { CatalogItemRichened } from '../../types/catalog';
35

46
type TopProps = {
5-
onToggleRegister: (checked: boolean) => void,
6-
item: CatalogItemRichened
7-
}
7+
onToggleRegister: (checked: boolean) => void;
8+
item: CatalogItemRichened;
9+
};
810

911
export default function Top({ item, onToggleRegister }: TopProps) {
10-
11-
const getActionButton = () => {
12-
if (!item.canRegister) {
13-
return <Stack direction="row" spacing={0} alignItems="center">
14-
<Tooltip title="This tile needs configuration before it can be used.">
15-
<span>
16-
<Switch checked={false} disabled />
17-
</span>
18-
</Tooltip>
19-
</Stack>
20-
}
21-
return <Stack direction="row" spacing={0} alignItems="center">
22-
<Tooltip title={item.registered ? "Unregistering this tile will hide it from MCP clients." : "Registering this tile will expose it to MCP clients."}>
23-
<Switch
24-
checked={item.registered}
25-
onChange={(event, checked) => {
26-
event.stopPropagation()
27-
event.preventDefault()
28-
onToggleRegister(checked)
29-
}}
30-
/>
31-
</Tooltip>
32-
</Stack>
33-
}
34-
return (
35-
<Stack direction="row" spacing={0} sx={{ alignItems: 'center', justifyContent: 'space-between', width: '100%', mb: 1.5 }}>
36-
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', justifyContent: 'space-between' }}>
37-
<CardMedia
38-
component="img"
39-
image={item.icon}
40-
alt={item.name}
41-
sx={{ width: 25, height: 25, borderRadius: 1, background: 'white', padding: '2px', justifySelf: 'flex-start' }}
42-
/>
43-
<Typography sx={{ justifySelf: 'flex-start', fontWeight: 'bold' }}>{item.name}</Typography>
44-
</Stack>
45-
{getActionButton()}
46-
</Stack>
47-
)
12+
return (
13+
<CardHeader
14+
sx={{ padding: 0 }}
15+
avatar={
16+
<Avatar
17+
variant="square"
18+
src={item.icon}
19+
alt={item.name}
20+
sx={{
21+
width: 24,
22+
height: 24,
23+
borderRadius: 1,
24+
}}
25+
/>
26+
}
27+
title={
28+
<Typography sx={{ justifySelf: 'flex-start', fontWeight: 'bold' }}>
29+
{
30+
// Lodash doesn't have a capitalize function that works with strings
31+
item.name
32+
.replace(/-/g, ' ')
33+
.replace(/_/g, ' ')
34+
.split(' ')
35+
.map(capitalize)
36+
.join(' ')
37+
}
38+
</Typography>
39+
}
40+
action={
41+
item.canRegister ? (
42+
<Tooltip
43+
title={
44+
item.registered
45+
? 'Unregistering this server will hide it from MCP clients.'
46+
: 'Registering this server will expose it to MCP clients.'
47+
}
48+
>
49+
<Switch
50+
checked={item.registered}
51+
onChange={(event, checked) => {
52+
event.stopPropagation();
53+
event.preventDefault();
54+
onToggleRegister(checked);
55+
}}
56+
/>
57+
</Tooltip>
58+
) : (
59+
<Tooltip title="This server needs configuration before it can be used.">
60+
<span>
61+
<Switch checked={false} disabled />
62+
</span>
63+
</Tooltip>
64+
)
65+
}
66+
/>
67+
);
4868
}
Lines changed: 6 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)