Compare commits

...

4 Commits

Author SHA1 Message Date
Charles Bochet 5e29c0ef1d Fix remaining styled(Component) usages missed in initial pass
- WorkflowVariablesDropdown: styled(StyledDropdownButtonContainer) → parent wrapper
- DragSelect.stories: styled(ScrollWrapper) → parent container with overflow
- RecordTableActionRow: styled(RecordTableDragAndDropPlaceholderCell) → parent container
- RecordTableEmptyStateDisplay: styled(AnimatedPlaceholderEmptyContainer) → standalone styled.div
- TimelineCard: styled(AnimatedPlaceholderEmptyContainer) → conditional wrapper
- StyledMenuItemBase: merge StyledHoverableMenuItemBase into a standalone styled.div
  with all base + hoverable styles inlined (no styled(Component) extension)

Made-with: Cursor
2026-03-05 09:23:46 +01:00
Charles Bochet 09254580f2 Fix scroll regression in ScrollWrapper parent containers
Add overflow: hidden to parent containers wrapping ScrollWrapper.
Without it, flex items expand to content height instead of
constraining to available space, preventing scroll activation.

Made-with: Cursor
2026-03-05 09:19:03 +01:00
Charles Bochet ca8fccecff Remove all styled(Component) patterns in favor of parent wrappers and props
Replace 354 styled(Component) usages with:
- Parent styled containers for margin/layout CSS
- Child CSS selectors (> a, > div, > input) for third-party components (Link, TextareaAutosize, Handle, IMaskInput, DataGrid, QRCode)
- Existing component props (gridTemplateColumns, color, gap, align, etc.) for TableRow/TableCell
- Parent wrappers with > * selectors for intrinsic overrides on twenty-ui components

Made-with: Cursor
2026-03-05 09:09:43 +01:00
Charles Bochet bf2c6affbb Deprecate runtime theme objects in favor of CSS variables
- Remove ICON_SIZES/ICON_STROKES constants — all icon sizes/strokes
  now resolved at runtime via resolveThemeVariableAsNumber(themeCssVariables.icon.size/stroke.X)
- Move ColorSchemeContext/ColorSchemeProvider from twenty-ui/theme to
  twenty-ui/theme-constants — consumers no longer import from twenty-ui/theme
- Re-export ThemeColor, MAIN_COLOR_NAMES, getNextThemeColor, AnimationDuration
  from theme-constants via proxy files so the barrel generator picks them up
- Replace all ThemeContext/useContext(ThemeContext) usages across ~300 files
  with themeCssVariables (CSS contexts) or resolveThemeVariable (JS runtime)
- Simplify provider chain to just ColorSchemeProvider + DOM class toggle effect
- Fix pre-existing test failures: useIcons.test.ts (ES module spy),
  turnRecordFilterGroupIntoGqlOperationFilter.test.ts (Omit<RecordFilter,'id'>)
- Delete ThemeType, ThemeProvider, ThemeContextProvider

