Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit d36769f

Browse files
authored
Merge pull request #16 from builder-group/15-editor-sections
Move head-metadata
2 parents 767281d + 371e06b commit d36769f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2030
-25206
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ yarn-error.log*
3838
.DS_Store
3939
*.pem
4040
.todo
41+
.local

apps/web/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"@lit/react": "^1.0.7",
3030
"@mdx-js/loader": "^3.1.0",
3131
"@mdx-js/react": "^3.1.0",
32-
"@next/mdx": "^15.3.2",
32+
"@next/mdx": "^15.3.3",
3333
"@radix-ui/react-compose-refs": "^1.1.2",
3434
"@radix-ui/react-dialog": "^1.1.14",
3535
"@radix-ui/react-label": "^2.1.7",
@@ -40,7 +40,6 @@
4040
"@radix-ui/react-tooltip": "^1.2.7",
4141
"@radix-ui/react-visually-hidden": "^1.2.3",
4242
"@repo/api-core": "workspace:*",
43-
"@repo/head-metadata": "workspace:*",
4443
"@repo/widgets": "workspace:*",
4544
"@vercel/analytics": "^1.5.0",
4645
"@vercel/speed-insights": "^1.2.0",
@@ -52,11 +51,12 @@
5251
"feature-logger": "^0.0.42",
5352
"feature-react": "^0.0.51",
5453
"feature-state": "^0.0.51",
55-
"framer-motion": "^12.12.1",
54+
"framer-motion": "^12.15.0",
55+
"head-metadata": "0.0.4",
5656
"hono": "^4.7.10",
5757
"input-otp": "^1.4.2",
5858
"lucide-react": "^0.511.0",
59-
"next": "^15.3.2",
59+
"next": "^15.3.3",
6060
"react": "^19.1.0",
6161
"react-dom": "^19.1.0",
6262
"react-hot-toast": "^2.5.2",
@@ -74,14 +74,14 @@
7474
"devDependencies": {
7575
"@blgc/types": "^0.0.17",
7676
"@repo/types": "workspace:*",
77-
"@tailwindcss/postcss": "^4.1.7",
77+
"@tailwindcss/postcss": "^4.1.8",
7878
"@tailwindcss/typography": "^0.5.16",
7979
"@types/canvas-confetti": "^1.9.0",
8080
"@types/mdx": "^2.0.13",
81-
"@types/node": "^22.15.21",
82-
"@types/react": "19.1.5",
81+
"@types/node": "^22.15.29",
82+
"@types/react": "19.1.6",
8383
"@types/react-dom": "19.1.5",
84-
"postcss": "^8.5.3",
85-
"tailwindcss": "^4.1.7"
84+
"postcss": "^8.5.4",
85+
"tailwindcss": "^4.1.8"
8686
}
8787
}

