@@ -18,6 +18,7 @@ jobs:
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-sdk/**
|
||||
packages/twenty-server/**
|
||||
!packages/twenty-sdk/package.json
|
||||
sdk-test:
|
||||
needs: changed-files-check
|
||||
|
||||
@@ -66,11 +66,10 @@ yarn twenty app:uninstall
|
||||
|
||||
Control which example files are included when creating a new app:
|
||||
|
||||
| Flag | Behavior |
|
||||
| ------------------- | ----------------------------------------------------------------------- |
|
||||
| `-e, --exhaustive` | **(default)** Creates all example files without prompting |
|
||||
| `-m, --minimal` | Creates only core files (`application-config.ts` and `default-role.ts`) |
|
||||
| `-i, --interactive` | Prompts you to select which examples to include |
|
||||
| Flag | Behavior |
|
||||
| ------------------ | ----------------------------------------------------------------------- |
|
||||
| `-e, --exhaustive` | **(default)** Creates all example files |
|
||||
| `-m, --minimal` | Creates only core files (`application-config.ts` and `default-role.ts`) |
|
||||
|
||||
```bash
|
||||
# Default: all examples included
|
||||
@@ -78,22 +77,8 @@ npx create-twenty-app@latest my-app
|
||||
|
||||
# Minimal: only core files
|
||||
npx create-twenty-app@latest my-app -m
|
||||
|
||||
# Interactive: choose which examples to include
|
||||
npx create-twenty-app@latest my-app -i
|
||||
```
|
||||
|
||||
In interactive mode, you can pick from:
|
||||
|
||||
- **Example object** — a custom CRM object definition (`objects/example-object.ts`)
|
||||
- **Example field** — a custom field on the example object (`fields/example-field.ts`)
|
||||
- **Example logic function** — a server-side handler with HTTP trigger (`logic-functions/hello-world.ts`)
|
||||
- **Example front component** — a React UI component (`front-components/hello-world.tsx`)
|
||||
- **Example view** — a saved view for the example object (`views/example-view.ts`)
|
||||
- **Example navigation menu item** — a sidebar link (`navigation-menu-items/example-navigation-menu-item.ts`)
|
||||
- **Example skill** — an AI agent skill definition (`skills/example-skill.ts`)
|
||||
- **Integration test** — a vitest integration test verifying app installation (`__tests__/app-install.integration-test.ts`)
|
||||
|
||||
## What gets scaffolded
|
||||
|
||||
**Core files (always created):**
|
||||
|
||||
@@ -18,10 +18,6 @@ const program = new Command(packageJson.name)
|
||||
'-m, --minimal',
|
||||
'Create only core entities (application-config and default-role)',
|
||||
)
|
||||
.option(
|
||||
'-i, --interactive',
|
||||
'Interactively choose which entity examples to include',
|
||||
)
|
||||
.helpOption('-h, --help', 'Display this help message.')
|
||||
.action(
|
||||
async (
|
||||
@@ -29,19 +25,14 @@ const program = new Command(packageJson.name)
|
||||
options?: {
|
||||
exhaustive?: boolean;
|
||||
minimal?: boolean;
|
||||
interactive?: boolean;
|
||||
},
|
||||
) => {
|
||||
const modeFlags = [
|
||||
options?.exhaustive,
|
||||
options?.minimal,
|
||||
options?.interactive,
|
||||
].filter(Boolean);
|
||||
const modeFlags = [options?.exhaustive, options?.minimal].filter(Boolean);
|
||||
|
||||
if (modeFlags.length > 1) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: --exhaustive, --minimal, and --interactive are mutually exclusive.',
|
||||
'Error: --exhaustive and --minimal are mutually exclusive.',
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
@@ -56,11 +47,7 @@ const program = new Command(packageJson.name)
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const mode: ScaffoldingMode = options?.minimal
|
||||
? 'minimal'
|
||||
: options?.interactive
|
||||
? 'interactive'
|
||||
: 'exhaustive';
|
||||
const mode: ScaffoldingMode = options?.minimal ? 'minimal' : 'exhaustive';
|
||||
|
||||
await new CreateAppCommand().execute(directory, mode);
|
||||
},
|
||||
|
||||
@@ -24,7 +24,7 @@ export class CreateAppCommand {
|
||||
const { appName, appDisplayName, appDirectory, appDescription } =
|
||||
await this.getAppInfos(directory);
|
||||
|
||||
const exampleOptions = await this.resolveExampleOptions(mode);
|
||||
const exampleOptions = this.resolveExampleOptions(mode);
|
||||
|
||||
await this.validateDirectory(appDirectory);
|
||||
|
||||
@@ -103,9 +103,7 @@ export class CreateAppCommand {
|
||||
return { appName, appDisplayName, appDirectory, appDescription };
|
||||
}
|
||||
|
||||
private async resolveExampleOptions(
|
||||
mode: ScaffoldingMode,
|
||||
): Promise<ExampleOptions> {
|
||||
private resolveExampleOptions(mode: ScaffoldingMode): ExampleOptions {
|
||||
if (mode === 'minimal') {
|
||||
return {
|
||||
includeExampleObject: false,
|
||||
@@ -119,94 +117,15 @@ export class CreateAppCommand {
|
||||
};
|
||||
}
|
||||
|
||||
if (mode === 'exhaustive') {
|
||||
return {
|
||||
includeExampleObject: true,
|
||||
includeExampleField: true,
|
||||
includeExampleLogicFunction: true,
|
||||
includeExampleFrontComponent: true,
|
||||
includeExampleView: true,
|
||||
includeExampleNavigationMenuItem: true,
|
||||
includeExampleSkill: true,
|
||||
includeExampleIntegrationTest: true,
|
||||
};
|
||||
}
|
||||
|
||||
const { selectedExamples } = await inquirer.prompt([
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'selectedExamples',
|
||||
message: 'Select which example files to include:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Example object (custom object definition)',
|
||||
value: 'object',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example field (custom field on the example object)',
|
||||
value: 'field',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example logic function (server-side handler)',
|
||||
value: 'logicFunction',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example front component (React UI component)',
|
||||
value: 'frontComponent',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example view (saved view for the example object)',
|
||||
value: 'view',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example navigation menu item (sidebar link)',
|
||||
value: 'navigationMenuItem',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Example skill (AI agent skill definition)',
|
||||
value: 'skill',
|
||||
checked: true,
|
||||
},
|
||||
{
|
||||
name: 'Integration test (vitest test verifying app installation)',
|
||||
value: 'integrationTest',
|
||||
checked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const includeField = selectedExamples.includes('field');
|
||||
const includeView = selectedExamples.includes('view');
|
||||
const includeExampleIntegrationTest =
|
||||
selectedExamples.includes('integrationTest');
|
||||
const includeObject =
|
||||
selectedExamples.includes('object') || includeField || includeView;
|
||||
|
||||
if ((includeField || includeView) && !selectedExamples.includes('object')) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Note: Example object auto-included because example field/view depends on it.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
includeExampleObject: includeObject,
|
||||
includeExampleField: includeField,
|
||||
includeExampleLogicFunction: selectedExamples.includes('logicFunction'),
|
||||
includeExampleFrontComponent: selectedExamples.includes('frontComponent'),
|
||||
includeExampleView: includeView,
|
||||
includeExampleNavigationMenuItem:
|
||||
selectedExamples.includes('navigationMenuItem'),
|
||||
includeExampleSkill: selectedExamples.includes('skill'),
|
||||
includeExampleIntegrationTest,
|
||||
includeExampleObject: true,
|
||||
includeExampleField: true,
|
||||
includeExampleLogicFunction: true,
|
||||
includeExampleFrontComponent: true,
|
||||
includeExampleView: true,
|
||||
includeExampleNavigationMenuItem: true,
|
||||
includeExampleSkill: true,
|
||||
includeExampleIntegrationTest: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type ScaffoldingMode = 'exhaustive' | 'minimal' | 'interactive';
|
||||
export type ScaffoldingMode = 'exhaustive' | 'minimal';
|
||||
|
||||
export type ExampleOptions = {
|
||||
includeExampleObject: boolean;
|
||||
|
||||
@@ -673,15 +673,24 @@ describe('copyBaseApplicationProject', () => {
|
||||
|
||||
const content = await fs.readFile(viewPath, 'utf8');
|
||||
|
||||
expect(content).toContain("import { defineView } from 'twenty-sdk'");
|
||||
expect(content).toContain(
|
||||
"import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object'",
|
||||
"import { defineView, ViewKey } from 'twenty-sdk'",
|
||||
);
|
||||
expect(content).toContain(
|
||||
"import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object'",
|
||||
);
|
||||
expect(content).toContain('export default defineView({');
|
||||
expect(content).toContain(
|
||||
'objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER',
|
||||
);
|
||||
expect(content).toContain("name: 'example-view'");
|
||||
expect(content).toContain("name: 'All example items'");
|
||||
expect(content).toContain('fields: [');
|
||||
expect(content).toContain(
|
||||
'fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER',
|
||||
);
|
||||
expect(content).toContain('isVisible: true');
|
||||
expect(content).toContain('key: ViewKey.INDEX');
|
||||
expect(content).toContain('size: 200');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -712,6 +721,7 @@ describe('copyBaseApplicationProject', () => {
|
||||
expect(content).toContain('export default defineNavigationMenuItem({');
|
||||
expect(content).toContain("name: 'example-navigation-menu-item'");
|
||||
expect(content).toContain("icon: 'IconList'");
|
||||
expect(content).toContain("color: 'blue'");
|
||||
expect(content).toContain('position: 0');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -431,16 +431,29 @@ const createExampleView = async ({
|
||||
fileName: string;
|
||||
}) => {
|
||||
const universalIdentifier = v4();
|
||||
const viewFieldUniversalIdentifier = v4();
|
||||
|
||||
const content = `import { defineView } from 'twenty-sdk';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
|
||||
const content = `import { defineView, ViewKey } from 'twenty-sdk';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
|
||||
|
||||
export const EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER = '${universalIdentifier}';
|
||||
|
||||
export default defineView({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
name: 'example-view',
|
||||
universalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
|
||||
name: 'All example items',
|
||||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
icon: 'IconList',
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: '${viewFieldUniversalIdentifier}',
|
||||
fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
],
|
||||
});
|
||||
`;
|
||||
|
||||
@@ -460,18 +473,15 @@ const createExampleNavigationMenuItem = async ({
|
||||
const universalIdentifier = v4();
|
||||
|
||||
const content = `import { defineNavigationMenuItem } from 'twenty-sdk';
|
||||
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from 'src/views/example-view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
name: 'example-navigation-menu-item',
|
||||
icon: 'IconList',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
// Link to a view:
|
||||
// viewUniversalIdentifier: '...',
|
||||
// Or link to an object:
|
||||
// targetObjectUniversalIdentifier: '...',
|
||||
// Or link to an external URL:
|
||||
// link: 'https://example.com',
|
||||
viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
`;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ cd my-twenty-app
|
||||
yarn twenty app:dev
|
||||
```
|
||||
|
||||
The scaffolder supports three modes for controlling which example files are included:
|
||||
The scaffolder supports two modes for controlling which example files are included:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill)
|
||||
@@ -43,9 +43,6 @@ npx create-twenty-app@latest my-app
|
||||
|
||||
# Minimal: only core files (application-config.ts and default-role.ts)
|
||||
npx create-twenty-app@latest my-app --minimal
|
||||
|
||||
# Interactive: select which examples to include
|
||||
npx create-twenty-app@latest my-app --interactive
|
||||
```
|
||||
|
||||
From here you can:
|
||||
@@ -121,7 +118,7 @@ my-twenty-app/
|
||||
└── example-skill.ts # Example AI agent skill definition
|
||||
```
|
||||
|
||||
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`). With `--interactive`, you choose which example files to include.
|
||||
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`).
|
||||
|
||||
At a high level:
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ const APP_FOLDER = 'src';
|
||||
|
||||
export class EntityAddCommand {
|
||||
private lastObjectUniversalIdentifier: string | undefined;
|
||||
private lastNameFieldUniversalIdentifier: string | undefined;
|
||||
|
||||
async execute(entityType?: SyncableEntity, path?: string): Promise<void> {
|
||||
try {
|
||||
@@ -75,13 +76,16 @@ export class EntityAddCommand {
|
||||
|
||||
const name = entityData.nameSingular;
|
||||
const objectUniversalIdentifier = v4();
|
||||
const nameFieldUniversalIdentifier = v4();
|
||||
|
||||
this.lastObjectUniversalIdentifier = objectUniversalIdentifier;
|
||||
this.lastNameFieldUniversalIdentifier = nameFieldUniversalIdentifier;
|
||||
|
||||
const file = getObjectBaseFile({
|
||||
data: entityData,
|
||||
name,
|
||||
universalIdentifier: objectUniversalIdentifier,
|
||||
nameFieldUniversalIdentifier,
|
||||
});
|
||||
|
||||
return { name, file };
|
||||
@@ -202,6 +206,17 @@ export class EntityAddCommand {
|
||||
name: `all-${kebabCase(objectName)}`,
|
||||
universalIdentifier: viewUniversalIdentifier,
|
||||
objectUniversalIdentifier: this.lastObjectUniversalIdentifier,
|
||||
fields: this.lastNameFieldUniversalIdentifier
|
||||
? [
|
||||
{
|
||||
fieldMetadataUniversalIdentifier:
|
||||
this.lastNameFieldUniversalIdentifier,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
});
|
||||
|
||||
const viewFolderPath = customPath
|
||||
|
||||
+10
-1
@@ -12,15 +12,24 @@ describe('getNewObjectFileContent', () => {
|
||||
name: 'company',
|
||||
});
|
||||
|
||||
expect(result).toContain("import { defineObject } from 'twenty-sdk'");
|
||||
expect(result).toContain(
|
||||
"import { defineObject, FieldType } from 'twenty-sdk'",
|
||||
);
|
||||
expect(result).toContain('export default defineObject({');
|
||||
expect(result).toContain('export const NAME_FIELD_UNIVERSAL_IDENTIFIER');
|
||||
|
||||
expect(result).toContain("nameSingular: 'company'");
|
||||
expect(result).toContain("namePlural: 'companies'");
|
||||
expect(result).toContain("labelSingular: 'Company'");
|
||||
expect(result).toContain("labelPlural: 'Companies'");
|
||||
expect(result).toContain("icon: 'IconBox'");
|
||||
expect(result).toContain(
|
||||
'labelIdentifierFieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER',
|
||||
);
|
||||
expect(result).toContain('fields: [');
|
||||
expect(result).toContain('FieldType.TEXT');
|
||||
expect(result).toContain("name: 'name'");
|
||||
expect(result).toContain("label: 'Name'");
|
||||
|
||||
expect(result).toMatch(
|
||||
/universalIdentifier: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'/,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { v4 } from 'uuid';
|
||||
export const getObjectBaseFile = ({
|
||||
data,
|
||||
universalIdentifier = v4(),
|
||||
nameFieldUniversalIdentifier = v4(),
|
||||
}: {
|
||||
data: {
|
||||
nameSingular: string;
|
||||
@@ -12,8 +13,12 @@ export const getObjectBaseFile = ({
|
||||
};
|
||||
name: string;
|
||||
universalIdentifier?: string;
|
||||
nameFieldUniversalIdentifier?: string;
|
||||
}) => {
|
||||
return `import { defineObject } from 'twenty-sdk';
|
||||
return `import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
export const NAME_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'${nameFieldUniversalIdentifier}';
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
@@ -22,15 +27,16 @@ export default defineObject({
|
||||
labelSingular: '${data.labelSingular}',
|
||||
labelPlural: '${data.labelPlural}',
|
||||
icon: 'IconBox',
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
fields: [
|
||||
// Add your fields here using defineField helper
|
||||
// Example:
|
||||
// {
|
||||
// universalIdentifier: '...',
|
||||
// type: FieldMetadataType.TEXT,
|
||||
// name: 'description',
|
||||
// label: 'Description',
|
||||
// },
|
||||
{
|
||||
universalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
type: FieldType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
description: 'Name of the ${data.nameSingular}',
|
||||
icon: 'IconAbc',
|
||||
},
|
||||
],
|
||||
});
|
||||
`;
|
||||
|
||||
@@ -1,17 +1,56 @@
|
||||
import { kebabCase } from '@/cli/utilities/string/kebab-case';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
type ViewFieldTemplate = {
|
||||
universalIdentifier?: string;
|
||||
fieldMetadataUniversalIdentifier: string;
|
||||
position: number;
|
||||
isVisible?: boolean;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export const getViewBaseFile = ({
|
||||
name,
|
||||
universalIdentifier = v4(),
|
||||
objectUniversalIdentifier = 'fill-later',
|
||||
fields = [],
|
||||
}: {
|
||||
name: string;
|
||||
universalIdentifier?: string;
|
||||
objectUniversalIdentifier?: string;
|
||||
fields?: ViewFieldTemplate[];
|
||||
}) => {
|
||||
const kebabCaseName = kebabCase(name);
|
||||
|
||||
const formattedFields = fields.map((field, index) => {
|
||||
const uid = field.universalIdentifier ?? v4();
|
||||
return {
|
||||
universalIdentifier: uid,
|
||||
fieldMetadataUniversalIdentifier: field.fieldMetadataUniversalIdentifier,
|
||||
position: field.position ?? index,
|
||||
isVisible: field.isVisible ?? true,
|
||||
size: field.size ?? 200,
|
||||
};
|
||||
});
|
||||
|
||||
const defaultFields = ` // fields: [
|
||||
// {
|
||||
// universalIdentifier: '...',
|
||||
// fieldMetadataUniversalIdentifier: '...',
|
||||
// position: 0,
|
||||
// isVisible: true,
|
||||
// },
|
||||
// ],`;
|
||||
|
||||
const fieldsBlock =
|
||||
fields.length > 0
|
||||
? ` fields: [
|
||||
${formattedFields
|
||||
.map((field) => JSON.stringify(field, null, 2))
|
||||
.join(',\n')},\n
|
||||
],`
|
||||
: defaultFields;
|
||||
|
||||
return `import { defineView } from 'twenty-sdk';
|
||||
|
||||
export default defineView({
|
||||
@@ -20,14 +59,7 @@ export default defineView({
|
||||
objectUniversalIdentifier: '${objectUniversalIdentifier}',
|
||||
icon: 'IconList',
|
||||
position: 0,
|
||||
// fields: [
|
||||
// {
|
||||
// universalIdentifier: '...',
|
||||
// fieldMetadataUniversalIdentifier: '...',
|
||||
// position: 0,
|
||||
// isVisible: true,
|
||||
// },
|
||||
// ],
|
||||
${fieldsBlock}
|
||||
// filters: [
|
||||
// {
|
||||
// universalIdentifier: '...',
|
||||
|
||||
@@ -71,6 +71,7 @@ export { PermissionFlag } from './roles/permission-flag-type';
|
||||
export { defineSkill } from './skills/define-skill';
|
||||
export { defineView } from './views/define-view';
|
||||
export type { ViewConfig } from './views/view-config';
|
||||
export { ViewKey } from './views/view-key';
|
||||
|
||||
// Action components for front components
|
||||
export {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { ViewKey } from 'twenty-shared/types';
|
||||
+3
-1
@@ -2,6 +2,7 @@ import {
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewVisibility,
|
||||
ViewKey,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { fromViewManifestToUniversalFlatView } from 'src/engine/core-modules/application/application-manifest/converters/from-view-manifest-to-universal-flat-view.util';
|
||||
@@ -16,6 +17,7 @@ describe('fromViewManifestToUniversalFlatView', () => {
|
||||
universalIdentifier: 'view-uuid-1',
|
||||
name: 'All Records',
|
||||
objectUniversalIdentifier: 'object-uuid-1',
|
||||
key: ViewKey.INDEX,
|
||||
},
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
@@ -34,7 +36,7 @@ describe('fromViewManifestToUniversalFlatView', () => {
|
||||
expect(result.isCustom).toBe(true);
|
||||
expect(result.visibility).toBe(ViewVisibility.WORKSPACE);
|
||||
expect(result.openRecordIn).toBe(ViewOpenRecordIn.SIDE_PANEL);
|
||||
expect(result.key).toBeNull();
|
||||
expect(result.key).toBe(ViewKey.INDEX);
|
||||
expect(result.createdAt).toBe(now);
|
||||
expect(result.updatedAt).toBe(now);
|
||||
});
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export const fromViewManifestToUniversalFlatView = ({
|
||||
isCustom: true,
|
||||
visibility: viewManifest.visibility ?? ViewVisibility.WORKSPACE,
|
||||
openRecordIn: viewManifest.openRecordIn ?? ViewOpenRecordIn.SIDE_PANEL,
|
||||
key: null,
|
||||
key: viewManifest.key ?? null,
|
||||
kanbanAggregateOperation: null,
|
||||
kanbanAggregateOperationFieldMetadataUniversalIdentifier: null,
|
||||
calendarLayout: null,
|
||||
|
||||
+1
-1
@@ -6,6 +6,7 @@ import {
|
||||
FeatureFlagKey,
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewKey,
|
||||
ViewVisibility,
|
||||
} from 'twenty-shared/types';
|
||||
import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils';
|
||||
@@ -41,7 +42,6 @@ import {
|
||||
import { computeFlatDefaultRecordPageLayoutToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-default-record-page-layout-to-create.util';
|
||||
import { computeFlatRecordPageFieldsViewToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-record-page-fields-view-to-create.util';
|
||||
import { computeFlatViewFieldsToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-view-fields-to-create.util';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
|
||||
+1
-1
@@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
AggregateOperations,
|
||||
ViewOpenRecordIn,
|
||||
ViewKey,
|
||||
ViewType,
|
||||
ViewVisibility,
|
||||
} from 'twenty-shared/types';
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
|
||||
import { ViewCalendarLayout } from 'src/engine/metadata-modules/view/enums/view-calendar-layout.enum';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
@InputType()
|
||||
export class CreateViewInput {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewVisibility,
|
||||
ViewKey,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
@@ -16,7 +17,6 @@ import { ViewFilterDTO } from 'src/engine/metadata-modules/view-filter/dtos/view
|
||||
import { ViewGroupDTO } from 'src/engine/metadata-modules/view-group/dtos/view-group.dto';
|
||||
import { ViewSortDTO } from 'src/engine/metadata-modules/view-sort/dtos/view-sort.dto';
|
||||
import { ViewCalendarLayout } from 'src/engine/metadata-modules/view/enums/view-calendar-layout.enum';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
registerEnumType(ViewOpenRecordIn, { name: 'ViewOpenRecordIn' });
|
||||
registerEnumType(ViewType, { name: 'ViewType' });
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AggregateOperations,
|
||||
ViewOpenRecordIn,
|
||||
ViewType,
|
||||
ViewKey,
|
||||
ViewVisibility,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
@@ -29,7 +30,6 @@ import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entiti
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
||||
import { ViewCalendarLayout } from 'src/engine/metadata-modules/view/enums/view-calendar-layout.enum';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
// We could refactor this type to be dynamic to view type
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { AggregateOperations, ViewType } from 'twenty-shared/types';
|
||||
import { AggregateOperations, ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
import { ViewType, ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
|
||||
import {
|
||||
createStandardViewFlatMetadata,
|
||||
type CreateStandardViewArgs,
|
||||
|
||||
+1
-1
@@ -3,12 +3,12 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import {
|
||||
type AggregateOperations,
|
||||
type ViewType,
|
||||
type ViewKey,
|
||||
ViewOpenRecordIn,
|
||||
ViewVisibility,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { type ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type';
|
||||
import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { findCoreViews } from 'test/integration/metadata/suites/view/utils/find-
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
|
||||
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
|
||||
describe('successful find view with all sub-relations (e2e)', () => {
|
||||
let companyObjectMetadataId: string;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { generateRecordName } from 'test/integration/utils/generate-record-name'
|
||||
import { assertViewStructure } from 'test/integration/utils/view-test.util';
|
||||
import { ViewOpenRecordIn, ViewType } from 'twenty-shared/types';
|
||||
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
|
||||
describe('View REST API', () => {
|
||||
let testObjectMetadataId: string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsType';
|
||||
import {
|
||||
type ViewKey,
|
||||
type AggregateOperations,
|
||||
type ViewFilterGroupLogicalOperator,
|
||||
type ViewFilterOperand,
|
||||
@@ -55,6 +56,7 @@ export type ViewManifest = SyncableEntityOptions & {
|
||||
name: string;
|
||||
objectUniversalIdentifier: string;
|
||||
type?: ViewType;
|
||||
key?: ViewKey;
|
||||
icon?: string;
|
||||
position?: number;
|
||||
isCompact?: boolean;
|
||||
|
||||
@@ -262,6 +262,7 @@ export { IsValidGraphQLEnumName } from './validators/is-valid-graphql-enum-name.
|
||||
export { ViewFilterGroupLogicalOperator } from './ViewFilterGroupLogicalOperator';
|
||||
export { ViewFilterOperand } from './ViewFilterOperand';
|
||||
export { ViewFilterOperandDeprecated } from './ViewFilterOperandDeprecated';
|
||||
export { ViewKey } from './ViewKey';
|
||||
export { ViewOpenRecordIn } from './ViewOpenRecordIn';
|
||||
export { ViewType } from './ViewType';
|
||||
export { ViewVisibility } from './ViewVisibility';
|
||||
|
||||
+18
-7
@@ -110,8 +110,16 @@
|
||||
--t-background-overlay-primary: #000000b8;
|
||||
--t-background-overlay-secondary: #0000005c;
|
||||
--t-background-overlay-tertiary: #0000005c;
|
||||
--t-background-radial-gradient: radial-gradient(50% 62.62% at 50% 0%, color(display-p3 0.506 0.506 0.506) 0%, color(display-p3 0.482 0.482 0.482) 100%);
|
||||
--t-background-radial-gradient-hover: radial-gradient(76.32% 95.59% at 50% 0%, color(display-p3 0.482 0.482 0.482) 0%, color(display-p3 0.702 0.702 0.702) 100%);
|
||||
--t-background-radial-gradient: radial-gradient(
|
||||
50% 62.62% at 50% 0%,
|
||||
color(display-p3 0.506 0.506 0.506) 0%,
|
||||
color(display-p3 0.482 0.482 0.482) 100%
|
||||
);
|
||||
--t-background-radial-gradient-hover: radial-gradient(
|
||||
76.32% 95.59% at 50% 0%,
|
||||
color(display-p3 0.482 0.482 0.482) 0%,
|
||||
color(display-p3 0.702 0.702 0.702) 100%
|
||||
);
|
||||
--t-background-primary-inverted: color(display-p3 0.922 0.922 0.922);
|
||||
--t-background-primary-inverted-hover: color(display-p3 0.702 0.702 0.702);
|
||||
--t-blur-light: blur(6px) saturate(200%) contrast(100%) brightness(130%);
|
||||
@@ -132,11 +140,14 @@
|
||||
--t-border-radius-xxl: 40px;
|
||||
--t-border-radius-pill: 999px;
|
||||
--t-border-radius-rounded: 100%;
|
||||
--t-box-shadow-color: rgba(0,0,0,0.6);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px rgba(0,0,0,0.04), 0px 0px 4px 0px rgba(0,0,0,0.08);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px rgba(0,0,0,0.16), 0px 2px 4px 0px rgba(0,0,0,0.08);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px rgba(0,0,0,0.32);
|
||||
--t-box-shadow-super-heavy: 2px 4px 16px 0px rgba(0,0,0,0.12), 0px 2px 4px 0px rgba(0,0,0,0.04);
|
||||
--t-box-shadow-color: rgba(0, 0, 0, 0.6);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px rgba(0, 0, 0, 0.04),
|
||||
0px 0px 4px 0px rgba(0, 0, 0, 0.08);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px rgba(0, 0, 0, 0.16),
|
||||
0px 2px 4px 0px rgba(0, 0, 0, 0.08);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px rgba(0, 0, 0, 0.32);
|
||||
--t-box-shadow-super-heavy: 2px 4px 16px 0px rgba(0, 0, 0, 0.12),
|
||||
0px 2px 4px 0px rgba(0, 0, 0, 0.04);
|
||||
--t-font-color-primary: color(display-p3 0.922 0.922 0.922);
|
||||
--t-font-color-secondary: color(display-p3 0.702 0.702 0.702);
|
||||
--t-font-color-tertiary: color(display-p3 0.506 0.506 0.506);
|
||||
|
||||
+17
-5
@@ -110,8 +110,16 @@
|
||||
--t-background-overlay-primary: color(display-p3 0 0 0 / 0.722);
|
||||
--t-background-overlay-secondary: color(display-p3 0 0 0 / 0.361);
|
||||
--t-background-overlay-tertiary: color(display-p3 0 0 0 / 0.071);
|
||||
--t-background-radial-gradient: radial-gradient(50% 62.62% at 50% 0%, color(display-p3 0.6 0.6 0.6) 0%, color(display-p3 0.514 0.514 0.514) 100%);
|
||||
--t-background-radial-gradient-hover: radial-gradient(76.32% 95.59% at 50% 0%, color(display-p3 0.514 0.514 0.514) 0%, color(display-p3 0.4 0.4 0.4) 100%);
|
||||
--t-background-radial-gradient: radial-gradient(
|
||||
50% 62.62% at 50% 0%,
|
||||
color(display-p3 0.6 0.6 0.6) 0%,
|
||||
color(display-p3 0.514 0.514 0.514) 100%
|
||||
);
|
||||
--t-background-radial-gradient-hover: radial-gradient(
|
||||
76.32% 95.59% at 50% 0%,
|
||||
color(display-p3 0.514 0.514 0.514) 0%,
|
||||
color(display-p3 0.4 0.4 0.4) 100%
|
||||
);
|
||||
--t-background-primary-inverted: color(display-p3 0.2 0.2 0.2);
|
||||
--t-background-primary-inverted-hover: color(display-p3 0.4 0.4 0.4);
|
||||
--t-blur-light: blur(6px) saturate(200%) contrast(50%) brightness(130%);
|
||||
@@ -133,10 +141,14 @@
|
||||
--t-border-radius-pill: 999px;
|
||||
--t-border-radius-rounded: 100%;
|
||||
--t-box-shadow-color: color(display-p3 0 0 0 / 0.039);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px color(display-p3 0 0 0 / 0.039), 0px 0px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px color(display-p3 0 0 0 / 0.161), 0px 2px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px color(display-p3 0 0 0 / 0.039),
|
||||
0px 0px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px color(display-p3 0 0 0 / 0.161),
|
||||
0px 2px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px color(display-p3 0 0 0 / 0.361);
|
||||
--t-box-shadow-super-heavy: 0px 0px 8px 0px color(display-p3 0 0 0 / 0.161), 0px 8px 64px -16px color(display-p3 0 0 0 / 0.478), 0px 24px 56px -16px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-super-heavy: 0px 0px 8px 0px color(display-p3 0 0 0 / 0.161),
|
||||
0px 8px 64px -16px color(display-p3 0 0 0 / 0.478),
|
||||
0px 24px 56px -16px color(display-p3 0 0 0 / 0.078);
|
||||
--t-font-color-primary: color(display-p3 0.2 0.2 0.2);
|
||||
--t-font-color-secondary: color(display-p3 0.4 0.4 0.4);
|
||||
--t-font-color-tertiary: color(display-p3 0.6 0.6 0.6);
|
||||
|
||||
Reference in New Issue
Block a user