Made-with: Cursor
2026-03-05 02:03:24 +01:00
611 changed files with 9288 additions and 7328 deletions
@@ -1,7 +1,6 @@
import { defineLogicFunction, RoutePayload } from "twenty-sdk";
import { MetadataApiClient } from 'twenty-sdk/generated';
export const OAUTH_TOKEN_PAIRS_PATH = '/oauth/token-pairs';
type ApolloTokenResponse = {
@@ -53,16 +52,8 @@ const handler = async (event: RoutePayload): Promise<any> => {
const apolloClientSecret = process.env.APOLLO_CLIENT_SECRET ?? '';
const applicationId = process.env.APPLICATION_ID ?? '';
const metadataClient = new MetadataApiClient({});
const tokenPairs = await getAuthenticationTokenPairs(
code,
apolloClientId,
@@ -5,8 +5,6 @@ import {
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/generated';
type CompanyRecord = {
id: string;
name?: string;
@@ -38,8 +36,6 @@ type ApolloEnrichResponse = {
organization?: ApolloOrganization;
};
const extractDomain = (
domainName?: CompanyRecord['domainName'],
): string | undefined => {
@@ -172,7 +168,6 @@ const updateCompanyInTwenty = async (
}
};
type CompanyUpdateEvent = DatabaseEventPayload<
ObjectRecordUpdateEvent<CompanyRecord>
>;
@@ -181,7 +176,6 @@ const handler = async (
event: CompanyUpdateEvent,
): Promise<object | undefined> => {
const { recordId, properties } = event;
const { after: companyAfter } = properties;
@@ -206,7 +200,6 @@ const handler = async (
return { skipped: true, reason: 'no enrichment data to apply' };
}
await updateCompanyInTwenty(recordId, updateData);
const result = {
@@ -219,4 +219,3 @@ main().catch((error) => {
process.exit(1);
});
@@ -81,4 +81,3 @@ describe('HistoricalImporter', () => {
});
});
@@ -158,4 +158,3 @@ export class HistoricalImporter {
}
}
@@ -1,3 +1,2 @@
export { Meeting } from './meeting';
@@ -330,7 +330,6 @@ export class WebhookHandler {
}
}
private async createFailedMeetingRecord(params: unknown, error: string): Promise<void> {
try {
const twentyApiKey = process.env.TWENTY_API_KEY || '';
@@ -43,7 +43,6 @@ export default defineContentScript({
},
});
ctx.addEventListener(window, 'wxt:locationchange', ({newUrl, }) => {
const injectedBtn = document.querySelector('[data-id="twenty-btn"]');
if(personPattern.includes(newUrl) && !injectedBtn) ui.mount();
@@ -8,7 +8,6 @@ const StyledButton = styled.button`
--text-color: #0a66c2;
--bg-color: #00000000;
font-size: ${({theme}) => theme.spacing(3.5)};
font-weight: ${({theme}) => theme.font.weight.semiBold};
font-family: ${({theme}) => theme.font.family};
@@ -26,7 +25,6 @@ const StyledButton = styled.button`
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
transition-duration: 167ms;
&:hover {
background-color: var(--hover-bg-color);
color: var(--hover-color);
@@ -1,6 +1,5 @@
import { ThemeProvider } from '@emotion/react';
import type React from 'react';
import { THEME_DARK, ThemeContextProvider } from 'twenty-ui/theme';
import { ColorSchemeProvider } from 'twenty-ui/theme-constants';
type ThemeContextProps = {
children: React.ReactNode;
@@ -8,10 +7,6 @@ type ThemeContextProps = {
export const ThemeContext = ({ children }: ThemeContextProps) => {
return (
<ThemeProvider theme={THEME_DARK}>
<ThemeContextProvider theme={THEME_DARK}>
{children}
</ThemeContextProvider>
</ThemeProvider>
<ColorSchemeProvider colorScheme="dark">{children}</ColorSchemeProvider>
);
};
@@ -1,5 +1,3 @@
import type { ThemeType } from 'twenty-ui/theme';
declare module '@emotion/react' {
export interface Theme extends ThemeType {}
export interface Theme {}
}
+3 -13
View File
@@ -2,9 +2,7 @@ import { i18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { type Preview } from '@storybook/react-vite';
import { initialize, mswLoader } from 'msw-storybook-addon';
import { useEffect } from 'react';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
//import { useDarkMode } from 'storybook-dark-mode';
// eslint-disable-next-line no-restricted-imports
import { RootDecorator } from '../src/testing/decorators/RootDecorator';
@@ -13,7 +11,7 @@ import { resetJotaiStore } from '../src/modules/ui/utilities/state/jotai/jotaiSt
import 'react-loading-skeleton/dist/skeleton.css';
import 'twenty-ui/style.css';
import { THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme';
import { ColorSchemeProvider } from 'twenty-ui/theme-constants';
// eslint-disable-next-line no-restricted-imports
import { messages as enMessages } from '../src/locales/generated/en';
@@ -60,23 +58,15 @@ initialize({
const preview: Preview = {
decorators: [
(Story) => {
// const theme = useDarkMode() ? THEME_DARK : THEME_LIGHT;
const theme = THEME_LIGHT;
useEffect(() => {
document.documentElement.className =
theme.name === 'dark' ? 'dark' : 'light';
}, [theme]);
return (
<I18nProvider i18n={i18n}>
<ThemeContextProvider theme={theme}>
<ColorSchemeProvider colorScheme="light">
<ClickOutsideListenerContext.Provider
value={{ excludedClickOutsideId: undefined }}
>
<Story />
</ClickOutsideListenerContext.Provider>
</ThemeContextProvider>
</ColorSchemeProvider>
</I18nProvider>
);
},
File diff suppressed because one or more lines are too long
@@ -143,92 +143,74 @@ export type Mutation = {
updateWorkflowVersionStep: WorkflowAction;
};
export type MutationActivateWorkflowVersionArgs = {
workflowVersionId: Scalars['UUID'];
};
export type MutationComputeStepOutputSchemaArgs = {
input: ComputeStepOutputSchemaInput;
};
export type MutationCreateDraftFromWorkflowVersionArgs = {
input: CreateDraftFromWorkflowVersionInput;
};
export type MutationCreateWorkflowVersionEdgeArgs = {
input: CreateWorkflowVersionEdgeInput;
};
export type MutationCreateWorkflowVersionStepArgs = {
input: CreateWorkflowVersionStepInput;
};
export type MutationDeactivateWorkflowVersionArgs = {
workflowVersionId: Scalars['UUID'];
};
export type MutationDeleteWorkflowVersionEdgeArgs = {
input: CreateWorkflowVersionEdgeInput;
};
export type MutationDeleteWorkflowVersionStepArgs = {
input: DeleteWorkflowVersionStepInput;
};
export type MutationDismissReconnectAccountBannerArgs = {
connectedAccountId: Scalars['UUID'];
};
export type MutationDuplicateWorkflowArgs = {
input: DuplicateWorkflowInput;
};
export type MutationDuplicateWorkflowVersionStepArgs = {
input: DuplicateWorkflowVersionStepInput;
};
export type MutationRunWorkflowVersionArgs = {
input: RunWorkflowVersionInput;
};
export type MutationStopWorkflowRunArgs = {
workflowRunId: Scalars['UUID'];
};
export type MutationSubmitFormStepArgs = {
input: SubmitFormStepInput;
};
export type MutationTestHttpRequestArgs = {
input: TestHttpRequestInput;
};
export type MutationUpdateWorkflowRunStepArgs = {
input: UpdateWorkflowRunStepInput;
};
export type MutationUpdateWorkflowVersionPositionsArgs = {
input: UpdateWorkflowVersionPositionsInput;
};
export type MutationUpdateWorkflowVersionStepArgs = {
input: UpdateWorkflowVersionStepInput;
};
@@ -254,49 +236,42 @@ export type Query = {
search: SearchResultConnection;
};
export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
companyId: Scalars['UUID'];
page: Scalars['Int'];
pageSize: Scalars['Int'];
};
export type QueryGetTimelineCalendarEventsFromOpportunityIdArgs = {
opportunityId: Scalars['UUID'];
page: Scalars['Int'];
pageSize: Scalars['Int'];
};
export type QueryGetTimelineCalendarEventsFromPersonIdArgs = {
page: Scalars['Int'];
pageSize: Scalars['Int'];
personId: Scalars['UUID'];
};
export type QueryGetTimelineThreadsFromCompanyIdArgs = {
companyId: Scalars['UUID'];
page: Scalars['Int'];
pageSize: Scalars['Int'];
};
export type QueryGetTimelineThreadsFromOpportunityIdArgs = {
opportunityId: Scalars['UUID'];
page: Scalars['Int'];
pageSize: Scalars['Int'];
};
export type QueryGetTimelineThreadsFromPersonIdArgs = {
page: Scalars['Int'];
pageSize: Scalars['Int'];
personId: Scalars['UUID'];
};
export type QuerySearchArgs = {
after?: InputMaybe<Scalars['String']>;
excludedObjectNameSingulars?: InputMaybe<Array<Scalars['String']>>;
@@ -579,7 +554,6 @@ export type GetTimelineCalendarEventsFromCompanyIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineCalendarEventsFromCompanyIdQuery = { __typename?: 'Query', getTimelineCalendarEventsFromCompanyId: { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: any, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, visibility: CalendarChannelVisibility, participants: Array<{ __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type GetTimelineCalendarEventsFromOpportunityIdQueryVariables = Exact<{
@@ -588,7 +562,6 @@ export type GetTimelineCalendarEventsFromOpportunityIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineCalendarEventsFromOpportunityIdQuery = { __typename?: 'Query', getTimelineCalendarEventsFromOpportunityId: { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: any, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, visibility: CalendarChannelVisibility, participants: Array<{ __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type GetTimelineCalendarEventsFromPersonIdQueryVariables = Exact<{
@@ -597,7 +570,6 @@ export type GetTimelineCalendarEventsFromPersonIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineCalendarEventsFromPersonIdQuery = { __typename?: 'Query', getTimelineCalendarEventsFromPersonId: { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: any, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, visibility: CalendarChannelVisibility, participants: Array<{ __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type ParticipantFragmentFragment = { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string };
@@ -612,7 +584,6 @@ export type GetTimelineThreadsFromCompanyIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineThreadsFromCompanyIdQuery = { __typename?: 'Query', getTimelineThreadsFromCompanyId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type GetTimelineThreadsFromOpportunityIdQueryVariables = Exact<{
@@ -621,7 +592,6 @@ export type GetTimelineThreadsFromOpportunityIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineThreadsFromOpportunityIdQuery = { __typename?: 'Query', getTimelineThreadsFromOpportunityId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{
@@ -630,7 +600,6 @@ export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{
pageSize: Scalars['Int'];
}>;
export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type SearchQueryVariables = Exact<{
@@ -642,7 +611,6 @@ export type SearchQueryVariables = Exact<{
filter?: InputMaybe<ObjectRecordFilterInput>;
}>;
export type SearchQuery = { __typename?: 'Query', search: { __typename?: 'SearchResultConnection', edges: Array<{ __typename?: 'SearchResultEdge', cursor: string, node: { __typename?: 'SearchRecord', recordId: any, objectNameSingular: string, objectLabelSingular: string, label: string, imageUrl?: string | null, tsRankCD: number, tsRank: number } }>, pageInfo: { __typename?: 'SearchResultPageInfo', hasNextPage: boolean, endCursor?: string | null } } };
export type WorkflowDiffFragmentFragment = { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null };
@@ -651,119 +619,102 @@ export type ActivateWorkflowVersionMutationVariables = Exact<{
workflowVersionId: Scalars['UUID'];
}>;
export type ActivateWorkflowVersionMutation = { __typename?: 'Mutation', activateWorkflowVersion: boolean };
export type ComputeStepOutputSchemaMutationVariables = Exact<{
input: ComputeStepOutputSchemaInput;
}>;
export type ComputeStepOutputSchemaMutation = { __typename?: 'Mutation', computeStepOutputSchema: any };
export type CreateDraftFromWorkflowVersionMutationVariables = Exact<{
input: CreateDraftFromWorkflowVersionInput;
}>;
export type CreateDraftFromWorkflowVersionMutation = { __typename?: 'Mutation', createDraftFromWorkflowVersion: { __typename?: 'WorkflowVersionDTO', id: any, name: string, status: string, trigger?: any | null, steps?: any | null, createdAt: string, updatedAt: string } };
export type CreateWorkflowVersionEdgeMutationVariables = Exact<{
input: CreateWorkflowVersionEdgeInput;
}>;
export type CreateWorkflowVersionEdgeMutation = { __typename?: 'Mutation', createWorkflowVersionEdge: { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null } };
export type CreateWorkflowVersionStepMutationVariables = Exact<{
input: CreateWorkflowVersionStepInput;
}>;
export type CreateWorkflowVersionStepMutation = { __typename?: 'Mutation', createWorkflowVersionStep: { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null } };
export type DeactivateWorkflowVersionMutationVariables = Exact<{
workflowVersionId: Scalars['UUID'];
}>;
export type DeactivateWorkflowVersionMutation = { __typename?: 'Mutation', deactivateWorkflowVersion: boolean };
export type DeleteWorkflowVersionEdgeMutationVariables = Exact<{
input: CreateWorkflowVersionEdgeInput;
}>;
export type DeleteWorkflowVersionEdgeMutation = { __typename?: 'Mutation', deleteWorkflowVersionEdge: { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null } };
export type DeleteWorkflowVersionStepMutationVariables = Exact<{
input: DeleteWorkflowVersionStepInput;
}>;
export type DeleteWorkflowVersionStepMutation = { __typename?: 'Mutation', deleteWorkflowVersionStep: { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null } };
export type DuplicateWorkflowMutationVariables = Exact<{
input: DuplicateWorkflowInput;
}>;
export type DuplicateWorkflowMutation = { __typename?: 'Mutation', duplicateWorkflow: { __typename?: 'WorkflowVersionDTO', id: any, name: string, status: string, trigger?: any | null, steps?: any | null, createdAt: string, updatedAt: string, workflowId: any } };
export type DuplicateWorkflowVersionStepMutationVariables = Exact<{
input: DuplicateWorkflowVersionStepInput;
}>;
export type DuplicateWorkflowVersionStepMutation = { __typename?: 'Mutation', duplicateWorkflowVersionStep: { __typename?: 'WorkflowVersionStepChanges', triggerDiff?: any | null, stepsDiff?: any | null } };
export type RunWorkflowVersionMutationVariables = Exact<{
input: RunWorkflowVersionInput;
}>;
export type RunWorkflowVersionMutation = { __typename?: 'Mutation', runWorkflowVersion: { __typename?: 'RunWorkflowVersion', workflowRunId: any } };
export type StopWorkflowRunMutationVariables = Exact<{
workflowRunId: Scalars['UUID'];
}>;
export type StopWorkflowRunMutation = { __typename?: 'Mutation', stopWorkflowRun: { __typename: 'WorkflowRun', id: any, status: WorkflowRunStatusEnum } };
export type UpdateWorkflowRunStepMutationVariables = Exact<{
input: UpdateWorkflowRunStepInput;
}>;
export type UpdateWorkflowRunStepMutation = { __typename?: 'Mutation', updateWorkflowRunStep: { __typename?: 'WorkflowAction', id: any, name: string, type: WorkflowActionType, settings: any, valid: boolean, nextStepIds?: Array<any> | null, position?: { __typename?: 'WorkflowStepPosition', x: number, y: number } | null } };
export type UpdateWorkflowVersionStepMutationVariables = Exact<{
input: UpdateWorkflowVersionStepInput;
}>;
export type UpdateWorkflowVersionStepMutation = { __typename?: 'Mutation', updateWorkflowVersionStep: { __typename?: 'WorkflowAction', id: any, name: string, type: WorkflowActionType, settings: any, valid: boolean, nextStepIds?: Array<any> | null, position?: { __typename?: 'WorkflowStepPosition', x: number, y: number } | null } };
export type SubmitFormStepMutationVariables = Exact<{
input: SubmitFormStepInput;
}>;
export type SubmitFormStepMutation = { __typename?: 'Mutation', submitFormStep: boolean };
export type TestHttpRequestMutationVariables = Exact<{
input: TestHttpRequestInput;
}>;
export type TestHttpRequestMutation = { __typename?: 'Mutation', testHttpRequest: { __typename?: 'TestHttpRequest', success: boolean, message: string, result?: any | null, error?: any | null, status?: number | null, statusText?: string | null, headers?: any | null } };
export type UpdateWorkflowVersionPositionsMutationVariables = Exact<{
input: UpdateWorkflowVersionPositionsInput;
}>;
export type UpdateWorkflowVersionPositionsMutation = { __typename?: 'Mutation', updateWorkflowVersionPositions: boolean };
export const TimelineCalendarEventParticipantFragmentFragmentDoc = gql`
@@ -1,11 +1,11 @@
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useLingui } from '@lingui/react/macro';
import { IconCopy, IconExclamationCircle } from 'twenty-ui/display';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const useCopyToClipboard = () => {
const { theme } = useContext(ThemeContext);
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const { t } = useLingui();
@@ -28,7 +28,13 @@ export const useCopyToClipboard = () => {
enqueueSuccessSnackBar({
message: message || t`Copied to clipboard`,
options: {
icon: <IconCopy size={theme.icon.size.md} />,
icon: (
<IconCopy
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
),
duration: 2000,
},
});
@@ -5,9 +5,12 @@ import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { NAVIGATION_DRAWER_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/NavigationDrawerConstraints';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useContext } from 'react';
import { ANIMATION, ThemeContext } from 'twenty-ui/theme';
import { MainNavigationDrawerItemsSkeletonLoader } from '~/loading/components/MainNavigationDrawerItemsSkeletonLoader';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledAnimatedContainer = styled(motion.div)`
align-items: center;
@@ -48,8 +51,6 @@ const StyledSkeletonTitleContainer = styled.div`
export const LeftPanelSkeletonLoader = () => {
const isMobile = useIsMobile();
const { theme } = useContext(ThemeContext);
return (
<StyledAnimatedContainer
initial={false}
@@ -57,13 +58,21 @@ export const LeftPanelSkeletonLoader = () => {
width: isMobile ? 0 : NAVIGATION_DRAWER_CONSTRAINTS.default,
opacity: isMobile ? 0 : 1,
}}
transition={{ duration: ANIMATION.duration.fast }}
transition={{
duration: resolveThemeVariableAsNumber(
themeCssVariables.animation.duration.fast,
),
}}
>
<StyledItemsContainer>
<StyledSkeletonTitleContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<Skeleton
@@ -1,8 +1,10 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledSkeletonContainer = styled.div`
align-items: flex-start;
@@ -21,13 +23,13 @@ export const MainNavigationDrawerItemsSkeletonLoader = ({
title?: boolean;
length: number;
}) => {
const { theme } = useContext(ThemeContext);
return (
<StyledSkeletonContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
{title && (
@@ -1,9 +1,10 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledRightDrawerContainer = styled.div`
display: flex;
@@ -13,12 +14,12 @@ const StyledRightDrawerContainer = styled.div`
`;
const StyledSkeletonLoader = () => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<Skeleton height={SKELETON_LOADER_HEIGHT_SIZES.standard.m} width={140} />
@@ -1,9 +1,11 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
import {
MOBILE_VIEWPORT,
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledMainContainer = styled.div`
background: ${themeCssVariables.background.noisy};
@@ -50,13 +52,13 @@ const StyledRightPanelFlexContainer = styled.div`
`;
const StyledSkeletonHeaderLoader = () => {
const { theme } = useContext(ThemeContext);
return (
<StyledHeaderContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<Skeleton
@@ -69,12 +71,12 @@ const StyledSkeletonHeaderLoader = () => {
};
const StyledSkeletonAddLoader = () => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<Skeleton width={132} height={SKELETON_LOADER_HEIGHT_SIZES.standard.s} />
@@ -3,7 +3,10 @@ import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { styled } from '@linaria/react';
import { motion } from 'framer-motion';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledActionContainer = styled(motion.div)`
display: flex;
@@ -13,8 +16,6 @@ const StyledActionContainer = styled(motion.div)`
export const PageHeaderActionMenuButtons = () => {
const { actions } = useContext(ActionMenuContext);
const { theme } = useContext(ThemeContext);
const pinnedActions = actions.filter((entry) => entry.isPinned).toReversed();
const actionsWithPositionForAnimation = pinnedActions.map(
@@ -34,7 +35,9 @@ export const PageHeaderActionMenuButtons = () => {
animate={{ width: 'unset', opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{
duration: theme.animation.duration.instant,
duration: resolveThemeVariable(
themeCssVariables.animation.duration.instant,
),
ease: 'easeInOut',
}}
>
@@ -1,12 +1,13 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { differenceInSeconds, endOfDay, format } from 'date-fns';
import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow';
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
import { CardContent } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
import { type TimelineCalendarEvent } from '~/generated/graphql';
type CalendarDayCardContentProps = {
@@ -14,12 +15,14 @@ type CalendarDayCardContentProps = {
divider?: boolean;
};
const StyledCardContent = styled(CardContent)`
align-items: flex-start;
display: flex;
flex-direction: row;
gap: ${themeCssVariables.spacing[3]};
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
const StyledCardContentContainer = styled.div`
> div {
align-items: flex-start;
display: flex;
flex-direction: row;
gap: ${themeCssVariables.spacing[3]};
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
}
`;
const StyledDayContainer = styled.div`
@@ -45,7 +48,7 @@ const StyledEvents = styled.div`
gap: ${themeCssVariables.spacing[3]};
`;
const StyledEventRow = styled(CalendarEventRow)`
const StyledEventRowWrapper = styled.div`
flex: 1 0 auto;
`;
@@ -53,8 +56,6 @@ export const CalendarDayCardContent = ({
calendarEvents,
divider,
}: CalendarDayCardContentProps) => {
const { theme } = useContext(ThemeContext);
const endOfDayDate = endOfDay(getCalendarEventStartDate(calendarEvents[0]));
const dayEndsIn = differenceInSeconds(endOfDayDate, Date.now());
@@ -63,32 +64,39 @@ export const CalendarDayCardContent = ({
const upcomingDayCardContentVariants = {
upcoming: {},
ended: { backgroundColor: theme.background.primary },
ended: {
backgroundColor: resolveThemeVariable(
themeCssVariables.background.primary,
),
},
};
return (
<StyledCardContent
divider={divider}
initial="upcoming"
animate="ended"
variants={upcomingDayCardContentVariants}
transition={{
delay: Math.max(0, dayEndsIn),
duration: theme.animation.duration.fast,
}}
>
<StyledDayContainer>
<StyledWeekDay>{weekDayLabel}</StyledWeekDay>
<StyledMonthDay>{monthDayLabel}</StyledMonthDay>
</StyledDayContainer>
<StyledEvents>
{calendarEvents.map((calendarEvent) => (
<StyledEventRow
key={calendarEvent.id}
calendarEvent={calendarEvent}
/>
))}
</StyledEvents>
</StyledCardContent>
<StyledCardContentContainer>
<CardContent
divider={divider}
initial="upcoming"
animate="ended"
variants={upcomingDayCardContentVariants}
transition={{
delay: Math.max(0, dayEndsIn),
duration: resolveThemeVariable(
themeCssVariables.animation.duration.fast,
),
}}
>
<StyledDayContainer>
<StyledWeekDay>{weekDayLabel}</StyledWeekDay>
<StyledMonthDay>{monthDayLabel}</StyledMonthDay>
</StyledDayContainer>
<StyledEvents>
{calendarEvents.map((calendarEvent) => (
<StyledEventRowWrapper key={calendarEvent.id}>
<CalendarEventRow calendarEvent={calendarEvent} />
</StyledEventRowWrapper>
))}
</StyledEvents>
</CardContent>
</StyledCardContentContainer>
);
};
@@ -85,9 +85,11 @@ const StyledFields = styled.div`
width: 100%;
`;
const StyledPropertyBox = styled(PropertyBox)`
height: ${themeCssVariables.spacing[6]};
width: 100%;
const StyledPropertyBoxWrapper = styled.div`
> div {
height: ${themeCssVariables.spacing[6]};
width: 100%;
}
`;
export const CalendarEventDetails = ({
@@ -165,35 +167,37 @@ export const CalendarEventDetails = ({
});
return (
<StyledPropertyBox key={fieldMetadataItem.id}>
<FieldContext.Provider
value={{
recordId: calendarEvent.id,
isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem,
showLabel: true,
labelWidth: 72,
}),
useUpdateRecord: useUpdateOneCalendarEventRecordMutation,
maxWidth: 300,
isRecordFieldReadOnly: isReadOnly,
}}
>
<RecordFieldComponentInstanceContext.Provider
<StyledPropertyBoxWrapper key={fieldMetadataItem.id}>
<PropertyBox>
<FieldContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId: calendarEvent.id,
fieldName: fieldMetadataItem.name,
prefix: INPUT_ID_PREFIX,
recordId: calendarEvent.id,
isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem,
showLabel: true,
labelWidth: 72,
}),
useUpdateRecord: useUpdateOneCalendarEventRecordMutation,
maxWidth: 300,
isRecordFieldReadOnly: isReadOnly,
}}
>
<RecordInlineCell />
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider>
</StyledPropertyBox>
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId: calendarEvent.id,
fieldName: fieldMetadataItem.name,
prefix: INPUT_ID_PREFIX,
}),
}}
>
<RecordInlineCell />
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider>
</PropertyBox>
</StyledPropertyBoxWrapper>
);
};
@@ -1,40 +1,52 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { Trans } from '@lingui/react/macro';
import { IconLock } from 'twenty-ui/display';
import { Card, CardContent } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledVisibilityCard = styled(Card)`
color: ${themeCssVariables.font.color.light};
const StyledVisibilityCardContainer = styled.div`
flex: 1;
border-color: ${themeCssVariables.border.color.light};
transition: color ${themeCssVariables.animation.duration.normal} ease;
width: 100%;
> div {
color: ${themeCssVariables.font.color.light};
border-color: ${themeCssVariables.border.color.light};
transition: color ${themeCssVariables.animation.duration.normal} ease;
}
`;
const StyledVisibilityCardContent = styled(CardContent)`
align-items: center;
background-color: ${themeCssVariables.background.transparent.lighter};
box-sizing: border-box;
display: flex;
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[1]};
height: ${themeCssVariables.spacing[6]};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1]};
const StyledVisibilityCardContentContainer = styled.div`
> div {
align-items: center;
background-color: ${themeCssVariables.background.transparent.lighter};
box-sizing: border-box;
display: flex;
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[1]};
height: ${themeCssVariables.spacing[6]};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1]};
}
`;
export const CalendarEventNotSharedContent = () => {
const { theme } = useContext(ThemeContext);
return (
<StyledVisibilityCard>
<StyledVisibilityCardContent>
<IconLock size={theme.icon.size.sm} />
<Trans>Not shared</Trans>
</StyledVisibilityCardContent>
</StyledVisibilityCard>
<StyledVisibilityCardContainer>
<Card>
<StyledVisibilityCardContentContainer>
<CardContent>
<IconLock
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<Trans>Not shared</Trans>
</CardContent>
</StyledVisibilityCardContentContainer>
</Card>
</StyledVisibilityCardContainer>
);
};
@@ -1,4 +1,4 @@
import { useContext, useRef } from 'react';
import { useRef } from 'react';
import { styled } from '@linaria/react';
import { type CalendarEventParticipant } from '@/activities/calendar/types/CalendarEventParticipant';
@@ -7,8 +7,10 @@ import { ParticipantChip } from '@/activities/components/ParticipantChip';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
import { IconCheck, IconQuestionMark, IconX } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledInlineCellBaseContainer = styled.div`
align-items: center;
@@ -22,9 +24,11 @@ const StyledInlineCellBaseContainer = styled.div`
user-select: none;
`;
const StyledPropertyBox = styled(PropertyBox)`
height: ${themeCssVariables.spacing[6]};
width: 100%;
const StyledPropertyBoxWrapper = styled.div`
> div {
height: ${themeCssVariables.spacing[6]};
width: 100%;
}
`;
const StyledIconContainer = styled.div`
@@ -65,12 +69,22 @@ export const CalendarEventParticipantsResponseStatusField = ({
responseStatus: 'Yes' | 'Maybe' | 'No';
participants: CalendarEventParticipant[];
}) => {
const { theme } = useContext(ThemeContext);
const Icon = {
Yes: <IconCheck stroke={theme.icon.stroke.sm} />,
Maybe: <IconQuestionMark stroke={theme.icon.stroke.sm} />,
No: <IconX stroke={theme.icon.stroke.sm} />,
Yes: (
<IconCheck
stroke={resolveThemeVariableAsNumber(themeCssVariables.icon.stroke.sm)}
/>
),
Maybe: (
<IconQuestionMark
stroke={resolveThemeVariableAsNumber(themeCssVariables.icon.stroke.sm)}
/>
),
No: (
<IconX
stroke={resolveThemeVariableAsNumber(themeCssVariables.icon.stroke.sm)}
/>
),
}[responseStatus];
// We want to display external participants first
@@ -88,19 +102,21 @@ export const CalendarEventParticipantsResponseStatusField = ({
));
return (
<StyledPropertyBox>
<StyledInlineCellBaseContainer>
<StyledLabelAndIconContainer>
<StyledIconContainer>{Icon}</StyledIconContainer>
<StyledPropertyBoxWrapper>
<PropertyBox>
<StyledInlineCellBaseContainer>
<StyledLabelAndIconContainer>
<StyledIconContainer>{Icon}</StyledIconContainer>
<StyledLabelContainer width={72}>
<EllipsisDisplay>{responseStatus}</EllipsisDisplay>
</StyledLabelContainer>
</StyledLabelAndIconContainer>
<StyledDiv ref={participantsContainerRef}>
<ExpandableList isChipCountDisplayed>{styledChips}</ExpandableList>
</StyledDiv>
</StyledInlineCellBaseContainer>
</StyledPropertyBox>
<StyledLabelContainer width={72}>
<EllipsisDisplay>{responseStatus}</EllipsisDisplay>
</StyledLabelContainer>
</StyledLabelAndIconContainer>
<StyledDiv ref={participantsContainerRef}>
<ExpandableList isChipCountDisplayed>{styledChips}</ExpandableList>
</StyledDiv>
</StyledInlineCellBaseContainer>
</PropertyBox>
</StyledPropertyBoxWrapper>
);
};
@@ -1,4 +1,3 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { format } from 'date-fns';
@@ -16,8 +15,10 @@ import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEv
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useOpenCalendarEventInCommandMenu } from '@/command-menu/hooks/useOpenCalendarEventInCommandMenu';
import { IconArrowRight } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type CalendarEventRowProps = {
calendarEvent: TimelineCalendarEvent;
@@ -76,7 +77,6 @@ export const CalendarEventRow = ({
calendarEvent,
className,
}: CalendarEventRowProps) => {
const { theme } = useContext(ThemeContext);
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const { openCalendarEventInCommandMenu } =
useOpenCalendarEventInCommandMenu();
@@ -114,7 +114,11 @@ export const CalendarEventRow = ({
{startTimeLabel}
{endTimeLabel && (
<>
<IconArrowRight size={theme.icon.size.sm} />
<IconArrowRight
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
{endTimeLabel}
</>
)}
@@ -2,16 +2,20 @@ import { styled } from '@linaria/react';
import { Card } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledList = styled(Card)`
& > :not(:last-child) {
border-bottom: 1px solid ${themeCssVariables.border.color.light};
}
const StyledListContainer = styled.div`
width: calc(100% - 2px);
overflow: auto;
> div {
overflow: auto;
& > :not(:last-child) {
border-bottom: 1px solid ${themeCssVariables.border.color.light};
}
}
`;
export const ActivityList = ({ children }: React.PropsWithChildren) => {
return <StyledList>{children}</StyledList>;
return (
<StyledListContainer>
<Card>{children}</Card>
</StyledListContainer>
);
};
@@ -3,15 +3,17 @@ import React from 'react';
import { CardContent } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRowContent = styled(CardContent)`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[2]};
height: ${themeCssVariables.spacing[12]};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[4]};
const StyledRowContentContainer = styled.div`
> div {
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[2]};
height: ${themeCssVariables.spacing[12]};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[4]};
&[data-clickable='false'] {
cursor: default;
&[data-clickable='false'] {
cursor: default;
}
}
`;
@@ -30,8 +32,10 @@ export const ActivityRow = ({
};
return (
<StyledRowContent onClick={handleClick} isClickable={disabled !== true}>
{children}
</StyledRowContent>
<StyledRowContentContainer>
<CardContent onClick={handleClick} isClickable={disabled !== true}>
{children}
</CardContent>
</StyledRowContentContainer>
);
};
@@ -7,7 +7,7 @@ import { RecordChip } from '@/object-record/components/RecordChip';
import { Avatar } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledAvatar = styled(Avatar)`
const StyledAvatarContainer = styled.div`
margin-right: ${themeCssVariables.spacing[1]};
`;
@@ -67,12 +67,14 @@ export const ParticipantChip = ({
/>
) : (
<StyledChip>
<StyledAvatar
avatarUrl={avatarUrl}
type="rounded"
placeholder={displayName}
size="sm"
/>
<StyledAvatarContainer>
<Avatar
avatarUrl={avatarUrl}
type="rounded"
placeholder={displayName}
size="sm"
/>
</StyledAvatarContainer>
<StyledSenderName variant={variant}>{displayName}</StyledSenderName>
</StyledChip>
)}
@@ -1,8 +1,9 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledSkeletonContainer = styled.div`
align-items: center;
@@ -43,12 +44,12 @@ export const SKELETON_LOADER_HEIGHT_SIZES = {
};
const SkeletonColumnLoader = ({ height }: { height: number }) => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={80}
>
<Skeleton width={24} height={height} />
@@ -61,15 +62,16 @@ export const SkeletonLoader = ({
}: {
withSubSections?: boolean;
}) => {
const { theme } = useContext(ThemeContext);
const skeletonItems = Array.from({ length: 3 }).map((_, index) => ({
id: `skeleton-item-${index}`,
}));
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<StyledSkeletonContainer>
@@ -1,9 +1,10 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { AppTooltip, IconLock, TooltipDelay } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { MessageChannelVisibility } from '~/generated/graphql';
const StyledContainer = styled.div<{ isCompact?: boolean }>`
@@ -35,14 +36,15 @@ export const EmailThreadNotShared = ({
visibility,
}: EmailThreadNotSharedProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const containerId = 'email-thread-not-shared';
const isCompact = visibility === MessageChannelVisibility.SUBJECT;
return (
<>
<StyledContainer id={containerId} isCompact={isCompact}>
<IconLock size={theme.icon.size.sm} />
<IconLock
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
{t`Not shared`}
</StyledContainer>
{visibility === MessageChannelVisibility.SUBJECT && (
@@ -1,12 +1,13 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { ActivityRow } from '@/activities/components/ActivityRow';
import { EmailThreadNotShared } from '@/activities/emails/components/EmailThreadNotShared';
import { useOpenEmailThreadInCommandMenu } from '@/command-menu/hooks/useOpenEmailThreadInCommandMenu';
import { Avatar } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
import {
MessageChannelVisibility,
type TimelineThread,
@@ -107,8 +108,6 @@ export const EmailThreadPreview = ({ thread }: EmailThreadPreviewProps) => {
};
const isDisabled = visibility !== MessageChannelVisibility.SHARE_EVERYTHING;
const { theme } = useContext(ThemeContext);
return (
<ActivityRow onClick={handleThreadClick} disabled={isDisabled}>
<StyledHeading unread={!thread.read}>
@@ -141,9 +140,15 @@ export const EmailThreadPreview = ({ thread }: EmailThreadPreviewProps) => {
avatarUrl={finalAvatarUrl}
placeholder={finalDisplayedName}
type="rounded"
color={isCountIcon ? theme.grayScale.gray11 : undefined}
color={
isCountIcon
? resolveThemeVariable(themeCssVariables.grayScale.gray11)
: undefined
}
backgroundColor={
isCountIcon ? theme.grayScale.gray2 : undefined
isCountIcon
? resolveThemeVariable(themeCssVariables.grayScale.gray2)
: undefined
}
/>
</StyledAvatarWrapper>
@@ -38,9 +38,11 @@ const StyledContainer = styled.div`
overflow: auto;
`;
const StyledH1Title = styled(H1Title)`
display: flex;
gap: ${themeCssVariables.spacing[2]};
const StyledH1TitleWrapper = styled.div`
> div {
display: flex;
gap: ${themeCssVariables.spacing[2]};
}
`;
const StyledEmailCount = styled.span`
@@ -107,15 +109,17 @@ export const EmailsCard = () => {
return (
<StyledContainer>
<Section>
<StyledH1Title
title={
<>
<Trans>Inbox</Trans>{' '}
<StyledEmailCount>{totalNumberOfThreads}</StyledEmailCount>
</>
}
fontColor={H1TitleFontColor.Primary}
/>
<StyledH1TitleWrapper>
<H1Title
title={
<>
<Trans>Inbox</Trans>{' '}
<StyledEmailCount>{totalNumberOfThreads}</StyledEmailCount>
</>
}
fontColor={H1TitleFontColor.Primary}
/>
</StyledH1TitleWrapper>
{!firstQueryLoading && (
<ActivityList>
{timelineThreads?.map((thread: TimelineThread) => (
@@ -10,7 +10,7 @@ import {
import { getFileCategoryFromExtension } from '@/object-record/record-field/ui/utils/getFileCategoryFromExtension';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { type AttachmentWithFile } from '@/activities/files/utils/filterAttachmentsWithFile';
@@ -18,8 +18,10 @@ import { FileIcon } from '@/file/components/FileIcon';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { isNavigationModifierPressed } from 'twenty-ui/utilities';
import { PermissionFlagType } from '~/generated-metadata/graphql';
import { formatToHumanReadableDate } from '~/utils/date-utils';
@@ -81,7 +83,6 @@ export const AttachmentRow = ({
attachment,
onPreview,
}: AttachmentRowProps) => {
const { theme } = useContext(ThemeContext);
const [isEditing, setIsEditing] = useState(false);
const hasDownloadPermission = useHasPermissionFlag(
@@ -200,7 +201,11 @@ export const AttachmentRow = ({
</StyledLeftContent>
<StyledRightContent>
<StyledCalendarIconContainer>
<IconCalendar size={theme.icon.size.md} />
<IconCalendar
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
</StyledCalendarIconContainer>
{formatToHumanReadableDate(attachment.createdAt)}
<AttachmentDropdown
@@ -9,12 +9,14 @@ import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
import '@cyntler/react-doc-viewer/dist/index.css';
import { styled } from '@linaria/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconDownload } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
const MS_OFFICE_EXTENSIONS = [
@@ -201,7 +203,6 @@ export const DocumentViewer = ({
documentExtension,
}: DocumentViewerProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const [csvPreview, setCsvPreview] = useState<CsvPreviewData | undefined>(
undefined,
);
@@ -319,8 +320,10 @@ export const DocumentViewer = ({
pluginRenderers={DocViewerRenderers}
style={{
height: '100%',
color: theme.font.color.primary,
backgroundColor: theme.background.primary,
color: resolveThemeVariable(themeCssVariables.font.color.primary),
backgroundColor: resolveThemeVariable(
themeCssVariables.background.primary,
),
}}
config={{
header: {
@@ -335,12 +338,20 @@ export const DocumentViewer = ({
},
}}
theme={{
primary: theme.background.primary,
secondary: theme.background.secondary,
tertiary: theme.background.tertiary,
textPrimary: theme.font.color.primary,
textSecondary: theme.font.color.secondary,
textTertiary: theme.font.color.tertiary,
primary: resolveThemeVariable(themeCssVariables.background.primary),
secondary: resolveThemeVariable(
themeCssVariables.background.secondary,
),
tertiary: resolveThemeVariable(themeCssVariables.background.tertiary),
textPrimary: resolveThemeVariable(
themeCssVariables.font.color.primary,
),
textSecondary: resolveThemeVariable(
themeCssVariables.font.color.secondary,
),
textTertiary: resolveThemeVariable(
themeCssVariables.font.color.tertiary,
),
disableThemeScrollbar: true,
}}
/>
@@ -1,12 +1,13 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useDropzone } from 'react-dropzone';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { IconUpload } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: center;
@@ -35,9 +36,10 @@ const StyledUploadDragSubTitle = styled.div`
line-height: ${themeCssVariables.text.lineHeight.md};
`;
const StyledUploadIcon = styled(IconUpload)`
const StyledUploadIconContainer = styled.span`
color: ${themeCssVariables.font.color.tertiary};
margin-bottom: ${themeCssVariables.spacing[3]};
display: flex;
`;
type DropZoneProps = {
@@ -50,7 +52,6 @@ export const DropZone = ({
onUploadFiles,
}: DropZoneProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { maxFileSize } = useSpreadsheetImportInternal();
const { getRootProps, getInputProps, isDragActive } = useDropzone({
@@ -84,10 +85,16 @@ export const DropZone = ({
// eslint-disable-next-line react/jsx-props-no-spreading
{...getInputProps()}
/>
<StyledUploadIcon
stroke={theme.icon.stroke.sm}
size={theme.icon.size.lg}
/>
<StyledUploadIconContainer>
<IconUpload
stroke={resolveThemeVariableAsNumber(
themeCssVariables.icon.stroke.sm,
)}
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.lg,
)}
/>
</StyledUploadIconContainer>
<StyledUploadDragTitle>{t`Upload files`}</StyledUploadDragTitle>
<StyledUploadDragSubTitle>
{t`Drag and Drop Here`}
@@ -1,4 +1,3 @@
import { useContext } from 'react';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
@@ -16,8 +15,10 @@ import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-l
import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { Checkbox, CheckboxShape } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask';
const StyledTaskBody = styled.div`
@@ -86,7 +87,6 @@ const StyledCheckboxContainer = styled.div`
`;
export const TaskRow = ({ task }: { task: Task }) => {
const { theme } = useContext(ThemeContext);
const { openRecordInCommandMenu } = useOpenRecordInCommandMenu();
const body = getActivitySummary(task?.bodyV2?.blocknote ?? null);
@@ -131,7 +131,11 @@ export const TaskRow = ({ task }: { task: Task }) => {
<StyledDueDate
isPast={hasDatePassed(task.dueAt) && task.status === 'TODO'}
>
<IconCalendar size={theme.icon.size.md} />
<IconCalendar
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
{beautifyExactDate(task.dueAt)}
</StyledDueDate>
)}
@@ -38,9 +38,7 @@ const StyledMainContainer = styled.div`
}
`;
const StyledRightDrawerAnimatedPlaceholderEmptyContainer = styled(
AnimatedPlaceholderEmptyContainer,
)`
const StyledRightDrawerEmptyContainerWrapper = styled.div`
height: auto;
padding-top: ${themeCssVariables.spacing[8]};
`;
@@ -59,12 +57,8 @@ export const TimelineCard = () => {
}
if (isTimelineActivitiesEmpty) {
const EmptyContainer = isInRightDrawer
? StyledRightDrawerAnimatedPlaceholderEmptyContainer
: AnimatedPlaceholderEmptyContainer;
return (
<EmptyContainer
const content = (
<AnimatedPlaceholderEmptyContainer
// eslint-disable-next-line react/jsx-props-no-spreading
{...EMPTY_PLACEHOLDER_TRANSITION_PROPS}
>
@@ -77,7 +71,15 @@ export const TimelineCard = () => {
{t`There is no activity associated with this record.`}
</AnimatedPlaceholderEmptySubTitle>
</AnimatedPlaceholderEmptyTextContainer>
</EmptyContainer>
</AnimatedPlaceholderEmptyContainer>
);
return isInRightDrawer ? (
<StyledRightDrawerEmptyContainerWrapper>
{content}
</StyledRightDrawerEmptyContainerWrapper>
) : (
content
);
}
@@ -20,27 +20,27 @@ const StyledCardContainer = styled.div`
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: 300px;
}
`;
const StyledCard = styled(Card)`
background: ${themeCssVariables.background.secondary};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.md};
box-sizing: border-box;
display: flex;
padding: ${themeCssVariables.spacing[2]};
flex-direction: column;
align-items: flex-start;
gap: ${themeCssVariables.spacing[2]};
justify-content: center;
align-self: stretch;
> div {
background: ${themeCssVariables.background.secondary};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.md};
box-sizing: border-box;
display: flex;
padding: ${themeCssVariables.spacing[2]};
flex-direction: column;
align-items: flex-start;
gap: ${themeCssVariables.spacing[2]};
justify-content: center;
align-self: stretch;
}
`;
export const EventCard = ({ children, isOpen }: EventCardProps) => {
return (
isOpen && (
<StyledCardContainer>
<StyledCard fullWidth>{children}</StyledCard>
<Card fullWidth>{children}</Card>
</StyledCardContainer>
)
);
@@ -26,8 +26,12 @@ export const StyledEventRowItemColumn = styled.div`
gap: ${themeCssVariables.spacing[1]};
`;
export const StyledEventRowItemAction = styled(StyledEventRowItemColumn)`
export const StyledEventRowItemAction = styled.div`
align-items: center;
color: ${themeCssVariables.font.color.secondary};
display: flex;
flex-direction: row;
gap: ${themeCssVariables.spacing[1]};
`;
export const EventRowDynamicComponent = ({
@@ -13,11 +13,13 @@ type BubbleMenuIconButtonProps = {
isActive?: boolean;
};
const StyledBubbleMenuIconButton = styled(FloatingIconButton)`
border: none;
border-radius: ${themeCssVariables.spacing[1.5]};
width: ${themeCssVariables.spacing[6]};
height: ${themeCssVariables.spacing[6]};
const StyledBubbleMenuIconButtonContainer = styled.div`
& > button {
border: none;
border-radius: ${themeCssVariables.spacing[1.5]};
width: ${themeCssVariables.spacing[6]};
height: ${themeCssVariables.spacing[6]};
}
`;
export const BubbleMenuIconButton = ({
@@ -29,17 +31,18 @@ export const BubbleMenuIconButton = ({
isActive,
}: BubbleMenuIconButtonProps) => {
return (
<StyledBubbleMenuIconButton
className={className}
Icon={Icon}
disabled={disabled}
focus={focus}
onClick={onClick}
isActive={isActive}
applyShadow={false}
applyBlur={false}
size="medium"
position="standalone"
/>
<StyledBubbleMenuIconButtonContainer className={className}>
<FloatingIconButton
Icon={Icon}
disabled={disabled}
focus={focus}
onClick={onClick}
isActive={isActive}
applyShadow={false}
applyBlur={false}
size="medium"
position="standalone"
/>
</StyledBubbleMenuIconButtonContainer>
);
};
@@ -6,11 +6,14 @@ import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown'
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { type Editor } from '@tiptap/react';
import { useContext, useId } from 'react';
import { useId } from 'react';
import { IconPilcrow } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledMenuItem = styled.button`
align-items: center;
@@ -44,8 +47,6 @@ type TurnIntoBlockDropdownProps = {
export const TurnIntoBlockDropdown = ({
editor,
}: TurnIntoBlockDropdownProps) => {
const { theme } = useContext(ThemeContext);
const instanceId = useId();
const dropdownId = `turn-into-block-dropdown-${instanceId}`;
const { toggleDropdown } = useToggleDropdown();
@@ -79,12 +80,14 @@ export const TurnIntoBlockDropdown = ({
dropdownId={dropdownId}
clickableComponent={
<StyledMenuItem>
<ActiveIcon size={theme.icon.size.md} />
<ActiveIcon
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
/>
{activeTitle}
</StyledMenuItem>
}
dropdownOffset={{
y: parseInt(theme.spacing(1), 10),
y: parseInt(resolveThemeVariable(themeCssVariables.spacing[1]), 10),
}}
/>
);
@@ -2,12 +2,13 @@ import { getFileType } from '@/activities/files/utils/getFileType';
import { useFileCategoryColors } from '@/file/hooks/useFileCategoryColors';
import { IconMapping } from '@/file/utils/fileIconMappings';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { type WorkflowAttachment } from 'twenty-shared/workflow';
import { AvatarOrIcon } from 'twenty-ui/components';
import { IconX } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type WorkflowAttachmentChipProps = {
file: WorkflowAttachment;
@@ -66,8 +67,6 @@ export const WorkflowAttachmentChip = ({
readonly = false,
}: WorkflowAttachmentChipProps) => {
const iconColors = useFileCategoryColors();
const { theme } = useContext(ThemeContext);
return (
<StyledChip data-chip deletable={!readonly}>
<AvatarOrIcon
@@ -78,7 +77,12 @@ export const WorkflowAttachmentChip = ({
{!readonly && (
<StyledDelete onClick={onRemove}>
<IconX size={theme.icon.size.sm} stroke={theme.icon.stroke.sm} />
<IconX
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
stroke={resolveThemeVariableAsNumber(
themeCssVariables.icon.stroke.sm,
)}
/>
</StyledDelete>
)}
</StyledChip>
@@ -4,12 +4,14 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { type ChangeEvent, useContext, useRef } from 'react';
import { type ChangeEvent, useRef } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { type WorkflowAttachment } from 'twenty-shared/workflow';
import { IconUpload } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type WorkflowSendEmailAttachmentsProps = {
files: WorkflowAttachment[];
@@ -70,8 +72,6 @@ export const WorkflowSendEmailAttachments = ({
const fileInputRef = useRef<HTMLInputElement>(null);
const { uploadWorkflowFile } = useUploadWorkflowFile();
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const handleAddFileClick = (e: React.MouseEvent) => {
const target = e.target as HTMLElement;
@@ -141,7 +141,11 @@ export const WorkflowSendEmailAttachments = ({
</StyledChipsContainer>
) : (
<StyledUploadAreaLabel>
<IconUpload size={theme.icon.size.sm} />
<IconUpload
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<span>{t`Upload file`}</span>
</StyledUploadAreaLabel>
)}
@@ -7,11 +7,12 @@ import { LazyMarkdownRenderer } from '@/ai/components/LazyMarkdownRenderer';
import { ToolStepRenderer } from '@/ai/components/ToolStepRenderer';
import { groupContiguousThinkingStepParts } from '@/ai/utils/groupContiguousThinkingStepParts';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { isToolUIPart, type ToolUIPart } from 'ai';
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledMessagePartsContainer = styled.div`
display: flex;
@@ -28,17 +29,20 @@ const StyledLoadingIconContainer = styled.div`
padding-inline: ${themeCssVariables.spacing[1]};
`;
const StyledLoadingIcon = styled(IconDotsVertical)`
const StyledLoadingIconWrapper = styled.span`
color: ${themeCssVariables.font.color.light};
transform: rotate(90deg);
display: flex;
`;
const InitialLoadingIndicator = () => {
const { theme } = useContext(ThemeContext);
return (
<StyledLoadingIconContainer>
<StyledLoadingIcon size={theme.icon.size.xl} />
<StyledLoadingIconWrapper>
<IconDotsVertical
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.xl)}
/>
</StyledLoadingIconWrapper>
</StyledLoadingIconContainer>
);
};
@@ -1,11 +1,12 @@
import { useAgentChatContextOrThrow } from '@/ai/hooks/useAgentChatContextOrThrow';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { IconAlertCircle, IconRefresh } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledErrorContainer = styled.div`
align-items: center;
@@ -47,13 +48,14 @@ type AIChatErrorMessageProps = {
};
export const AIChatErrorMessage = ({ error }: AIChatErrorMessageProps) => {
const { theme } = useContext(ThemeContext);
const { handleRetry, isStreaming } = useAgentChatContextOrThrow();
return (
<StyledErrorContainer>
<StyledErrorIcon>
<IconAlertCircle size={theme.icon.size.md} />
<IconAlertCircle
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
/>
</StyledErrorIcon>
<StyledErrorContent>
<StyledErrorTitle>{t`Failed to get response`}</StyledErrorTitle>
@@ -100,14 +100,17 @@ const StyledEditorWrapper = styled.div`
}
`;
const StyledScrollWrapper = styled(ScrollWrapper)`
const StyledScrollWrapperContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
gap: ${themeCssVariables.spacing[2]};
overflow-y: auto;
padding: ${themeCssVariables.spacing[3]};
width: calc(100% - 24px) !important;
overflow: hidden;
> div {
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[3]};
width: calc(100% - 24px) !important;
}
`;
const StyledButtonsContainer = styled.div`
@@ -129,12 +132,14 @@ const StyledRightButtonsContainer = styled.div`
gap: ${themeCssVariables.spacing[1]};
`;
const StyledReadOnlyModelButton = styled(LightButton)`
cursor: default;
const StyledReadOnlyModelButtonContainer = styled.div`
& > button {
cursor: default;
&:hover,
&:active {
background: transparent;
&:hover,
&:active {
background: transparent;
}
}
`;
@@ -167,31 +172,32 @@ export const AIChatTab = () => {
{!isDraggingFile && (
<>
{hasMessages && (
<StyledScrollWrapper
componentInstanceId={AI_CHAT_SCROLL_WRAPPER_ID}
>
{messages.map((message, index) => {
const isLastMessage = index === messages.length - 1;
const isLastMessageStreaming = isStreaming && isLastMessage;
const isLastAssistantMessage =
isLastMessage && message.role === AgentMessageRole.ASSISTANT;
const shouldShowError = error && isLastAssistantMessage;
<StyledScrollWrapperContainer>
<ScrollWrapper componentInstanceId={AI_CHAT_SCROLL_WRAPPER_ID}>
{messages.map((message, index) => {
const isLastMessage = index === messages.length - 1;
const isLastMessageStreaming = isStreaming && isLastMessage;
const isLastAssistantMessage =
isLastMessage &&
message.role === AgentMessageRole.ASSISTANT;
const shouldShowError = error && isLastAssistantMessage;
return (
<AIChatMessage
isLastMessageStreaming={isLastMessageStreaming}
message={message}
key={message.id}
error={shouldShowError ? error : null}
/>
);
})}
{error &&
!isStreaming &&
messages.at(-1)?.role === AgentMessageRole.USER && (
<AIChatStandaloneError error={error} />
)}
</StyledScrollWrapper>
return (
<AIChatMessage
isLastMessageStreaming={isLastMessageStreaming}
message={message}
key={message.id}
error={shouldShowError ? error : null}
/>
);
})}
{error &&
!isStreaming &&
messages.at(-1)?.role === AgentMessageRole.USER && (
<AIChatStandaloneError error={error} />
)}
</ScrollWrapper>
</StyledScrollWrapperContainer>
)}
{!hasMessages && !error && !isLoading && (
<AIChatEmptyState editor={editor} />
@@ -213,10 +219,9 @@ export const AIChatTab = () => {
{hasMessages && <AIChatContextUsageButton />}
</StyledLeftButtonsContainer>
<StyledRightButtonsContainer>
<StyledReadOnlyModelButton
accent="tertiary"
title={smartModelLabel}
/>
<StyledReadOnlyModelButtonContainer>
<LightButton accent="tertiary" title={smartModelLabel} />
</StyledReadOnlyModelButtonContainer>
<SendMessageButton onSend={handleSendAndClear} />
</StyledRightButtonsContainer>
</StyledButtonsContainer>
@@ -1,10 +1,12 @@
import { useAIChatThreadClick } from '@/ai/hooks/useAIChatThreadClick';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { useLingui } from '@lingui/react/macro';
import { IconSparkles } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { type AgentChatThread } from '~/generated-metadata/graphql';
const StyledThreadsList = styled.div`
@@ -74,7 +76,6 @@ export const AIChatThreadGroup = ({
title: string;
}) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { handleThreadClick } = useAIChatThreadClick();
if (threads.length === 0) {
@@ -92,8 +93,10 @@ export const AIChatThreadGroup = ({
>
<StyledSparkleIcon>
<IconSparkles
size={theme.icon.size.md}
color={theme.color.blue}
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
color={resolveThemeVariable(themeCssVariables.color.blue)}
/>
</StyledSparkleIcon>
<StyledThreadContent>
@@ -1,6 +1,6 @@
import { TerminalOutput } from '@/ai/components/TerminalOutput';
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { useLingui } from '@lingui/react/macro';
import {
IconChevronDown,
@@ -15,8 +15,11 @@ import {
} from 'twenty-ui/display';
import { CodeEditor, LightIconButton } from 'twenty-ui/input';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
const StyledContainer = styled.div`
@@ -203,7 +206,6 @@ export const CodeExecutionDisplay = ({
isRunning = false,
}: CodeExecutionDisplayProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { copyToClipboard } = useCopyToClipboard();
const [isCodeExpanded, setIsCodeExpanded] = useState(false);
const [isOutputExpanded, setIsOutputExpanded] = useState(true);
@@ -235,12 +237,18 @@ export const CodeExecutionDisplay = ({
<StyledContainer>
<StyledHeader status={status}>
<StyledHeaderLeft>
<IconCode size={theme.icon.size.md} />
<IconCode
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
/>
<StyledTitle>{t`Python Code Execution`}</StyledTitle>
</StyledHeaderLeft>
<StyledHeaderRight>
<StyledStatusBadge status={status}>
<StatusIcon size={theme.icon.size.sm} />
<StatusIcon
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
{statusText}
</StyledStatusBadge>
</StyledHeaderRight>
@@ -249,7 +257,11 @@ export const CodeExecutionDisplay = ({
<StyledSection>
<StyledSectionHeader onClick={() => setIsCodeExpanded(!isCodeExpanded)}>
<StyledSectionHeaderLeft>
<IconCode size={theme.icon.size.sm} />
<IconCode
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
{t`Code`}
</StyledSectionHeaderLeft>
<StyledHeaderRight>
@@ -264,9 +276,17 @@ export const CodeExecutionDisplay = ({
accent="tertiary"
/>
{isCodeExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
)}
</StyledHeaderRight>
</StyledSectionHeader>
@@ -296,9 +316,17 @@ export const CodeExecutionDisplay = ({
>
<StyledSectionHeaderLeft>{t`Output`}</StyledSectionHeaderLeft>
{isOutputExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
)}
</StyledSectionHeader>
<AnimatedExpandableContainer
@@ -320,13 +348,25 @@ export const CodeExecutionDisplay = ({
onClick={() => setIsFilesExpanded(!isFilesExpanded)}
>
<StyledSectionHeaderLeft>
<IconFile size={theme.icon.size.sm} />
<IconFile
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
{t`Generated Files`} ({files.length})
</StyledSectionHeaderLeft>
{isFilesExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
)}
</StyledSectionHeader>
<AnimatedExpandableContainer
@@ -347,7 +387,12 @@ export const CodeExecutionDisplay = ({
loading="lazy"
/>
) : (
<IconFile size={48} color={theme.font.color.tertiary} />
<IconFile
size={48}
color={resolveThemeVariable(
themeCssVariables.font.color.tertiary,
)}
/>
)}
</StyledFilePreview>
<StyledFileInfo>
@@ -361,7 +406,11 @@ export const CodeExecutionDisplay = ({
target="_blank"
rel="noopener noreferrer"
>
<IconDownload size={theme.icon.size.sm} />
<IconDownload
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
</StyledDownloadLink>
</StyledFileInfo>
</StyledFileCard>
@@ -10,10 +10,13 @@ import {
StyledSkeletonContainer,
StyledTableScrollContainer,
} from '@/ai/components/LazyMarkdownRendererStyledComponents';
import { lazy, Suspense, useContext } from 'react';
import { lazy, Suspense } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const TextWithRecordLinks = ({ text }: { text: string }) => {
const parts: React.ReactNode[] = [];
@@ -131,13 +134,13 @@ const MarkdownRenderer = lazy(async () => {
});
const LoadingSkeleton = () => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={theme.border.radius.sm}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={resolveThemeVariable(themeCssVariables.border.radius.sm)}
>
<StyledSkeletonContainer>
<Skeleton
@@ -1,10 +1,12 @@
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { IconBrain, IconChevronDown, IconChevronUp } from 'twenty-ui/display';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { ShimmeringText } from '@/ai/components/ShimmeringText';
import { t } from '@lingui/core/macro';
@@ -67,7 +69,6 @@ export const ReasoningSummaryDisplay = ({
content: string;
isThinking?: boolean;
}) => {
const { theme } = useContext(ThemeContext);
const [isExpanded, setIsExpanded] = useState(false);
const hasContent = content.trim().length > 0;
@@ -82,7 +83,11 @@ export const ReasoningSummaryDisplay = ({
<>
<ShimmeringText>
<StyledIconContainer>
<IconBrain size={theme.icon.size.sm} />
<IconBrain
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<StyledThinkingText>{t`Thinking...`}</StyledThinkingText>
</StyledIconContainer>
</ShimmeringText>
@@ -96,13 +101,25 @@ export const ReasoningSummaryDisplay = ({
<>
<StyledToggleButton onClick={() => setIsExpanded(!isExpanded)}>
<StyledIconContainer>
<IconBrain size={theme.icon.size.sm} />
<IconBrain
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<span>{t`Finished thinking`}</span>
</StyledIconContainer>
{isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
)}
</StyledToggleButton>
@@ -1,11 +1,13 @@
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { IconChevronDown, IconChevronUp } from 'twenty-ui/display';
import { JsonTree } from 'twenty-ui/json-visualizer';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { useLingui } from '@lingui/react/macro';
import { type DataMessagePart } from 'twenty-shared/ai';
@@ -324,7 +326,6 @@ type RoutingDebugDisplayProps = {
export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { copyToClipboard } = useCopyToClipboard();
const [isExpanded, setIsExpanded] = useState(false);
const [activeTab, setActiveTab] = useState<TabType>('timing');
@@ -334,9 +335,13 @@ export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
<StyledToggleButton onClick={() => setIsExpanded(!isExpanded)}>
<StyledTimingLabel>{t`Debug Info`}</StyledTimingLabel>
{isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
)}
</StyledToggleButton>
@@ -1,12 +1,14 @@
import { RoutingDebugDisplay } from '@/ai/components/RoutingDebugDisplay';
import { ShimmeringText } from '@/ai/components/ShimmeringText';
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { type DataMessagePart } from 'twenty-shared/ai';
import { IconChevronDown, IconChevronUp, IconCpu } from 'twenty-ui/display';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
@@ -62,7 +64,6 @@ export const RoutingStatusDisplay = ({
}: {
data: DataMessagePart['routing-status'];
}) => {
const { theme } = useContext(ThemeContext);
const [isExpanded, setIsExpanded] = useState(false);
const isLoading = data.state === 'loading';
const isDebugMode = process.env.IS_DEBUG_MODE === 'true';
@@ -76,7 +77,9 @@ export const RoutingStatusDisplay = ({
return (
<StyledContainer>
<StyledIconTextContainer>
<IconCpu size={theme.icon.size.sm} />
<IconCpu
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
<ShimmeringText>
<StyledDisplayMessage>{data.text}</StyledDisplayMessage>
</ShimmeringText>
@@ -92,14 +95,24 @@ export const RoutingStatusDisplay = ({
isExpandable={!!isExpandable}
>
<StyledIconTextContainer>
<IconCpu size={theme.icon.size.sm} />
<IconCpu
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
<StyledDisplayMessage>{data.text}</StyledDisplayMessage>
</StyledIconTextContainer>
{isExpandable &&
(isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
))}
</StyledToggleButton>
@@ -140,7 +140,7 @@ const StyledReasoningText = styled.p`
white-space: pre-wrap;
`;
const StyledOrbitLoaderIcon = styled(ThinkingOrbitLoaderIcon)`
const StyledOrbitLoaderIconWrapper = styled.span`
color: ${themeCssVariables.font.color.tertiary};
`;
@@ -214,9 +214,11 @@ const StyledToolDetailsContainer = styled.div`
overflow: hidden;
`;
const StyledToolTabList = styled(TabList)`
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[1]};
const StyledToolTabListContainer = styled.div`
& > div {
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[1]};
}
`;
const StyledToolDetailsContent = styled.div`
@@ -334,11 +336,13 @@ const ThinkingToolStepRow = ({
<StyledToolErrorText>{part.errorText}</StyledToolErrorText>
) : (
<StyledToolDetailsContent>
<StyledToolTabList
tabs={toolTabs}
behaveAsLinks={false}
componentInstanceId={toolTabListComponentInstanceId}
/>
<StyledToolTabListContainer>
<TabList
tabs={toolTabs}
behaveAsLinks={false}
componentInstanceId={toolTabListComponentInstanceId}
/>
</StyledToolTabListContainer>
<StyledToolJsonContent>
<StyledJsonTreeContainer>
<JsonTree
@@ -388,7 +392,13 @@ const ThinkingStepRow = ({
return (
<StyledRow>
<StyledIconContainer>
{isActive ? <StyledOrbitLoaderIcon /> : <IconCpu size={14} />}
{isActive ? (
<StyledOrbitLoaderIconWrapper>
<ThinkingOrbitLoaderIcon />
</StyledOrbitLoaderIconWrapper>
) : (
<IconCpu size={14} />
)}
</StyledIconContainer>
<StyledRowLabelContainer>
<StyledRowLabel>{isActive ? t`Thinking` : t`Thought`}</StyledRowLabel>
@@ -1,11 +1,13 @@
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { IconChevronDown, IconChevronUp } from 'twenty-ui/display';
import { JsonTree } from 'twenty-ui/json-visualizer';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { CodeExecutionDisplay } from '@/ai/components/CodeExecutionDisplay';
import { ShimmeringText } from '@/ai/components/ShimmeringText';
@@ -138,7 +140,6 @@ export const ToolStepRenderer = ({
isStreaming: boolean;
}) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { copyToClipboard } = useCopyToClipboard();
const [isExpanded, setIsExpanded] = useState(false);
const [activeTab, setActiveTab] = useState<TabType>('output');
@@ -193,7 +194,11 @@ export const ToolStepRenderer = ({
<StyledToggleButton isExpandable={false}>
<StyledLeftContent>
<StyledIconTextContainer>
<ToolIcon size={theme.icon.size.sm} />
<ToolIcon
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
{isStreaming ? (
<ShimmeringText>
<StyledDisplayMessage>{displayText}</StyledDisplayMessage>
@@ -242,7 +247,11 @@ export const ToolStepRenderer = ({
>
<StyledLeftContent>
<StyledIconTextContainer>
<ToolIcon size={theme.icon.size.sm} />
<ToolIcon
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<StyledDisplayMessage>{displayMessage}</StyledDisplayMessage>
</StyledIconTextContainer>
</StyledLeftContent>
@@ -250,9 +259,17 @@ export const ToolStepRenderer = ({
<StyledToolName>{toolName}</StyledToolName>
{isExpandable &&
(isExpanded ? (
<IconChevronUp size={theme.icon.size.sm} />
<IconChevronUp
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
) : (
<IconChevronDown size={theme.icon.size.sm} />
<IconChevronDown
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
))}
</StyledRightContent>
</StyledToggleButton>
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme';
import { ColorSchemeProvider } from 'twenty-ui/theme-constants';
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
import { AIChatAssistantMessageRenderer } from '@/ai/components/AIChatAssistantMessageRenderer';
@@ -42,12 +42,12 @@ jest.mock('@/ai/components/CodeExecutionDisplay', () => ({
const renderAssistantRenderer = (messageParts: ExtendedUIMessagePart[]) => {
return render(
<ThemeContextProvider theme={THEME_LIGHT}>
<ColorSchemeProvider colorScheme="light">
<AIChatAssistantMessageRenderer
messageParts={messageParts}
isLastMessageStreaming={false}
/>
</ThemeContextProvider>,
</ColorSchemeProvider>,
);
};
@@ -1,6 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme';
import { ColorSchemeProvider } from 'twenty-ui/theme-constants';
import { ThinkingStepsDisplay } from '@/ai/components/ThinkingStepsDisplay';
import { type ThinkingStepPart } from '@/ai/utils/thinkingStepPart';
@@ -80,13 +80,13 @@ const renderThinkingStepsDisplay = ({
hasAssistantTextResponseStarted?: boolean;
}) => {
return render(
<ThemeContextProvider theme={THEME_LIGHT}>
<ColorSchemeProvider colorScheme="light">
<ThinkingStepsDisplay
parts={parts}
isLastMessageStreaming={isLastMessageStreaming}
hasAssistantTextResponseStarted={hasAssistantTextResponseStarted}
/>
</ThemeContextProvider>,
</ColorSchemeProvider>,
);
};
@@ -1,12 +1,14 @@
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { HorizontalSeparator } from 'twenty-ui/display';
import { ProgressBar } from 'twenty-ui/feedback';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
import { ContextUsageProgressRing } from '@/ai/components/internal/ContextUsageProgressRing';
import { SettingsBillingLabelValueItem } from '@/billing/components/internal/SettingsBillingLabelValueItem';
@@ -114,7 +116,6 @@ const getCachedLabel = (lastMessage: AgentChatLastMessageUsage): string => {
export const AIChatContextUsageButton = () => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const [isHovered, setIsHovered] = useState(false);
const agentChatUsage = useAtomStateValue(agentChatUsageState);
@@ -165,19 +166,26 @@ export const AIChatContextUsageButton = () => {
value={percentage}
barColor={
percentage > 80
? theme.color.red
? resolveThemeVariable(themeCssVariables.color.red)
: percentage > 60
? theme.color.orange
: theme.color.blue
? resolveThemeVariable(themeCssVariables.color.orange)
: resolveThemeVariable(themeCssVariables.color.blue)
}
backgroundColor={theme.background.tertiary}
backgroundColor={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
withBorderRadius
/>
</StyledSection>
{isDefined(lastMessage) && (
<>
<HorizontalSeparator noMargin color={theme.background.tertiary} />
<HorizontalSeparator
noMargin
color={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
/>
<StyledSection>
<StyledSectionTitle>{t`Last message`}</StyledSectionTitle>
<SettingsBillingLabelValueItem
@@ -196,7 +204,10 @@ export const AIChatContextUsageButton = () => {
</>
)}
<HorizontalSeparator noMargin color={theme.background.tertiary} />
<HorizontalSeparator
noMargin
color={resolveThemeVariable(themeCssVariables.background.tertiary)}
/>
<StyledSection>
<StyledSectionTitle>{t`Conversation`}</StyledSectionTitle>
<SettingsBillingLabelValueItem
@@ -1,8 +1,9 @@
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledSkeletonContainer = styled.div`
display: flex;
@@ -25,12 +26,12 @@ const StyledMessageSkeleton = styled.div`
const NUMBER_OF_SKELETONS = 6;
export const AIChatSkeletonLoader = () => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={4}
>
<StyledSkeletonContainer>
@@ -7,13 +7,16 @@ import { filePreviewState } from '@/ui/field/display/states/filePreviewState';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useCallback, useContext } from 'react';
import { useCallback } from 'react';
import { type ExtendedFileUIPart } from 'twenty-shared/ai';
import { isDefined } from 'twenty-shared/utils';
import { AvatarOrIcon, Chip, ChipVariant } from 'twenty-ui/components';
import { type IconComponent, IconX } from 'twenty-ui/display';
import { Loader } from 'twenty-ui/feedback';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledClickableContainer = styled.div<{ clickable: boolean }>`
cursor: ${({ clickable }: { clickable: boolean }) =>
@@ -31,7 +34,6 @@ export const AgentChatFilePreview = ({
onRemove?: () => void;
isUploading?: boolean;
}) => {
const { theme } = useContext(ThemeContext);
const iconColors: Record<AttachmentFileCategory, string> =
useFileCategoryColors();
const setFilePreview = useSetAtomState(filePreviewState);
@@ -74,7 +76,7 @@ export const AgentChatFilePreview = ({
const rightComponent = onRemove ? (
<AvatarOrIcon
Icon={IconX}
IconColor={theme.font.color.secondary}
IconColor={resolveThemeVariable(themeCssVariables.font.color.secondary)}
onClick={onRemove}
/>
) : undefined;
@@ -1,7 +1,8 @@
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
type ContextUsageProgressRingProps = {
percentage: number;
@@ -32,8 +33,6 @@ export const ContextUsageProgressRing = ({
size = 16,
strokeWidth = 2,
}: ContextUsageProgressRingProps) => {
const { theme } = useContext(ThemeContext);
const normalizedPercentage = Math.min(Math.max(percentage, 0), 100);
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
@@ -42,10 +41,10 @@ export const ContextUsageProgressRing = ({
const progressColor =
normalizedPercentage > 80
? theme.color.red
? resolveThemeVariable(themeCssVariables.color.red)
: normalizedPercentage > 60
? theme.color.orange
: theme.color.blue;
? resolveThemeVariable(themeCssVariables.color.orange)
: resolveThemeVariable(themeCssVariables.color.blue);
return (
<StyledSvg width={size} height={size}>
@@ -29,7 +29,7 @@ const StyledTitle = styled.div`
padding: 0 ${themeCssVariables.spacing[2]};
`;
const StyledSuggestedPromptButton = styled(LightButton)`
const StyledSuggestedPromptButtonContainer = styled.div`
align-self: flex-start;
`;
@@ -62,13 +62,14 @@ export const AIChatSuggestedPrompts = ({
<StyledContainer>
<StyledTitle>{t`What can I help you with?`}</StyledTitle>
{DEFAULT_SUGGESTED_PROMPTS.map((prompt) => (
<StyledSuggestedPromptButton
key={prompt.id}
Icon={prompt.Icon}
title={resolveMessage(prompt.label)}
accent="secondary"
onClick={() => handleClick(prompt)}
/>
<StyledSuggestedPromptButtonContainer key={prompt.id}>
<LightButton
Icon={prompt.Icon}
title={resolveMessage(prompt.label)}
accent="secondary"
onClick={() => handleClick(prompt)}
/>
</StyledSuggestedPromptButtonContainer>
))}
</StyledContainer>
);
@@ -23,15 +23,16 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/consta
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { isNonEmptyString } from '@sniptt/guards';
import { motion } from 'framer-motion';
import { useContext } from 'react';
import {
Avatar,
HorizontalSeparator,
IconChevronRight,
IconPlus,
} from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { type AvailableWorkspace } from '~/generated-metadata/graphql';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
@@ -139,7 +140,6 @@ export const SignInUpGlobalScopeForm = () => {
const signInUpStep = useAtomStateValue(signInUpStepState);
const { buildWorkspaceUrl } = useBuildWorkspaceUrl();
const { signOut } = useAuth();
const { theme } = useContext(ThemeContext);
const { createWorkspace } = useSignUpInNewWorkspace();
const availableWorkspaces = useAtomStateValue(availableWorkspacesState);
@@ -201,7 +201,11 @@ export const SignInUpGlobalScopeForm = () => {
</StyledWorkspaceUrl>
</StyledWorkspaceTextContainer>
<StyledChevronIcon>
<IconChevronRight size={theme.icon.size.md} />
<IconChevronRight
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
</StyledChevronIcon>
</StyledWorkspaceContent>
</StyledWorkspaceItem>
@@ -210,13 +214,21 @@ export const SignInUpGlobalScopeForm = () => {
<StyledWorkspaceItem onClick={() => createWorkspace()}>
<StyledWorkspaceContent>
<StyledWorkspaceLogo>
<IconPlus size={theme.icon.size.lg} />
<IconPlus
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.lg,
)}
/>
</StyledWorkspaceLogo>
<StyledWorkspaceTextContainer>
<StyledWorkspaceName>{t`Create a workspace`}</StyledWorkspaceName>
</StyledWorkspaceTextContainer>
<StyledChevronIcon>
<IconChevronRight size={theme.icon.size.md} />
<IconChevronRight
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
</StyledChevronIcon>
</StyledWorkspaceContent>
</StyledWorkspaceItem>
@@ -3,11 +3,7 @@ import { I18nProvider } from '@lingui/react';
import { fireEvent, render, screen } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
import {
THEME_LIGHT,
ThemeContextProvider,
ThemeProvider,
} from 'twenty-ui/theme';
import { ColorSchemeProvider } from 'twenty-ui/theme-constants';
import { SignInUpGlobalScopeForm } from '@/auth/sign-in-up/components/SignInUpGlobalScopeForm';
import {
@@ -98,13 +94,11 @@ describe('SignInUpGlobalScopeForm', () => {
render(
<JotaiProvider store={jotaiStore}>
<ThemeProvider theme={THEME_LIGHT}>
<ThemeContextProvider theme={THEME_LIGHT}>
<I18nProvider i18n={i18n}>
<SignInUpGlobalScopeForm />
</I18nProvider>
</ThemeContextProvider>
</ThemeProvider>
<ColorSchemeProvider colorScheme="light">
<I18nProvider i18n={i18n}>
<SignInUpGlobalScopeForm />
</I18nProvider>
</ColorSchemeProvider>
</JotaiProvider>,
);
@@ -4,11 +4,12 @@ import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { motion } from 'framer-motion';
import { useContext } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { StyledText } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledFullWidthMotionDiv = styled(motion.div)`
width: 100%;
@@ -26,7 +27,6 @@ export const SignInUpPasswordField = ({
signInUpMode: SignInUpMode;
}) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const form = useFormContext<Form>();
return (
@@ -61,7 +61,9 @@ export const SignInUpPasswordField = ({
{signInUpMode === SignInUpMode.SignUp && (
<StyledText
text={t`At least 8 characters long.`}
color={theme.font.color.secondary}
color={resolveThemeVariable(
themeCssVariables.font.color.secondary,
)}
/>
)}
</StyledInputContainer>
@@ -7,14 +7,15 @@ import {
import { extractSecretFromOtpUri } from '@/settings/two-factor-authentication/utils/extractSecretFromOtpUri';
import { styled } from '@linaria/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import QRCode from 'react-qr-code';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { IconCopy } from 'twenty-ui/display';
import { Loader } from 'twenty-ui/feedback';
import { MainButton } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
@@ -70,7 +71,6 @@ const StyledCopySetupKeyLink = styled.button`
export const SignInUpTwoFactorAuthenticationProvision = () => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { copyToClipboard } = useCopyToClipboard();
const qrCode = useAtomStateValue(qrCodeState);
const setSignInUpStep = useSetAtomState(signInUpStepState);
@@ -102,7 +102,11 @@ export const SignInUpTwoFactorAuthenticationProvision = () => {
{!qrCode ? <Loader /> : <QRCode value={qrCode} />}
{qrCode && (
<StyledCopySetupKeyLink onClick={handleCopySetupKey}>
<IconCopy size={theme.icon.size.sm} />
<IconCopy
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>
<Trans>Copy Setup Key</Trans>
</StyledCopySetupKeyLink>
)}
@@ -8,18 +8,23 @@ import {
import { AuthenticatedMethod } from '@/auth/types/AuthenticatedMethod.enum';
import { type SocialSSOSignInUpActionType } from '@/auth/types/socialSSOSignInUp.type';
import { useLingui } from '@lingui/react/macro';
import { memo, useContext } from 'react';
import { memo } from 'react';
import { HorizontalSeparator, IconGoogle } from 'twenty-ui/display';
import { MainButton } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { LastUsedPill } from './LastUsedPill';
import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const GoogleIcon = memo(() => {
const { theme } = useContext(ThemeContext);
return <IconGoogle size={theme.icon.size.md} />;
return (
<IconGoogle
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
/>
);
});
export const SignInUpWithGoogle = ({
@@ -8,14 +8,16 @@ import {
import { AuthenticatedMethod } from '@/auth/types/AuthenticatedMethod.enum';
import { type SocialSSOSignInUpActionType } from '@/auth/types/socialSSOSignInUp.type';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import { HorizontalSeparator, IconMicrosoft } from 'twenty-ui/display';
import { MainButton } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { LastUsedPill } from './LastUsedPill';
import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const SignInUpWithMicrosoft = ({
action,
@@ -24,7 +26,6 @@ export const SignInUpWithMicrosoft = ({
action: SocialSSOSignInUpActionType;
isGlobalScope?: boolean;
}) => {
const { theme } = useContext(ThemeContext);
const { t } = useLingui();
const signInUpStep = useAtomStateValue(signInUpStepState);
@@ -45,7 +46,13 @@ export const SignInUpWithMicrosoft = ({
<>
<StyledSSOButtonContainer>
<MainButton
Icon={() => <IconMicrosoft size={theme.icon.size.md} />}
Icon={() => (
<IconMicrosoft
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
)}
title={t`Continue with Microsoft`}
onClick={handleClick}
variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
@@ -11,16 +11,17 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { HorizontalSeparator, IconLock } from 'twenty-ui/display';
import { MainButton } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { LastUsedPill } from './LastUsedPill';
import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const SignInUpWithSSO = () => {
const { theme } = useContext(ThemeContext);
const { t } = useLingui();
const setSignInUpStep = useSetAtomState(signInUpStepState);
const workspaceAuthProviders = useAtomStateValue(workspaceAuthProvidersState);
@@ -50,7 +51,13 @@ export const SignInUpWithSSO = () => {
<>
<StyledSSOButtonContainer>
<MainButton
Icon={() => <IconLock size={theme.icon.size.md} />}
Icon={() => (
<IconLock
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.md,
)}
/>
)}
title={t`Single sign-on (SSO)`}
onClick={signInWithSSO}
variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
@@ -9,12 +9,14 @@ import { useGetWorkflowNodeExecutionUsage } from '@/billing/hooks/useGetWorkflow
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { formatToShortNumber } from 'twenty-shared/utils';
import { H2Title, HorizontalSeparator } from 'twenty-ui/display';
import { ProgressBar } from 'twenty-ui/feedback';
import { Section } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
import { SubscriptionStatus } from '~/generated-metadata/graphql';
export const SettingsBillingCreditsSection = ({
@@ -60,8 +62,6 @@ export const SettingsBillingCreditsSection = ({
currentBillingSubscription.interval,
);
const { theme } = useContext(ThemeContext);
return (
<>
<Section>
@@ -77,15 +77,24 @@ export const SettingsBillingCreditsSection = ({
<ProgressBar
value={displayedProgressBarValue}
barColor={
progressBarValue > 100 ? theme.color.red8 : theme.color.blue
progressBarValue > 100
? resolveThemeVariable(themeCssVariables.color.red8)
: resolveThemeVariable(themeCssVariables.color.blue)
}
backgroundColor={theme.background.tertiary}
backgroundColor={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
withBorderRadius={true}
/>
{!isTrialing && (
<>
<HorizontalSeparator noMargin color={theme.background.tertiary} />
<HorizontalSeparator
noMargin
color={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
/>
<SettingsBillingLabelValueItem
label={t`Base Credits`}
value={formatNumber(grantedCredits, {
@@ -111,7 +120,12 @@ export const SettingsBillingCreditsSection = ({
})}
/>
)}
<HorizontalSeparator noMargin color={theme.background.tertiary} />
<HorizontalSeparator
noMargin
color={resolveThemeVariable(
themeCssVariables.background.tertiary,
)}
/>
<SettingsBillingLabelValueItem
label={t`Extra Credits Used`}
value={`${formatToShortNumber(extraCreditsUsed)}`}
@@ -1,8 +1,10 @@
import { styled } from '@linaria/react';
import React, { useContext } from 'react';
import React from 'react';
import { IconCheck } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledBenefitContainer = styled.div`
color: ${themeCssVariables.font.color.secondary};
@@ -24,11 +26,13 @@ type SubscriptionBenefitProps = {
children: React.ReactNode;
};
export const SubscriptionBenefit = ({ children }: SubscriptionBenefitProps) => {
const { theme } = useContext(ThemeContext);
return (
<StyledBenefitContainer>
<StyledCheckContainer>
<IconCheck color={theme.grayScale.gray11} size={14} />
<IconCheck
color={resolveThemeVariable(themeCssVariables.grayScale.gray11)}
size={14}
/>
</StyledCheckContainer>
{children}
</StyledBenefitContainer>
@@ -31,11 +31,11 @@ const StyledRow = styled.div`
gap: ${themeCssVariables.spacing[2]};
`;
const StyledSelect = styled(Select<string>)`
const StyledSelectWrapper = styled.div`
flex: 1 1;
`;
const StyledButton = styled(Button)`
const StyledButtonContainer = styled.div`
flex: 0 0 auto;
`;
@@ -160,26 +160,30 @@ export const MeteredPriceSelector = ({
description={t`Number of new credits allocated every ${recurringInterval}`}
/>
<StyledRow>
<StyledSelect
dropdownId="settings_billing-metered-price"
options={options}
value={selectedPriceId ?? currentMeteredPrice.stripePriceId}
onChange={handleChange}
disabled={isUpdating || isTrialing}
description={
isTrialing ? t`Please start your subscription first` : undefined
}
fullWidth
/>
{isChanged && (
<StyledButton
title={isUpgrade() ? t`Upgrade` : t`Downgrade`}
onClick={handleOpenConfirm}
variant="primary"
isLoading={isUpdating}
disabled={!isChanged}
accent={isUpgrade() ? 'blue' : 'danger'}
<StyledSelectWrapper>
<Select<string>
dropdownId="settings_billing-metered-price"
options={options}
value={selectedPriceId ?? currentMeteredPrice.stripePriceId}
onChange={handleChange}
disabled={isUpdating || isTrialing}
description={
isTrialing ? t`Please start your subscription first` : undefined
}
fullWidth
/>
</StyledSelectWrapper>
{isChanged && (
<StyledButtonContainer>
<Button
title={isUpgrade() ? t`Upgrade` : t`Downgrade`}
onClick={handleOpenConfirm}
variant="primary"
isLoading={isUpdating}
disabled={!isChanged}
accent={isUpgrade() ? 'blue' : 'danger'}
/>
</StyledButtonContainer>
)}
</StyledRow>
<ConfirmationModal
@@ -1,9 +1,11 @@
import { type IconComponent } from 'twenty-ui/display';
import React, { useContext } from 'react';
import React from 'react';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type SubscriptionInfoRowContainerProps = {
Icon: IconComponent;
@@ -55,11 +57,12 @@ export const SubscriptionInfoRowContainer = ({
currentValue,
nextValue,
}: SubscriptionInfoRowContainerProps) => {
const { theme } = useContext(ThemeContext);
return (
<StyledContainer>
<StyledIconLabelContainer>
<Icon size={theme.icon.size.md} />
<Icon
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
/>
<StyledLabelContainer>{label}</StyledLabelContainer>
</StyledIconLabelContainer>
{currentValue}
@@ -11,16 +11,20 @@ import { Chip, ChipVariant } from 'twenty-ui/components';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
const StyledRecordChip = styled(RecordChip)`
height: auto;
margin: 0;
padding: 0 ${themeCssVariables.spacing[1]};
const StyledRecordChipContainer = styled.span`
> * {
height: auto;
margin: 0;
padding: 0 ${themeCssVariables.spacing[1]};
}
`;
const StyledInlineMentionRecordChip = styled(MentionRecordChip)`
height: auto;
margin: 0;
padding: 0 ${themeCssVariables.spacing[1]};
const StyledMentionRecordChipContainer = styled.span`
> * {
height: auto;
margin: 0;
padding: 0 ${themeCssVariables.spacing[1]};
}
`;
const LegacyMentionRenderer = ({
@@ -69,11 +73,13 @@ const LegacyMentionRenderer = ({
}
return (
<StyledRecordChip
objectNameSingular={objectMetadataItem.nameSingular}
record={record}
forceDisableClick={false}
/>
<StyledRecordChipContainer>
<RecordChip
objectNameSingular={objectMetadataItem.nameSingular}
record={record}
forceDisableClick={false}
/>
</StyledRecordChipContainer>
);
};
@@ -112,12 +118,14 @@ export const MentionInlineContent = createReactInlineContentSpec(
// New notes store objectNameSingular + label + imageUrl directly
if (isNonEmptyString(objectNameSingular) && isNonEmptyString(label)) {
return (
<StyledInlineMentionRecordChip
recordId={recordId}
objectNameSingular={objectNameSingular}
label={label}
imageUrl={imageUrl}
/>
<StyledMentionRecordChipContainer>
<MentionRecordChip
recordId={recordId}
objectNameSingular={objectNameSingular}
label={label}
imageUrl={imageUrl}
/>
</StyledMentionRecordChipContainer>
);
}
@@ -3,9 +3,6 @@ import { BlockNoteView } from '@blocknote/mantine';
import { SuggestionMenuController } from '@blocknote/react';
import { styled } from '@linaria/react';
import { type ClipboardEvent, useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
import { getSlashMenu } from '@/blocknote-editor/utils/getSlashMenu';
import { CustomMentionMenu } from '@/blocknote-editor/components/CustomMentionMenu';
@@ -15,6 +12,10 @@ import {
type SuggestionItem,
} from '@/blocknote-editor/components/CustomSlashMenu';
import { useMentionMenu } from '@/mention/hooks/useMentionMenu';
import {
ColorSchemeContext,
themeCssVariables,
} from 'twenty-ui/theme-constants';
interface BlockEditorProps {
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
@@ -142,8 +143,9 @@ export const BlockEditor = ({
onPaste,
readonly,
}: BlockEditorProps) => {
const { theme } = useContext(ThemeContext);
const blockNoteTheme = theme.name === 'light' ? 'light' : 'dark';
const { colorScheme } = useContext(ColorSchemeContext);
const blockNoteTheme = colorScheme === 'light' ? 'light' : 'dark';
const getMentionItems = useMentionMenu(editor);
const handleFocus = () => {
@@ -1,8 +1,9 @@
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { type IconComponent } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ Variant: Variants }>`
color: ${({ Variant }) =>
@@ -28,12 +29,11 @@ export const CustomSideMenuOptions = ({
Variant,
text,
}: CustomSideMenuOptionsProps) => {
const { theme } = useContext(ThemeContext);
return (
<StyledContainer Variant={Variant}>
<LeftIcon
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
stroke={resolveThemeVariableAsNumber(themeCssVariables.icon.stroke.sm)}
></LeftIcon>
<StyledTextContainer>{text}</StyledTextContainer>
</StyledContainer>
@@ -3,7 +3,7 @@ import React from 'react';
import { Label } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledGroupHeading = styled(Label)`
const StyledGroupHeadingContainer = styled.div`
align-items: center;
padding-bottom: ${themeCssVariables.spacing[1]};
padding-left: ${themeCssVariables.spacing[1]};
@@ -29,7 +29,9 @@ export const CommandGroup = ({ heading, children }: CommandGroupProps) => {
}
return (
<>
<StyledGroupHeading>{heading}</StyledGroupHeading>
<StyledGroupHeadingContainer>
<Label>{heading}</Label>
</StyledGroupHeadingContainer>
<StyledGroup>{children}</StyledGroup>
</>
);
@@ -19,7 +19,7 @@ const StyledNavigationIcon = styled.div`
justify-content: center;
`;
const StyledIconChevronLeft = styled(IconChevronLeft)`
const StyledIconButtonContainer = styled.span`
color: ${themeCssVariables.font.color.secondary};
`;
@@ -52,12 +52,14 @@ export const CommandMenuBackButton = () => {
<Dropdown
clickableComponent={
<StyledNavigationIcon onContextMenu={handleBackButtonContextMenu}>
<IconButton
Icon={StyledIconChevronLeft}
size="small"
variant="tertiary"
onClick={goBackFromCommandMenu}
/>
<StyledIconButtonContainer>
<IconButton
Icon={IconChevronLeft}
size="small"
variant="tertiary"
onClick={goBackFromCommandMenu}
/>
</StyledIconButtonContainer>
</StyledNavigationIcon>
}
dropdownComponents={
@@ -4,10 +4,10 @@ import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { styled } from '@linaria/react';
import { Avatar } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledIconWrapper = styled.div<{ withIconBackground?: boolean }>`
background: ${({ withIconBackground }) =>
withIconBackground ? themeCssVariables.background.primary : 'unset'};
@@ -39,15 +39,15 @@ export const CommandMenuContextRecordChipAvatars = ({
const { Icon, IconColor } = useGetStandardObjectIcon(
objectMetadataItem.nameSingular,
);
const { theme } = useContext(ThemeContext);
return (
<StyledIconWrapper
withIconBackground={recordChipData.avatarType !== 'rounded'}
>
{Icon ? (
<Icon color={IconColor} size={theme.icon.size.sm} />
<Icon
color={IconColor}
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.sm)}
/>
) : (
<Avatar
avatarUrl={recordChipData.avatarUrl}
@@ -25,7 +25,7 @@ type CommandMenuItemNumberInputProps = {
placeholder?: string;
};
const StyledRightAlignedTextInput = styled(TextInput)`
const StyledRightAlignedTextInputContainer = styled.div`
input {
text-align: right;
}
@@ -132,18 +132,20 @@ export const CommandMenuItemNumberInput = ({
Icon={Icon}
onClick={focusInput}
RightComponent={
<StyledRightAlignedTextInput
ref={inputRef}
value={draftValue}
sizeVariant="sm"
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder}
error={hasError ? ' ' : undefined}
noErrorHelper
textClickOutsideId={focusId}
/>
<StyledRightAlignedTextInputContainer>
<TextInput
ref={inputRef}
value={draftValue}
sizeVariant="sm"
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder}
error={hasError ? ' ' : undefined}
noErrorHelper
textClickOutsideId={focusId}
/>
</StyledRightAlignedTextInputContainer>
}
/>
);
@@ -19,7 +19,7 @@ type CommandMenuItemTextInputProps = {
placeholder?: string;
};
const StyledRightAlignedTextInput = styled(TextInput)`
const StyledRightAlignedTextInputContainer = styled.div`
input {
text-align: right;
}
@@ -96,16 +96,18 @@ export const CommandMenuItemTextInput = ({
Icon={Icon}
onClick={focusInput}
RightComponent={
<StyledRightAlignedTextInput
ref={inputRef}
value={draftValue}
sizeVariant="sm"
onChange={setDraftValue}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder}
textClickOutsideId={focusId}
/>
<StyledRightAlignedTextInputContainer>
<TextInput
ref={inputRef}
value={draftValue}
sizeVariant="sm"
onChange={setDraftValue}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder}
textClickOutsideId={focusId}
/>
</StyledRightAlignedTextInputContainer>
}
/>
);
@@ -4,8 +4,11 @@ import { getActionLabel } from '@/action-menu/utils/getActionLabel';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type CommandMenuMultipleRecordsInfoProps = {
commandMenuPageInstanceId: string;
@@ -14,8 +17,6 @@ type CommandMenuMultipleRecordsInfoProps = {
export const CommandMenuMultipleRecordsInfo = ({
commandMenuPageInstanceId,
}: CommandMenuMultipleRecordsInfoProps) => {
const { theme } = useContext(ThemeContext);
const { totalCount } = useFindManyRecordsSelectedInContextStore({
instanceId: commandMenuPageInstanceId,
limit: 1,
@@ -26,8 +27,15 @@ export const CommandMenuMultipleRecordsInfo = ({
return (
<CommandMenuPageInfoLayout
icon={<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />}
iconColor={theme.font.color.tertiary}
icon={
<Icon
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
stroke={resolveThemeVariableAsNumber(
themeCssVariables.icon.stroke.sm,
)}
/>
}
iconColor={resolveThemeVariable(themeCssVariables.font.color.tertiary)}
title={getActionLabel(label)}
label={t`${totalCount} selected`}
/>
@@ -20,12 +20,13 @@ import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workf
import { styled } from '@linaria/react';
import { motion } from 'framer-motion';
import { useCallback, useContext, useRef } from 'react';
import { useCallback, useRef } from 'react';
import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components';
import { useIsMobile } from 'twenty-ui/utilities';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const StyledCommandMenuBase = styled.div`
background: ${themeCssVariables.background.primary};
border-left: 1px solid ${themeCssVariables.border.color.medium};
@@ -51,9 +52,6 @@ export const CommandMenuOpenContainer = ({
const targetVariantForAnimation: CommandMenuAnimationVariant = isMobile
? 'fullScreen'
: 'normal';
const { theme } = useContext(ThemeContext);
const { closeCommandMenu } = useCommandMenu();
const commandMenuRef = useRef<HTMLDivElement>(null);
@@ -100,7 +98,11 @@ export const CommandMenuOpenContainer = ({
initial="closed"
exit="closed"
variants={COMMAND_MENU_ANIMATION_VARIANTS}
transition={{ duration: theme.animation.duration.normal }}
transition={{
duration: resolveThemeVariable(
themeCssVariables.animation.duration.normal,
),
}}
onAnimationStart={() => setIsSidePanelAnimating(true)}
onAnimationComplete={() => setIsSidePanelAnimating(false)}
>
@@ -21,10 +21,11 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { CommandMenuPages } from 'twenty-shared/types';
import { type CommandMenuContextChipProps } from './CommandMenuContextChip';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledPageTitle = styled.div`
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.sm};
@@ -36,7 +37,6 @@ type CommandMenuPageInfoProps = {
};
export const CommandMenuPageInfo = ({ pageChip }: CommandMenuPageInfoProps) => {
const { theme } = useContext(ThemeContext);
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
selectedNavigationMenuItemInEditModeState,
);
@@ -134,8 +134,8 @@ export const CommandMenuPageInfo = ({ pageChip }: CommandMenuPageInfoProps) => {
<CommandMenuPageInfoLayout
icon={
<IconColumnInsertRight
size={theme.icon.size.md}
color={theme.font.color.tertiary}
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
color={resolveThemeVariable(themeCssVariables.font.color.tertiary)}
/>
}
title={<OverflowingTextWithTooltip text={pageChip.text ?? ''} />}
@@ -13,19 +13,20 @@ import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomC
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { isNonEmptyString } from '@sniptt/guards';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useContext, useState } from 'react';
import { useState } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const CommandMenuPageLayoutInfoContent = ({
pageLayoutId,
}: {
pageLayoutId: string;
}) => {
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const commandMenuPage = useAtomStateValue(commandMenuPageState);
const commandMenuPageInfo = useAtomStateValue(commandMenuPageInfoState);
@@ -121,7 +122,12 @@ export const CommandMenuPageLayoutInfoContent = ({
<CommandMenuPageInfoLayout
icon={
isDefined(headerIcon) ? (
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
<Icon
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
stroke={resolveThemeVariableAsNumber(
themeCssVariables.icon.stroke.sm,
)}
/>
) : undefined
}
iconColor={headerIconColor}
@@ -9,8 +9,10 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { styled } from '@linaria/react';
import { motion } from 'framer-motion';
import { isDefined } from 'twenty-shared/utils';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledCommandMenuContent = styled.div`
flex: 1;
@@ -26,9 +28,6 @@ export const CommandMenuRouter = () => {
) : (
<></>
);
const { theme } = useContext(ThemeContext);
return (
<CommandMenuContainer>
<CommandMenuPageComponentInstanceContext.Provider
@@ -39,7 +38,9 @@ export const CommandMenuRouter = () => {
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
duration: theme.animation.duration.instant,
duration: resolveThemeVariable(
themeCssVariables.animation.duration.instant,
),
delay: 0.1,
}}
>
@@ -18,13 +18,15 @@ import { useLingui } from '@lingui/react/macro';
import { AnimatePresence, motion } from 'framer-motion';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useContext, useRef } from 'react';
import { useRef } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconX } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { useIsMobile } from 'twenty-ui/utilities';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const StyledInputContainer = styled.div<{ isMobile: boolean }>`
align-items: center;
@@ -101,8 +103,6 @@ export const CommandMenuTopBar = () => {
commandMenuNavigationStackState,
);
const { theme } = useContext(ThemeContext);
const { contextChips } = useCommandMenuContextChips();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
@@ -145,7 +145,9 @@ export const CommandMenuTopBar = () => {
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: theme.animation.duration.instant,
duration: resolveThemeVariableAsNumber(
themeCssVariables.animation.duration.instant,
),
}}
>
<CommandMenuBackButton />
@@ -155,7 +157,9 @@ export const CommandMenuTopBar = () => {
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: theme.animation.duration.instant,
duration: resolveThemeVariableAsNumber(
themeCssVariables.animation.duration.instant,
),
}}
>
<IconButton
@@ -13,7 +13,7 @@ import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { useCreateNewAIChatThread } from '@/ai/hooks/useCreateNewAIChatThread';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledIconButton = styled(IconButton)`
const StyledIconButtonContainer = styled.span`
color: ${themeCssVariables.font.color.secondary};
`;
@@ -35,22 +35,26 @@ export const CommandMenuTopBarRightCornerIcon = () => {
if (!isOnAskAIPage) {
return (
<StyledIconButton
onClick={() => openAskAIPage({ resetNavigationStack: false })}
Icon={IconSparkles}
variant="tertiary"
size="small"
/>
<StyledIconButtonContainer>
<IconButton
onClick={() => openAskAIPage({ resetNavigationStack: false })}
Icon={IconSparkles}
variant="tertiary"
size="small"
/>
</StyledIconButtonContainer>
);
}
return (
<StyledIconButton
Icon={IconEdit}
size="small"
variant="tertiary"
onClick={() => createChatThread()}
ariaLabel={t`New conversation`}
/>
<StyledIconButtonContainer>
<IconButton
Icon={IconEdit}
size="small"
variant="tertiary"
onClick={() => createChatThread()}
ariaLabel={t`New conversation`}
/>
</StyledIconButtonContainer>
);
};
@@ -23,8 +23,11 @@ import { CommandMenuPages, CoreObjectNameSingular } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { useIcons } from 'twenty-ui/display';
import { ICON_SIZES, ICON_STROKES } from 'twenty-ui/theme-constants';
import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout';
import {
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const CommandMenuWorkflowStepInfo = ({
commandMenuPageInstanceId,
@@ -169,7 +172,12 @@ export const CommandMenuWorkflowStepInfo = ({
<CommandMenuPageInfoLayout
icon={
headerIcon ? (
<Icon size={ICON_SIZES.md} stroke={ICON_STROKES.sm} />
<Icon
size={resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)}
stroke={resolveThemeVariableAsNumber(
themeCssVariables.icon.stroke.sm,
)}
/>
) : undefined
}
iconColor={headerIconColor}
@@ -13,8 +13,10 @@ import {
IconPlus,
type IconComponent,
} from 'twenty-ui/display';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type PageLayoutHeaderInfo = {
headerIcon: IconComponent | undefined;
@@ -43,8 +45,7 @@ export const usePageLayoutHeaderInfo = ({
openTabId,
editedTitle,
}: UsePageLayoutHeaderInfoParams): PageLayoutHeaderInfo | null => {
const { theme } = useContext(ThemeContext);
const iconColor = theme.font.color.tertiary;
const iconColor = resolveThemeVariable(themeCssVariables.font.color.tertiary);
switch (commandMenuPage) {
case CommandMenuPages.PageLayoutTabSettings: {
@@ -1,4 +1,8 @@
import { THEME_COMMON } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const COMMAND_MENU_ANIMATION_VARIANTS = {
fullScreen: {
x: '0%',
@@ -9,14 +13,14 @@ export const COMMAND_MENU_ANIMATION_VARIANTS = {
},
normal: {
x: '0%',
width: THEME_COMMON.rightDrawerWidth,
width: resolveThemeVariable(themeCssVariables.rightDrawerWidth),
height: '100%',
bottom: '0',
top: '0',
},
closed: {
x: '100%',
width: THEME_COMMON.rightDrawerWidth,
width: resolveThemeVariable(themeCssVariables.rightDrawerWidth),
height: '100%',
bottom: '0',
top: 'auto',
@@ -9,10 +9,14 @@ import { recordStoreIdentifiersFamilySelector } from '@/object-record/record-sto
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useContext, useMemo } from 'react';
import { useMemo } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
resolveThemeVariableAsNumber,
themeCssVariables,
} from 'twenty-ui/theme-constants';
export const useCommandMenuContextChips = () => {
const commandMenuNavigationStack = useAtomStateValue(
@@ -27,8 +31,6 @@ export const useCommandMenuContextChips = () => {
const { navigateCommandMenuHistory } = useCommandMenuHistory();
const { theme } = useContext(ThemeContext);
const commandMenuNavigationMorphItemsByPage = useAtomStateValue(
commandMenuNavigationMorphItemsByPageState,
);
@@ -111,16 +113,26 @@ export const useCommandMenuContextChips = () => {
return {
page,
Icons: isLastChip
? [<page.pageIcon size={theme.icon.size.sm} />]
? [
<page.pageIcon
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
/>,
]
: [
<CommandMenuContextChipIconWrapper>
<page.pageIcon
size={theme.icon.size.sm}
size={resolveThemeVariableAsNumber(
themeCssVariables.icon.size.sm,
)}
color={
isDefined(page.pageIconColor) &&
page.pageIconColor !== 'currentColor'
? page.pageIconColor
: theme.font.color.tertiary
: resolveThemeVariable(
themeCssVariables.font.color.tertiary,
)
}
/>
</CommandMenuContextChipIconWrapper>,
@@ -141,8 +153,6 @@ export const useCommandMenuContextChips = () => {
objectMetadataItems,
recordIdentifiers,
records,
theme.font.color.tertiary,
theme.icon.size.sm,
]);
return {
@@ -15,9 +15,11 @@ import {
DEFAULT_COLOR_LABELS,
MenuItemSelectColor,
} from 'twenty-ui/navigation';
import { MAIN_COLOR_NAMES, type ThemeColor } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
type ThemeColor,
MAIN_COLOR_NAMES,
themeCssVariables,
} from 'twenty-ui/theme-constants';
const NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID = 'navigation-menu-item-color';
const StyledMenuStyleText = styled.span`
@@ -18,8 +18,10 @@ import { useAddLinkToNavigationMenu } from '@/command-menu/pages/navigation-menu
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import {
resolveThemeVariable,
themeCssVariables,
} from 'twenty-ui/theme-constants';
type CommandMenuNewSidebarItemMainMenuProps = {
onSelectObject: () => void;
@@ -33,7 +35,6 @@ export const CommandMenuNewSidebarItemMainMenu = ({
onSelectRecord,
}: CommandMenuNewSidebarItemMainMenuProps) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { handleAddFolder } = useAddFolderToNavigationMenu();
const { handleAddLink } = useAddLinkToNavigationMenu();
@@ -86,7 +87,9 @@ export const CommandMenuNewSidebarItemMainMenu = ({
<Avatar
placeholder="L"
type="rounded"
backgroundColor={theme.color.green4}
backgroundColor={resolveThemeVariable(
themeCssVariables.color.green4,
)}
/>
)}
label={t`Record`}
@@ -5,9 +5,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
import { styled } from '@linaria/react';
import { ColorSample } from 'twenty-ui/display';
import { MenuItemSelect } from 'twenty-ui/navigation';
import { type ThemeColor } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type ThemeColor, themeCssVariables } from 'twenty-ui/theme-constants';
type ChartColorGradientOptionProps = {
colorOption: {
id: string;
@@ -6,10 +6,8 @@ import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { ColorSample } from 'twenty-ui/display';
import { MenuItemSelect } from 'twenty-ui/navigation';
import { type ThemeColor } from 'twenty-ui/theme';
import { getMainColorNameFromPaletteColorName } from 'twenty-ui/utilities';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type ThemeColor, themeCssVariables } from 'twenty-ui/theme-constants';
type ChartColorPaletteOptionProps = {
selectedItemId: string | null;
currentColor: string | null | undefined;
@@ -17,7 +17,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
import { t } from '@lingui/core/macro';
import { useState } from 'react';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { MAIN_COLOR_NAMES, type ThemeColor } from 'twenty-ui/theme';
import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme-constants';
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
type ColorOption = {
@@ -9,7 +9,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { MenuItemSelectTag } from 'twenty-ui/navigation';
import { type ThemeColor } from 'twenty-ui/theme';
import { type ThemeColor } from 'twenty-ui/theme-constants';
import { AggregateOperations } from '~/generated-metadata/graphql';
export const ChartRatioOptionSelectSelectableListItem = ({
@@ -16,7 +16,7 @@ import { t } from '@lingui/core/macro';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronLeft } from 'twenty-ui/display';
import { type ThemeColor } from 'twenty-ui/theme';
import { type ThemeColor } from 'twenty-ui/theme-constants';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
@@ -2,11 +2,13 @@ import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLo
import { viewableRichTextComponentState } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { styled } from '@linaria/react';
import { lazy, Suspense, useContext } from 'react';
import { lazy, Suspense } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
themeCssVariables,
resolveThemeVariable,
} from 'twenty-ui/theme-constants';
const ActivityRichTextEditor = lazy(() =>
import('@/activities/components/ActivityRichTextEditor').then((module) => ({
@@ -22,13 +24,13 @@ const StyledContainer = styled.div`
`;
const LoadingSkeleton = () => {
const { theme } = useContext(ThemeContext);
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={theme.border.radius.sm}
baseColor={resolveThemeVariable(themeCssVariables.background.tertiary)}
highlightColor={resolveThemeVariable(
themeCssVariables.background.transparent.lighter,
)}
borderRadius={resolveThemeVariable(themeCssVariables.border.radius.sm)}
>
<Skeleton height={SKELETON_LOADER_HEIGHT_SIZES.standard.s} />
</SkeletonTheme>
@@ -35,9 +35,11 @@ const StyledContainer = styled.div`
height: 100%;
`;
const StyledTabList = styled(TabList)`
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[2]};
const StyledTabListContainer = styled.div`
& > div {
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[2]};
}
`;
type TabId = WorkflowRunTabIdType;
@@ -131,11 +133,15 @@ export const CommandMenuWorkflowRunViewStepContent = () => {
/>
) : (
<>
<StyledTabList
tabs={tabs}
behaveAsLinks={false}
componentInstanceId={commandMenuPageComponentInstance.instanceId}
/>
<StyledTabListContainer>
<TabList
tabs={tabs}
behaveAsLinks={false}
componentInstanceId={
commandMenuPageComponentInstance.instanceId
}
/>
</StyledTabListContainer>
{activeTabId === WorkflowRunTabId.OUTPUT ? (
<WorkflowRunStepOutputDetail

Some files were not shown because too many files have changed in this diff Show More