camox 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/components/lexical/SidebarLexicalEditor.js +2 -1
- package/dist/core/createApp.d.ts +231 -209
- package/dist/core/createApp.js +17 -17
- package/dist/core/createBlock.d.ts +74 -72
- package/dist/core/createBlock.js +274 -267
- package/dist/core/createLayout.d.ts +100 -80
- package/dist/core/createLayout.js +93 -65
- package/dist/features/preview/CamoxPreview.js +76 -54
- package/dist/features/preview/components/AddBlockSheet.js +12 -12
- package/dist/features/preview/components/AssetFieldEditor.js +1 -1
- package/dist/features/preview/components/AssetLightbox.js +1 -1
- package/dist/features/preview/components/AssetPickerGrid.js +1 -1
- package/dist/features/preview/components/BlockActionsPopover.js +26 -26
- package/dist/features/preview/components/BlockErrorBoundary.js +59 -0
- package/dist/features/preview/components/{CreatePageSheet.js → CreatePageModal.js} +16 -18
- package/dist/features/preview/components/{EditPageSheet.js → EditPageModal.js} +32 -25
- package/dist/features/preview/components/Frame.js +1 -1
- package/dist/features/preview/components/ItemFieldsEditor.js +134 -98
- package/dist/features/preview/components/LinkFieldEditor.js +166 -146
- package/dist/features/preview/components/PageContentSheet.js +42 -37
- package/dist/features/preview/components/PageLocationFieldset.js +28 -26
- package/dist/features/preview/components/PagePicker.js +15 -8
- package/dist/features/preview/components/PageTree.js +337 -351
- package/dist/features/preview/components/PeekedBlock.js +38 -26
- package/dist/features/preview/components/PreviewPanel.js +16 -2
- package/dist/features/preview/components/PreviewSideSheet.js +26 -42
- package/dist/features/preview/components/RepeatableItemsList.js +7 -7
- package/dist/features/preview/previewStore.js +7 -7
- package/dist/features/provider/CamoxProvider.js +41 -9
- package/dist/features/routes/ogRoute.js +2 -2
- package/dist/features/routes/pageRoute.js +1 -1
- package/dist/features/studio/components/EnvironmentMenu.js +2 -2
- package/dist/features/studio/components/UserButton.js +49 -34
- package/dist/features/vite/blockBoilerplate.js +2 -1
- package/dist/features/vite/definitionsSync.js +53 -22
- package/dist/features/vite/routeGeneration.js +1 -0
- package/dist/features/vite/vite.js +51 -7
- package/dist/lib/auth.js +6 -4
- package/dist/lib/use-project-room.js +25 -13
- package/dist/studio-overlays.css +34 -0
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox-layout/SKILL.md +34 -30
|
@@ -8,45 +8,56 @@ interface OgImageParams {
|
|
|
8
8
|
projectName: string;
|
|
9
9
|
}
|
|
10
10
|
interface LayoutBlockData {
|
|
11
|
-
_id:
|
|
11
|
+
_id: number;
|
|
12
12
|
type: string;
|
|
13
13
|
content: Record<string, unknown>;
|
|
14
14
|
settings?: Record<string, unknown>;
|
|
15
15
|
position: string;
|
|
16
16
|
}
|
|
17
17
|
/** Minimal block interface — avoids importing the full generic Block type. */
|
|
18
|
-
interface LayoutBlock {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
content: Record<string, unknown>;
|
|
30
|
-
settings: Record<string, unknown>;
|
|
31
|
-
repeatableItems: Array<{
|
|
32
|
-
tempId: string;
|
|
33
|
-
parentTempId: string | null;
|
|
34
|
-
fieldName: string;
|
|
35
|
-
content: Record<string, unknown>;
|
|
36
|
-
position: string;
|
|
18
|
+
interface LayoutBlock<TLayoutOnly extends boolean = boolean> {
|
|
19
|
+
_internal: {
|
|
20
|
+
id: string;
|
|
21
|
+
layoutOnly: TLayoutOnly;
|
|
22
|
+
Component: React.ComponentType<{
|
|
23
|
+
blockData: any;
|
|
24
|
+
mode: "site" | "peek" | "layout";
|
|
25
|
+
isFirstBlock?: boolean;
|
|
26
|
+
showAddBlockTop?: boolean;
|
|
27
|
+
showAddBlockBottom?: boolean;
|
|
28
|
+
addBlockAfterPosition?: string | null;
|
|
37
29
|
}>;
|
|
30
|
+
getInitialBundle: () => {
|
|
31
|
+
content: Record<string, unknown>;
|
|
32
|
+
settings: Record<string, unknown>;
|
|
33
|
+
repeatableItems: Array<{
|
|
34
|
+
tempId: string;
|
|
35
|
+
parentTempId: string | null;
|
|
36
|
+
fieldName: string;
|
|
37
|
+
content: Record<string, unknown>;
|
|
38
|
+
position: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
38
41
|
};
|
|
39
42
|
}
|
|
40
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Per-element validators that produce a human-readable error string when a block
|
|
45
|
+
* is in the wrong slot. We use mapped types (instead of `LayoutBlock<true>[]` /
|
|
46
|
+
* `LayoutBlock<false>[]`) so TypeScript reports
|
|
47
|
+
* `Type 'X' is not assignable to type '❌ Camox: ...'`
|
|
48
|
+
* instead of the unreadable structural diff on the full Block shape.
|
|
49
|
+
*/
|
|
50
|
+
type ValidateLayoutOnlyBlocks<T extends readonly LayoutBlock[]> = { [K in keyof T]: T[K] extends LayoutBlock<true> ? T[K] : "❌ Camox: blocks in `blocks.before` and `blocks.after` must be defined with `layoutOnly: true`. Add `layoutOnly: true` to this block's `createBlock` options." };
|
|
51
|
+
type ValidatePageContentBlocks<T extends readonly LayoutBlock[]> = { [K in keyof T]: T[K] extends LayoutBlock<true> ? "❌ Camox: blocks in `blocks.initial` must NOT be `layoutOnly: true` — `initial` is for page-content blocks. Remove `layoutOnly: true` from this block's `createBlock` options." : T[K] };
|
|
52
|
+
interface CreateLayoutOptions<TBefore extends readonly LayoutBlock[], TAfter extends readonly LayoutBlock[], TInitial extends readonly LayoutBlock[]> {
|
|
41
53
|
id: string;
|
|
42
54
|
title: string;
|
|
43
55
|
description: string;
|
|
44
56
|
blocks: {
|
|
45
|
-
before:
|
|
46
|
-
after:
|
|
57
|
+
before: ValidateLayoutOnlyBlocks<TBefore>;
|
|
58
|
+
after: ValidateLayoutOnlyBlocks<TAfter>; /** Ordered list of blocks to create on the initial page when a project is first set up. */
|
|
59
|
+
initial?: ValidatePageContentBlocks<TInitial>;
|
|
47
60
|
};
|
|
48
|
-
/** Ordered list of blocks to create on the initial page when a project is first set up. */
|
|
49
|
-
initialBlocks?: LayoutBlock[];
|
|
50
61
|
component: React.ComponentType<{
|
|
51
62
|
children: React.ReactNode;
|
|
52
63
|
}>;
|
|
@@ -57,64 +68,73 @@ interface CreateLayoutOptions {
|
|
|
57
68
|
}) => string;
|
|
58
69
|
buildOgImage?: (params: OgImageParams) => React.ReactElement;
|
|
59
70
|
}
|
|
60
|
-
declare function createLayout(options: CreateLayoutOptions): {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
declare function createLayout<const TBefore extends readonly LayoutBlock[], const TAfter extends readonly LayoutBlock[], const TInitial extends readonly LayoutBlock[] = []>(options: CreateLayoutOptions<TBefore, TAfter, TInitial>): {
|
|
72
|
+
BeforeBlocks: {
|
|
73
|
+
(): _$react_jsx_runtime0.JSX.Element;
|
|
74
|
+
displayName: string;
|
|
75
|
+
};
|
|
76
|
+
AfterBlocks: {
|
|
77
|
+
(): _$react_jsx_runtime0.JSX.Element;
|
|
78
|
+
displayName: string;
|
|
79
|
+
};
|
|
80
|
+
_internal: {
|
|
81
|
+
id: string;
|
|
82
|
+
title: string;
|
|
83
|
+
description: string;
|
|
84
|
+
buildMetaTitle: (params: {
|
|
85
|
+
pageMetaTitle: string;
|
|
86
|
+
projectName: string;
|
|
87
|
+
pageFullPath: string;
|
|
88
|
+
}) => string;
|
|
89
|
+
buildOgImage: ((params: OgImageParams) => Promise<Response>) | undefined;
|
|
90
|
+
blockDefinitions: ({
|
|
91
|
+
type: string;
|
|
78
92
|
content: Record<string, unknown>;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
settings: Record<string, unknown>;
|
|
94
|
+
repeatableItems: {
|
|
95
|
+
tempId: string;
|
|
96
|
+
parentTempId: string | null;
|
|
97
|
+
fieldName: string;
|
|
98
|
+
content: Record<string, unknown>;
|
|
99
|
+
position: string;
|
|
100
|
+
}[];
|
|
101
|
+
placement: "before";
|
|
102
|
+
} | {
|
|
103
|
+
type: string;
|
|
90
104
|
content: Record<string, unknown>;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
settings: Record<string, unknown>;
|
|
106
|
+
repeatableItems: {
|
|
107
|
+
tempId: string;
|
|
108
|
+
parentTempId: string | null;
|
|
109
|
+
fieldName: string;
|
|
110
|
+
content: Record<string, unknown>;
|
|
111
|
+
position: string;
|
|
112
|
+
}[];
|
|
113
|
+
placement: "after";
|
|
114
|
+
})[];
|
|
115
|
+
initialBlockBundles: {
|
|
116
|
+
type: string;
|
|
103
117
|
content: Record<string, unknown>;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
settings: Record<string, unknown>;
|
|
119
|
+
repeatableItems: {
|
|
120
|
+
tempId: string;
|
|
121
|
+
parentTempId: string | null;
|
|
122
|
+
fieldName: string;
|
|
123
|
+
content: Record<string, unknown>;
|
|
124
|
+
position: string;
|
|
125
|
+
}[];
|
|
126
|
+
}[] | undefined;
|
|
127
|
+
component: React.ComponentType<{
|
|
128
|
+
children: React.ReactNode;
|
|
129
|
+
}>;
|
|
130
|
+
Provider: ({
|
|
131
|
+
layoutBlocks,
|
|
132
|
+
children
|
|
133
|
+
}: {
|
|
134
|
+
layoutBlocks: Record<string, LayoutBlockData>;
|
|
135
|
+
children: React.ReactNode;
|
|
136
|
+
}) => _$react_jsx_runtime0.JSX.Element;
|
|
137
|
+
};
|
|
118
138
|
};
|
|
119
139
|
type Layout = ReturnType<typeof createLayout>;
|
|
120
140
|
//#endregion
|
|
@@ -1,55 +1,80 @@
|
|
|
1
|
+
import { BlockErrorBoundary } from "../features/preview/components/BlockErrorBoundary.js";
|
|
1
2
|
import { c } from "react/compiler-runtime";
|
|
2
3
|
import * as React from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
5
|
|
|
5
6
|
//#region src/core/createLayout.tsx
|
|
6
|
-
function toPascalCase(str) {
|
|
7
|
-
return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
8
|
-
}
|
|
9
7
|
function createLayout(options) {
|
|
10
8
|
const LayoutContext = React.createContext(null);
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
9
|
+
const beforeBlocks = options.blocks.before;
|
|
10
|
+
const afterBlocks = options.blocks.after;
|
|
11
|
+
const initialBlocks = options.blocks.initial;
|
|
12
|
+
const BeforeBlocks = () => {
|
|
13
|
+
const $ = c(4);
|
|
14
|
+
const ctx = React.use(LayoutContext);
|
|
15
|
+
if (!ctx) throw new Error(`Layout "${options.id}" BeforeBlocks must be rendered inside its Provider`);
|
|
16
|
+
let t0;
|
|
17
|
+
if ($[0] !== ctx) {
|
|
18
|
+
t0 = beforeBlocks.map((block, i) => {
|
|
19
|
+
const blockData = ctx.layoutBlocks[block._internal.id];
|
|
20
|
+
if (!blockData) return null;
|
|
21
|
+
const isLastBefore = i === beforeBlocks.length - 1;
|
|
22
|
+
return /* @__PURE__ */ jsx(BlockErrorBoundary, {
|
|
23
|
+
blockId: blockData._id,
|
|
24
|
+
blockType: blockData.type,
|
|
25
|
+
children: /* @__PURE__ */ jsx(block._internal.Component, {
|
|
26
|
+
blockData,
|
|
27
|
+
mode: "layout",
|
|
28
|
+
showAddBlockBottom: isLastBefore || void 0,
|
|
29
|
+
addBlockAfterPosition: isLastBefore ? "" : void 0
|
|
30
|
+
})
|
|
31
|
+
}, block._internal.id);
|
|
32
|
+
});
|
|
33
|
+
$[0] = ctx;
|
|
34
|
+
$[1] = t0;
|
|
35
|
+
} else t0 = $[1];
|
|
36
|
+
let t1;
|
|
37
|
+
if ($[2] !== t0) {
|
|
38
|
+
t1 = /* @__PURE__ */ jsx(Fragment, { children: t0 });
|
|
39
|
+
$[2] = t0;
|
|
40
|
+
$[3] = t1;
|
|
41
|
+
} else t1 = $[3];
|
|
42
|
+
return t1;
|
|
43
|
+
};
|
|
44
|
+
BeforeBlocks.displayName = `LayoutBeforeBlocks(${options.id})`;
|
|
45
|
+
const AfterBlocks = () => {
|
|
46
|
+
const $ = c(4);
|
|
47
|
+
const ctx = React.use(LayoutContext);
|
|
48
|
+
if (!ctx) throw new Error(`Layout "${options.id}" AfterBlocks must be rendered inside its Provider`);
|
|
49
|
+
let t0;
|
|
50
|
+
if ($[0] !== ctx) {
|
|
51
|
+
t0 = afterBlocks.map((block, i) => {
|
|
52
|
+
const blockData = ctx.layoutBlocks[block._internal.id];
|
|
53
|
+
if (!blockData) return null;
|
|
54
|
+
const isFirstAfter = i === 0;
|
|
55
|
+
return /* @__PURE__ */ jsx(BlockErrorBoundary, {
|
|
56
|
+
blockId: blockData._id,
|
|
57
|
+
blockType: blockData.type,
|
|
58
|
+
children: /* @__PURE__ */ jsx(block._internal.Component, {
|
|
59
|
+
blockData,
|
|
60
|
+
mode: "layout",
|
|
61
|
+
showAddBlockTop: isFirstAfter || void 0,
|
|
62
|
+
addBlockAfterPosition: isFirstAfter ? null : void 0
|
|
63
|
+
})
|
|
64
|
+
}, block._internal.id);
|
|
65
|
+
});
|
|
66
|
+
$[0] = ctx;
|
|
67
|
+
$[1] = t0;
|
|
68
|
+
} else t0 = $[1];
|
|
69
|
+
let t1;
|
|
70
|
+
if ($[2] !== t0) {
|
|
71
|
+
t1 = /* @__PURE__ */ jsx(Fragment, { children: t0 });
|
|
72
|
+
$[2] = t0;
|
|
73
|
+
$[3] = t1;
|
|
74
|
+
} else t1 = $[3];
|
|
75
|
+
return t1;
|
|
76
|
+
};
|
|
77
|
+
AfterBlocks.displayName = `LayoutAfterBlocks(${options.id})`;
|
|
53
78
|
const Provider = (t0) => {
|
|
54
79
|
const $ = c(5);
|
|
55
80
|
const { layoutBlocks, children } = t0;
|
|
@@ -72,19 +97,19 @@ function createLayout(options) {
|
|
|
72
97
|
} else t2 = $[4];
|
|
73
98
|
return t2;
|
|
74
99
|
};
|
|
75
|
-
const blockDefinitions = [...
|
|
76
|
-
const bundle = block.getInitialBundle();
|
|
100
|
+
const blockDefinitions = [...beforeBlocks.map((block) => {
|
|
101
|
+
const bundle = block._internal.getInitialBundle();
|
|
77
102
|
return {
|
|
78
|
-
type: block.id,
|
|
103
|
+
type: block._internal.id,
|
|
79
104
|
content: bundle.content,
|
|
80
105
|
settings: bundle.settings,
|
|
81
106
|
repeatableItems: bundle.repeatableItems,
|
|
82
107
|
placement: "before"
|
|
83
108
|
};
|
|
84
|
-
}), ...
|
|
85
|
-
const bundle = block.getInitialBundle();
|
|
109
|
+
}), ...afterBlocks.map((block) => {
|
|
110
|
+
const bundle = block._internal.getInitialBundle();
|
|
86
111
|
return {
|
|
87
|
-
type: block.id,
|
|
112
|
+
type: block._internal.id,
|
|
88
113
|
content: bundle.content,
|
|
89
114
|
settings: bundle.settings,
|
|
90
115
|
repeatableItems: bundle.repeatableItems,
|
|
@@ -98,26 +123,29 @@ function createLayout(options) {
|
|
|
98
123
|
height: 630
|
|
99
124
|
});
|
|
100
125
|
} : void 0;
|
|
101
|
-
const initialBlockBundles =
|
|
102
|
-
const bundle = block.getInitialBundle();
|
|
126
|
+
const initialBlockBundles = initialBlocks?.map((block) => {
|
|
127
|
+
const bundle = block._internal.getInitialBundle();
|
|
103
128
|
return {
|
|
104
|
-
type: block.id,
|
|
129
|
+
type: block._internal.id,
|
|
105
130
|
content: bundle.content,
|
|
106
131
|
settings: bundle.settings,
|
|
107
132
|
repeatableItems: bundle.repeatableItems
|
|
108
133
|
};
|
|
109
134
|
});
|
|
110
135
|
return {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
BeforeBlocks,
|
|
137
|
+
AfterBlocks,
|
|
138
|
+
_internal: {
|
|
139
|
+
id: options.id,
|
|
140
|
+
title: options.title,
|
|
141
|
+
description: options.description,
|
|
142
|
+
buildMetaTitle: options.buildMetaTitle,
|
|
143
|
+
buildOgImage,
|
|
144
|
+
blockDefinitions,
|
|
145
|
+
initialBlockBundles,
|
|
146
|
+
component: options.component,
|
|
147
|
+
Provider
|
|
148
|
+
}
|
|
121
149
|
};
|
|
122
150
|
}
|
|
123
151
|
|