Skip to content

Added ability to customize logo and title #9052

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/20250205_141639_klakhov_setup_logo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Added

- Ability to customize `api/sever/about` endpoint via settings including logo and sign-in page subtitle
(<https://github.com/cvat-ai/cvat/pull/9052>)
41 changes: 41 additions & 0 deletions cvat-core/src/about.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import { SerializedAbout } from './server-response-types';

export default class AboutData {
#description: string;
#name: string;
#version: string;
#logoURL: string;
#subtitle: string;

constructor(initialData: SerializedAbout) {
this.#description = initialData.description;
this.#name = initialData.name;
this.#version = initialData.version;
this.#logoURL = initialData.logo_url;
this.#subtitle = initialData.subtitle;
}

get description(): string {
return this.#description;
}

get name(): string {
return this.#name;
}

get version(): string {
return this.#version;
}

get logoURL(): string {
return this.#logoURL;
}

get subtitle(): string {
return this.#subtitle;
}
}
3 changes: 2 additions & 1 deletion cvat-core/src/api-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
QualitySettingsFilter, SerializedAsset,
} from './server-response-types';
import QualityReport from './quality-report';
import AboutData from './about';
import QualityConflict, { ConflictSeverity } from './quality-conflict';
import QualitySettings from './quality-settings';
import { getFramesMeta } from './frames';
Expand Down Expand Up @@ -72,7 +73,7 @@ export default function implementAPI(cvat: CVATCore): CVATCore {

implementationMixin(cvat.server.about, async () => {
const result = await serverProxy.server.about();
return result;
return new AboutData(result);
});
implementationMixin(cvat.server.share, async (directory: string, searchPrefix?: string) => {
const result = await serverProxy.server.share(directory, searchPrefix);
Expand Down
3 changes: 2 additions & 1 deletion cvat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import AnalyticsReport from './analytics-report';
import AnnotationGuide from './guide';
import { JobValidationLayout, TaskValidationLayout } from './validation-layout';
import { Request } from './request';
import AboutData from './about';
import {
runAction,
callAction,
Expand Down Expand Up @@ -61,7 +62,7 @@ export default interface CVATCore {
requests: typeof lambdaManager.requests;
};
server: {
about: typeof serverProxy.server.about;
about: () => Promise<AboutData>;
share: (dir: string) => Promise<{
mimeType: string;
name: string;
Expand Down
2 changes: 2 additions & 0 deletions cvat-core/src/server-response-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export interface SerializedAbout {
description: string;
name: string;
version: string;
logo_url: string;
subtitle: string;
}

export interface SerializedRemoteFile {
Expand Down
4 changes: 2 additions & 2 deletions cvat-ui/src/actions/about-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT

import { ActionUnion, createAction, ThunkAction } from 'utils/redux';
import { getCore } from 'cvat-core-wrapper';
import { AboutData, getCore } from 'cvat-core-wrapper';

const core = getCore();

Expand All @@ -15,7 +15,7 @@ export enum AboutActionTypes {

const aboutActions = {
getAbout: () => createAction(AboutActionTypes.GET_ABOUT),
getAboutSuccess: (server: any) => createAction(AboutActionTypes.GET_ABOUT_SUCCESS, { server }),
getAboutSuccess: (server: AboutData) => createAction(AboutActionTypes.GET_ABOUT_SUCCESS, { server }),
getAboutFailed: (error: any) => createAction(AboutActionTypes.GET_ABOUT_FAILED, { error }),
};

Expand Down
19 changes: 19 additions & 0 deletions cvat-ui/src/components/common/cvat-logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React from 'react';
import { useSelector } from 'react-redux';
import { CombinedState } from 'reducers';

function CVATLogo(): JSX.Element {
const logo = useSelector((state: CombinedState) => state.about.server.logoURL);

return (
<div className='cvat-logo-icon'>
<img src={logo} alt='CVAT Logo' />
</div>
);
}

export default React.memo(CVATLogo);
9 changes: 5 additions & 4 deletions cvat-ui/src/components/cvat-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
loadServerAPISchema();
}

if (!aboutInitialized && !aboutFetching) {
loadAbout();
return;
}

if (user == null || !user.isVerified || !user.id) {
return;
}
Expand All @@ -337,10 +342,6 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
loadFormats();
}

if (!aboutInitialized && !aboutFetching) {
loadAbout();
}

if (organizationInitialized && !requestsInitialized && !requestsFetching) {
initRequests();
}
Expand Down
6 changes: 3 additions & 3 deletions cvat-ui/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { useHistory, useLocation } from 'react-router';
import { Row, Col } from 'antd/lib/grid';
import { MenuProps } from 'antd/lib/menu';
import Icon, {
import {
SettingOutlined,
InfoCircleOutlined,
EditOutlined,
Expand All @@ -34,9 +34,9 @@ import notification from 'antd/lib/notification';
import config from 'config';

import { Organization, getCore } from 'cvat-core-wrapper';
import { CVATLogo } from 'icons';
import ChangePasswordDialog from 'components/change-password-modal/change-password-modal';
import CVATTooltip from 'components/common/cvat-tooltip';
import CVATLogo from 'components/common/cvat-logo';
import { switchSettingsModalVisible as switchSettingsModalVisibleAction } from 'actions/settings-actions';
import { logoutAsync, authActions } from 'actions/auth-actions';
import { shortcutsActions, registerComponentShortcuts } from 'actions/shortcuts-actions';
Expand Down Expand Up @@ -430,7 +430,7 @@ function HeaderComponent(props: Props): JSX.Element {
<Layout.Header className='cvat-header'>
<GlobalHotKeys keyMap={subKeyMap(componentShortcuts, keyMap)} handlers={handlers} />
<div className='cvat-left-header'>
<Icon className='cvat-logo-icon' component={CVATLogo} />
<CVATLogo />
<Button
className={getButtonClassName('projects')}
type='link'
Expand Down
19 changes: 17 additions & 2 deletions cvat-ui/src/components/header/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
line-height: 0;
height: $header-height;
background: $header-color;
justify-content: space-between;
}

.ant-btn.ant-btn-link.cvat-header-button {
Expand All @@ -23,6 +24,21 @@
}
}

.cvat-logo-icon {
width: $grid-unit-size * 8;
height: $grid-unit-size * 4;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;

img {
object-fit: contain;
width: 100%;
height: 100%;
}
}

.cvat-left-header {
.ant-btn.cvat-header-button {
opacity: 0.7;
Expand All @@ -33,11 +49,10 @@
}
}

.anticon.cvat-logo-icon {
.cvat-logo-icon {
margin: 0 $grid-unit-size * 2;
}

width: 50%;
display: flex;
justify-content: flex-start;
align-items: center;
Expand Down
14 changes: 8 additions & 6 deletions cvat-ui/src/components/signing-common/signing-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

import './styles.scss';
import React from 'react';
import { useSelector } from 'react-redux';
import { CombinedState } from 'reducers';
import Layout from 'antd/lib/layout';
import { Col, Row } from 'antd/lib/grid';
import { CVATLogo } from 'icons';
import Icon from '@ant-design/icons';
import Title from 'antd/lib/typography/Title';
import CVATLogo from 'components/common/cvat-logo';
import SVGSigningBackground from '../../assets/signing-background.svg';

interface SignInLayoutComponentProps {
Expand Down Expand Up @@ -51,6 +52,8 @@ export const formSizes: FormSizes = {
function SignInLayout(props: SignInLayoutComponentProps): JSX.Element {
const { children } = props;
const { Content, Header } = Layout;
const subtitle = useSelector((state: CombinedState) => state.about.server.subtitle);

const titleSizes = {
xs: { span: 0 },
sm: { span: 0 },
Expand All @@ -71,18 +74,17 @@ function SignInLayout(props: SignInLayoutComponentProps): JSX.Element {
<Layout>
<SVGSigningBackground className='cvat-signing-background' />
<Header className='cvat-signing-header'>
<Row justify='center' align='middle'>
<Row className='cvat-signing-header-logo-wrapper' justify='center' align='middle'>
<Col {...logoSizes}>
<Icon className='cvat-logo-icon' component={CVATLogo} />
<CVATLogo />
</Col>
</Row>
</Header>
<Layout className='cvat-signing-layout'>
<Content>
<Row justify='center' align='middle' style={{ height: '100%' }}>
<Col {...titleSizes} className='cvat-signing-title'>
<Title>Open Data</Title>
<Title>Annotation Platform</Title>
<Title>{subtitle}</Title>
</Col>
{children}
</Row>
Expand Down
6 changes: 5 additions & 1 deletion cvat-ui/src/components/signing-common/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,18 @@ $social-google-background: #4286f5;
}
}

.cvat-signing-header-logo-wrapper {
height: 100%;
}

.cvat-signing-header {
background: transparent;
position: fixed;
padding: $grid-unit-size * 2 0 0 0;
width: 100%;

.cvat-logo-icon {
fill: white;
filter: brightness(0) invert(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it makes any logo on sign-in page white, however what if a color logo provided?
This approach will break such logo.

}
}

Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/cvat-core-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { BaseShapesAction } from 'cvat-core/src/annotations-actions/base-shapes-
import { BaseCollectionAction } from 'cvat-core/src/annotations-actions/base-collection-action';
import { ActionParameterType, BaseAction } from 'cvat-core/src/annotations-actions/base-action';
import { Request, RequestOperation } from 'cvat-core/src/request';
import AboutData from 'cvat-core/src/about';

const cvat: CVATCore = _cvat;

Expand Down Expand Up @@ -114,6 +115,7 @@ export {
JobValidationLayout,
TaskValidationLayout,
StorageLocation,
AboutData,
};

export type {
Expand Down
2 changes: 0 additions & 2 deletions cvat-ui/src/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import React from 'react';

import SVGCVATLogo from './assets/cvat-logo.svg';
import SVGCVATIcon from './assets/cvat-icon.svg';
import SVGCursorIcon from './assets/cursor-icon.svg';
import SVGMoveIcon from './assets/move-icon.svg';
Expand Down Expand Up @@ -72,7 +71,6 @@ import SVGShowGroundTruthIcon from './assets/show-gt-icon.svg';
import SVGJoinIcon from './assets/join-icon.svg';
import SVGSliceIcon from './assets/slice-icon.svg';

export const CVATLogo = React.memo((): JSX.Element => <SVGCVATLogo />);
export const CVATIcon = React.memo((): JSX.Element => <SVGCVATIcon />);
export const CursorIcon = React.memo((): JSX.Element => <SVGCursorIcon />);
export const MoveIcon = React.memo((): JSX.Element => <SVGMoveIcon />);
Expand Down
3 changes: 2 additions & 1 deletion cvat-ui/src/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Webhook, MLModel, Organization, Job, Task, Project, Label, User,
QualityConflict, FramesMetaData, RQStatus, Event, Invitation, SerializedAPISchema,
Request, JobValidationLayout, QualitySettings, TaskValidationLayout, ObjectState,
AboutData,
} from 'cvat-core-wrapper';
import { IntelligentScissors } from 'utils/opencv-wrapper/intelligent-scissors';
import { KeyMap, KeyMapItem } from 'utils/mousetrap-react';
Expand Down Expand Up @@ -336,7 +337,7 @@ export interface PluginsState {
}

export interface AboutState {
server: any;
server: AboutData;
packageVersion: {
ui: string;
};
Expand Down
2 changes: 2 additions & 0 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,8 @@ class AboutSerializer(serializers.Serializer):
name = serializers.CharField(max_length=128)
description = serializers.CharField(max_length=2048)
version = serializers.CharField(max_length=64)
logo_url = serializers.CharField()
subtitle = serializers.CharField(max_length=1024)

class FrameMetaSerializer(serializers.Serializer):
width = serializers.IntegerField()
Expand Down
File renamed without changes
7 changes: 5 additions & 2 deletions cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from attr.converters import to_bool
from django.conf import settings
from django.contrib.auth.models import User
from django.core.files.storage import storages
from django.db import IntegrityError
from django.db import models as django_models
from django.db import transaction
Expand Down Expand Up @@ -211,14 +212,16 @@ def about(request):
from cvat import __version__ as cvat_version
about = {
"name": "Computer Vision Annotation Tool",
"version": cvat_version,
"subtitle": settings.ABOUT_INFO["subtitle"],
"description": "CVAT is completely re-designed and re-implemented " +
"version of Video Annotation Tool from Irvine, California " +
"tool. It is free, online, interactive video and image annotation " +
"tool for computer vision. It is being used by our team to " +
"annotate million of objects with different properties. Many UI " +
"and UX decisions are based on feedbacks from professional data " +
"annotation team."
"annotation team.",
"version": cvat_version,
"logo_url": request.build_absolute_uri(storages["staticfiles"].url(settings.LOGO_FILENAME)),
}
serializer = AboutSerializer(data=about)
if serializer.is_valid(raise_exception=True):
Expand Down
7 changes: 7 additions & 0 deletions cvat/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6905,9 +6905,16 @@ components:
version:
type: string
maxLength: 64
logo_url:
type: string
subtitle:
type: string
maxLength: 1024
required:
- description
- logo_url
- name
- subtitle
- version
AcceptInvitationRead:
type: object
Expand Down
5 changes: 5 additions & 0 deletions cvat/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,3 +769,8 @@ class CVAT_QUEUES(Enum):

# Indicates the maximum number of days a file or directory is retained in the temporary directory
TMP_FILE_OR_DIR_RETENTION_DAYS = 3

LOGO_FILENAME = 'logo.svg'
ABOUT_INFO = {
"subtitle": "Open Data Annotation Platform",
}
Loading