apps/web/src/features/editor/lib/plugins/grid-node/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type TGridNodeECSPlugin = TECSPlugin<{
1515
// Markers
1616
AbsoluteLayout: TCAbsoluteLayout;
1717
AnimateGridRegion: TCAnimateGridRegion;
18+
1819
// Mixins
1920
GridSectionLayoutMixin: TCGridSectionLayoutMixin[];
2021
GridSectionRegionMixin: TCGridSectionRegionMixin[];
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Editor State and Layout Management
2+
3+
In our Editor, each Node is an entity within an Entity Component System (ECS). Each Node can have multiple components assigned to it, which may include functions, classes, or entity references. Due to the nature of these components, they cannot be stored directly in a database. Instead, each Node entity defines a mapper to a storable object, ensuring a persistable format through serialization and deserialization functions, similar to Rust's serde crate.
4+
5+
## Layout Management
6+
7+
Previously, layout data was stored separately from the Node, with each Node's position defined in a grid layout. This approach was limited to grid-based layouts and did not support other layout types.
8+
9+
### New Layout Format
10+
11+
The new format integrates layout data directly within each Node, allowing for more flexible layout management. Each Node can define its layout using a CSS-like approach, supporting multiple layout types such as Grid, Absolute, and potentially Flex in the future. Each Node specifies its layout regions directly, with a default layout for fallback purposes.
12+
13+
### Example
14+
15+
**Old Format**
16+
17+
```json
18+
Node
19+
{
20+
id: 'A'
21+
}
22+
23+
Layout
24+
lg: [
25+
{ id: 'A', region: { start: [0, 0], size: [4, 2] } },
26+
],
27+
md: [
28+
{ id: 'A', region: { start: [0, 0], size: [2, 2] } },
29+
],
30+
```
31+
32+
**New Format**
33+
34+
```json
35+
Node
36+
{
37+
id: 'A',
38+
region: {
39+
default: 'lg',
40+
lg: {
41+
start: [0, 0], size: [4, 2]
42+
},
43+
md: {
44+
start: [0, 0], size: [2, 2]
45+
}
46+
}
47+
}
48+
```
49+
50+
### Generic Layout Type
51+
52+
```ts
53+
type TBreakpointObject<TBase extends object, TKeys extends string> = {
54+
default: TKeys;
55+
} & {
56+
[K in TKeys]: TBase;
57+
};
58+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { TECSApp, TECSAppContext } from '../../ecs';
2+
import { TEditorECSPlugin } from '../editor';
3+
import { TSectionNodeECSPlugin } from './types';
4+
5+
export function gridNodePlugin(): TSectionNodeECSPlugin {
6+
return {
7+
name: 'section-node',
8+
components: {
9+
SectionNode: [],
10+
11+
SectionNodeGridLayoutMixin: [],
12+
SectionNodeGridChildMixin: [],
13+
SectionNodeGridAnimateRegionMixin: [],
14+
SectionNodeGridPendingMoveMixin: [],
15+
SectionNodeGridLastMoveMixin: [],
16+
SectionNodeAbsoluteLayoutMixin: [],
17+
SectionNodeAbsoluteChildMixin: []
18+
},
19+
setup(app: TECSApp<TECSAppContext<[TEditorECSPlugin, TSectionNodeECSPlugin]>>) {},
20+
appExtensions: {}
21+
};
22+
}

apps/web/src/features/editor/lib/plugins/section-node/systems.ts

Whitespace-only changes.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { TCIdMixinId, TECSPlugin, TEntity } from '../../ecs';
2+
import { TSize } from '../../types';
3+
import { TBaseGridLayout, TGridPosition, TGridRegion } from '../grid-node';
4+
5+
// =========================================================================
6+
// Plugin
7+
// =========================================================================
8+
9+
export type TSectionNodeECSPlugin = TECSPlugin<{
10+
name: 'section-node';
11+
components: {
12+
// Nodes
13+
SectionNode: TCSectionNode[];
14+
15+
// Mixins
16+
SectionNodeGridLayoutMixin: TCSectionNodeGridLayoutMixin[];
17+
SectionNodeGridChildMixin: TCSectionNodeGridChildMixin[];
18+
SectionNodeGridAnimateRegionMixin: TCSectionNodeGridAnimateRegionMixin[];
19+
SectionNodeGridPendingMoveMixin: TCSectionNodeGridPendingMoveMixin[];
20+
SectionNodeGridLastMoveMixin: TCSectionNodeGridLastMoveMixin[];
21+
SectionNodeAbsoluteLayoutMixin: TCSectionNodeAbsoluteLayoutMixin[];
22+
SectionNodeAbsoluteChildMixin: TCSectionNodeAbsoluteChildMixin[];
23+
};
24+
resources: {};
25+
events: {};
26+
appExtensions: {};
27+
systemSets: ['First', 'Update', 'Last'];
28+
customTypes: {};
29+
}>;
30+
31+
export interface TAddSectionNodeOptions {
32+
id?: TCIdMixinId;
33+
registerChildren?: (parentEid: TEntity) => void;
34+
layout?: TAddSectionNodeLayout;
35+
clipsContent?: boolean;
36+
root?: boolean;
37+
locked?: boolean;
38+
}
39+
40+
type TAddSectionNodeLayout =
41+
| {
42+
mode: 'grid';
43+
cellSize?: TSize;
44+
gapSize?: TSize;
45+
// initialLayout?: { id: TCIdMixinId; region: TGridRegion }[];
46+
}
47+
| {
48+
mode: 'absolute';
49+
};
50+
51+
// =========================================================================
52+
// Components
53+
// =========================================================================
54+
55+
export interface TCSectionNode {
56+
layoutMode: TSectionNodeLayoutMode;
57+
clipsContent: boolean;
58+
}
59+
60+
type TSectionNodeLayoutMode = 'grid' | 'absolute';
61+
62+
// Grid
63+
64+
export interface TCSectionNodeGridLayoutMixin {
65+
mode: 'grid';
66+
grid: TBaseGridLayout;
67+
cellSize: TSize;
68+
gapSize: TSize;
69+
}
70+
71+
export interface TCSectionNodeGridChildMixin {
72+
mode: 'grid';
73+
region: TGridRegion;
74+
}
75+
76+
export interface TCSectionNodeGridAnimateRegionMixin {
77+
targetRegion: TGridRegion;
78+
duration: number;
79+
easing?: string;
80+
}
81+
82+
export interface TCSectionNodeGridPendingMoveMixin {
83+
position: TGridPosition;
84+
timeout: NodeJS.Timeout;
85+
}
86+
87+
export interface TCSectionNodeGridLastMoveMixin {
88+
position: TGridPosition;
89+
}
90+
91+
// Absolute
92+
93+
export interface TCSectionNodeAbsoluteLayoutMixin {
94+
mode: 'absolute';
95+
}
96+
97+
export interface TCSectionNodeAbsoluteChildMixin {
98+
mode: 'absolute';
99+
}

packages/api-core/package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,22 @@
3737
"update:latest": "pnpm update --latest"
3838
},
3939
"dependencies": {
40-
"@asteasolutions/zod-to-openapi": "^7.3.0",
41-
"@aws-sdk/client-s3": "^3.815.0",
42-
"@aws-sdk/s3-request-presigner": "^3.815.0",
40+
"@asteasolutions/zod-to-openapi": "^7.3.2",
41+
"@aws-sdk/client-s3": "^3.821.0",
42+
"@aws-sdk/s3-request-presigner": "^3.821.0",
4343
"@blgc/utils": "^0.0.52",
44-
"@hono/zod-openapi": "^0.19.6",
45-
"@hono/zod-validator": "^0.5.0",
46-
"@repo/head-metadata": "workspace:*",
44+
"@hono/zod-openapi": "^0.19.8",
45+
"@hono/zod-validator": "^0.7.0",
4746
"@repo/hono-utils": "workspace:*",
4847
"@repo/transactional": "workspace:*",
4948
"arctic": "^3.7.0",
5049
"blurhash": "^2.0.5",
5150
"decode-ico": "^0.4.1",
52-
"drizzle-orm": "^0.43.1",
53-
"drizzle-zod": "^0.8.1",
51+
"drizzle-orm": "^0.44.1",
52+
"drizzle-zod": "^0.7.1",
5453
"feature-fetch": "^0.0.43",
5554
"feature-logger": "^0.0.42",
55+
"head-metadata": "0.0.4",
5656
"hono": "^4.7.10",
5757
"ico-endec": "^0.1.6",
5858
"jose": "^6.0.11",
@@ -63,16 +63,16 @@
6363
"sharp": "^0.34.2",
6464
"validatenv": "^0.0.35",
6565
"validation-adapters": "^0.0.25",
66-
"zod": "3.25.20"
66+
"zod": "3.25.42"
6767
},
6868
"devDependencies": {
6969
"@blgc/types": "^0.0.17",
70-
"@hono/node-server": "^1.14.2",
70+
"@hono/node-server": "^1.14.3",
7171
"@rollup/plugin-replace": "^6.0.2",
72-
"@types/mime-types": "^2.1.4",
73-
"@types/node": "^22.15.21",
72+
"@types/mime-types": "^3.0.0",
73+
"@types/node": "^22.15.29",
7474
"@types/pg": "^8.15.2",
75-
"@types/react": "^19.1.5",
75+
"@types/react": "^19.1.6",
7676
"dotenv": "^16.5.0",
7777
"drizzle-kit": "^0.31.1"
7878
}

packages/api-core/src/app/routes/v1.metatags/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import {
2-
extractHeadMetadata,
3-
linkExtractor,
4-
metaExtractor,
5-
titleExtractor
6-
} from '@repo/head-metadata';
71
import { AppError } from '@repo/hono-utils';
82
import { RequestError } from 'feature-fetch';
3+
import { extractHeadMetadata, linkExtractor, metaExtractor, titleExtractor } from 'head-metadata';
94
import { router } from '@/app/router';
105
import { fetchClient } from '@/environment';
116
import { GetMetatagsRoute, TMetatagsDto } from './schema';

packages/api-core/src/app/routes/v1.sites.[id].pages/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { SPageLayoutTable, SPageTable, SPageWidgetTable, SWidgetTable } from '@/environment';
12
import { createRoute } from '@hono/zod-openapi';
23
import { ErrorResponse, JsonSuccessResponse } from '@repo/hono-utils';
34
import { z } from 'zod';
4-
import { SPageLayoutTable, SPageTable, SPageWidgetTable, SWidgetTable } from '@/environment';
55

66
export const SPageWidgetDto = z.object({
77
id: SWidgetTable.shape.id,

0 commit comments

Comments
 (0)