camox 0.11.0 → 0.13.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/InlineLexicalEditor.js +24 -1
- package/dist/core/components/lexical/SidebarLexicalEditor.js +25 -1
- package/dist/core/createBlock.d.ts +5 -2
- package/dist/core/lib/contentType.d.ts +8 -5
- package/dist/core/lib/contentType.js +2 -2
- package/dist/core/lib/lexicalReact.js +8 -2
- package/dist/core/lib/lexicalState.js +13 -1
- package/dist/features/preview/CamoxPreview.js +3 -3
- package/dist/features/preview/components/EditPageModal.js +1 -1
- package/dist/features/preview/components/{TextFormatToolbar.js → FieldToolbar.js} +73 -44
- package/dist/features/preview/components/PageContentSheet.js +47 -48
- package/dist/features/preview/components/PeekedBlock.js +1 -1
- package/dist/features/preview/components/PreviewPanel.js +3 -3
- package/dist/features/preview/components/PreviewToolbar.js +1 -1
- package/dist/features/preview/previewStore.js +2 -5
- package/dist/features/provider/CamoxProvider.js +28 -14
- package/dist/features/studio/components/EnvironmentMenu.js +6 -5
- package/dist/features/studio/components/Navbar.js +1 -1
- package/dist/features/vite/definitionsSync.js +14 -3
- package/dist/features/vite/routeGeneration.js +8 -8
- package/dist/features/vite/vite.js +78 -62
- package/dist/studio-overlays.css +1 -2
- package/dist/studio.css +1 -1
- package/package.json +11 -5
- package/skills/camox-block/SKILL.md +26 -21
|
@@ -14,6 +14,7 @@ const EnvironmentMenu = () => {
|
|
|
14
14
|
const authCtx = React.useContext(AuthContext);
|
|
15
15
|
if (!authCtx?.environmentName) return null;
|
|
16
16
|
const isProduction = authCtx.environmentName === "production";
|
|
17
|
+
const label = isProduction ? "PROD" : "DEV";
|
|
17
18
|
const badgeClassName = isProduction ? "bg-green-100 text-green-800 border border-green-300 hover:bg-green-100 dark:bg-green-900 dark:text-green-300 dark:border-green-700 dark:hover:bg-green-900 font-mono text-xs" : "bg-yellow-100 text-yellow-800 border border-yellow-300 hover:bg-yellow-100 dark:bg-yellow-900 dark:text-yellow-300 dark:border-yellow-700 dark:hover:bg-yellow-900 font-mono text-xs";
|
|
18
19
|
let t0;
|
|
19
20
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
@@ -24,14 +25,14 @@ const EnvironmentMenu = () => {
|
|
|
24
25
|
$[0] = t0;
|
|
25
26
|
} else t0 = $[0];
|
|
26
27
|
let t1;
|
|
27
|
-
if ($[1] !==
|
|
28
|
+
if ($[1] !== badgeClassName || $[2] !== label) {
|
|
28
29
|
t1 = /* @__PURE__ */ jsx(Badge, {
|
|
29
30
|
variant: "secondary",
|
|
30
31
|
className: badgeClassName,
|
|
31
|
-
children:
|
|
32
|
+
children: label
|
|
32
33
|
});
|
|
33
|
-
$[1] =
|
|
34
|
-
$[2] =
|
|
34
|
+
$[1] = badgeClassName;
|
|
35
|
+
$[2] = label;
|
|
35
36
|
$[3] = t1;
|
|
36
37
|
} else t1 = $[3];
|
|
37
38
|
let t2;
|
|
@@ -61,7 +62,7 @@ const EnvironmentMenu = () => {
|
|
|
61
62
|
children: "You are viewing the production environment."
|
|
62
63
|
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
|
|
63
64
|
className: "text-sm",
|
|
64
|
-
children: "This environment is your
|
|
65
|
+
children: "This environment is your personal space to iterate on content and data structures. It won't affect your teammates or production."
|
|
65
66
|
}), /* @__PURE__ */ jsx("p", {
|
|
66
67
|
className: "text-muted-foreground text-xs",
|
|
67
68
|
children: "You will be able to pull and push data between environments from here."
|
|
@@ -61,7 +61,7 @@ const Navbar = () => {
|
|
|
61
61
|
const t4 = useIsPreviewSheetOpen() ? "opacity-100" : "opacity-0";
|
|
62
62
|
let t5;
|
|
63
63
|
if ($[7] !== t4) {
|
|
64
|
-
t5 = cn("absolute top-0 left-0 w-full h-[calc(100%+2px)] bg-black/
|
|
64
|
+
t5 = cn("absolute top-0 left-0 w-full h-[calc(100%+2px)] bg-black/66 transition-opacity z-10 will-change-auto pointer-events-none", t4);
|
|
65
65
|
$[7] = t4;
|
|
66
66
|
$[8] = t5;
|
|
67
67
|
} else t5 = $[8];
|
|
@@ -11,8 +11,14 @@ const SYNC_DEBOUNCE_DELAY_MS = 100;
|
|
|
11
11
|
function throwIfSyncAuthError(error) {
|
|
12
12
|
if (error instanceof Error && error.name === "ORPCError" && error.message.toLowerCase().includes("unauthorized")) throw new Error("[camox] Definition sync failed: invalid syncSecret.");
|
|
13
13
|
}
|
|
14
|
+
function isNotFoundError(error) {
|
|
15
|
+
return error instanceof Error && error.name === "ORPCError" && error.message.toLowerCase().includes("not found");
|
|
16
|
+
}
|
|
17
|
+
function throwUnknownEnvironmentError(environmentName) {
|
|
18
|
+
throw new Error(`[camox] Environment "${environmentName}" does not exist. CAMOX_ENV must be "production" or a dev environment previously created by running the dev server while authenticated. Run \`npx camox login\` if needed.`);
|
|
19
|
+
}
|
|
14
20
|
async function syncDefinitionsToApi(options) {
|
|
15
|
-
const { camoxApp, projectSlug, apiUrl, syncSecret, environmentName, logger } = options;
|
|
21
|
+
const { camoxApp, projectSlug, apiUrl, syncSecret, environmentName, autoCreate, logger } = options;
|
|
16
22
|
const client = createServerApiClient(apiUrl, environmentName);
|
|
17
23
|
const blocks = camoxApp.getBlocks();
|
|
18
24
|
const layoutDefinitions = camoxApp.getSerializableLayoutDefinitions();
|
|
@@ -33,13 +39,15 @@ async function syncDefinitionsToApi(options) {
|
|
|
33
39
|
environmentCreated = (await client.blockDefinitions.sync({
|
|
34
40
|
projectSlug,
|
|
35
41
|
syncSecret,
|
|
42
|
+
autoCreate,
|
|
36
43
|
definitions
|
|
37
44
|
})).environmentCreated;
|
|
38
45
|
} catch (error) {
|
|
39
46
|
throwIfSyncAuthError(error);
|
|
47
|
+
if (!autoCreate && isNotFoundError(error)) throwUnknownEnvironmentError(environmentName);
|
|
40
48
|
throw error;
|
|
41
49
|
}
|
|
42
|
-
if (environmentCreated
|
|
50
|
+
if (environmentCreated) logger.info(`[camox] Created empty environment "${environmentName}"`, { timestamp: true });
|
|
43
51
|
logger.info(`[camox] Synced ${definitions.length} block definition${definitions.length === 1 ? "" : "s"}`, { timestamp: true });
|
|
44
52
|
if (layoutDefinitions.length > 0) {
|
|
45
53
|
let layoutSyncResults;
|
|
@@ -47,10 +55,12 @@ async function syncDefinitionsToApi(options) {
|
|
|
47
55
|
layoutSyncResults = await client.layouts.sync({
|
|
48
56
|
projectSlug,
|
|
49
57
|
syncSecret,
|
|
58
|
+
autoCreate,
|
|
50
59
|
layouts: layoutDefinitions
|
|
51
60
|
});
|
|
52
61
|
} catch (error) {
|
|
53
62
|
throwIfSyncAuthError(error);
|
|
63
|
+
if (!autoCreate && isNotFoundError(error)) throwUnknownEnvironmentError(environmentName);
|
|
54
64
|
throw error;
|
|
55
65
|
}
|
|
56
66
|
logger.info(`[camox] Synced ${layoutDefinitions.length} layout${layoutDefinitions.length === 1 ? "" : "s"} to Camox API`, { timestamp: true });
|
|
@@ -119,7 +129,7 @@ async function ssrLoadModule(server, modulePath) {
|
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
async function syncDefinitions(server, options) {
|
|
122
|
-
const { projectSlug, syncSecret, apiUrl, environmentName } = options;
|
|
132
|
+
const { projectSlug, syncSecret, apiUrl, environmentName, autoCreate } = options;
|
|
123
133
|
const blocksDir = path.resolve(server.config.root, "src/camox/blocks");
|
|
124
134
|
const client = createServerApiClient(apiUrl, environmentName);
|
|
125
135
|
async function performInitialSync() {
|
|
@@ -136,6 +146,7 @@ async function syncDefinitions(server, options) {
|
|
|
136
146
|
apiUrl,
|
|
137
147
|
syncSecret,
|
|
138
148
|
environmentName,
|
|
149
|
+
autoCreate,
|
|
139
150
|
logger: server.config.logger
|
|
140
151
|
});
|
|
141
152
|
}
|
|
@@ -15,7 +15,7 @@ const HEADER = `/* =============================================================
|
|
|
15
15
|
// @ts-nocheck
|
|
16
16
|
|
|
17
17
|
`;
|
|
18
|
-
function generateCamoxLayout(authenticationUrl
|
|
18
|
+
function generateCamoxLayout(authenticationUrl) {
|
|
19
19
|
return HEADER + `import { Outlet, createFileRoute } from "@tanstack/react-router";
|
|
20
20
|
import { CamoxProvider } from "camox/CamoxProvider";
|
|
21
21
|
import { camoxApp } from "@/camox/app";
|
|
@@ -26,14 +26,14 @@ export const Route = createFileRoute("/_camox")({
|
|
|
26
26
|
|
|
27
27
|
function CamoxPathlessLayout() {
|
|
28
28
|
return (
|
|
29
|
-
<CamoxProvider camoxApp={camoxApp} authenticationUrl="${authenticationUrl}" apiUrl=
|
|
29
|
+
<CamoxProvider camoxApp={camoxApp} authenticationUrl="${authenticationUrl}" apiUrl={__CAMOX_API_URL__} projectSlug={__CAMOX_PROJECT_SLUG__} environmentName={__CAMOX_ENVIRONMENT_NAME__}>
|
|
30
30
|
<Outlet />
|
|
31
31
|
</CamoxProvider>
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
`;
|
|
35
35
|
}
|
|
36
|
-
function generatePageRoute(
|
|
36
|
+
function generatePageRoute() {
|
|
37
37
|
return HEADER + `import { createFileRoute } from "@tanstack/react-router";
|
|
38
38
|
import {
|
|
39
39
|
createMarkdownMiddleware,
|
|
@@ -43,8 +43,8 @@ import {
|
|
|
43
43
|
} from "camox/_internal/pageRoute";
|
|
44
44
|
import { camoxApp } from "@/camox/app";
|
|
45
45
|
|
|
46
|
-
const markdownMiddleware = createMarkdownMiddleware(
|
|
47
|
-
const loader = createPageLoader(
|
|
46
|
+
const markdownMiddleware = createMarkdownMiddleware(__CAMOX_API_URL__, __CAMOX_PROJECT_SLUG__, __CAMOX_ENVIRONMENT_NAME__);
|
|
47
|
+
const loader = createPageLoader(__CAMOX_API_URL__, __CAMOX_PROJECT_SLUG__, __CAMOX_ENVIRONMENT_NAME__);
|
|
48
48
|
const head = createPageHead(camoxApp);
|
|
49
49
|
|
|
50
50
|
export const Route = createFileRoute("/_camox/$")({
|
|
@@ -134,16 +134,16 @@ function RouteComponent() {
|
|
|
134
134
|
}
|
|
135
135
|
`;
|
|
136
136
|
}
|
|
137
|
-
function getRouteFileEntries({ routesDir, authenticationUrl
|
|
137
|
+
function getRouteFileEntries({ routesDir, authenticationUrl }) {
|
|
138
138
|
const camoxDir = resolve(routesDir, "_camox");
|
|
139
139
|
return [
|
|
140
140
|
{
|
|
141
141
|
path: resolve(routesDir, "_camox.tsx"),
|
|
142
|
-
content: generateCamoxLayout(authenticationUrl
|
|
142
|
+
content: generateCamoxLayout(authenticationUrl)
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
path: resolve(camoxDir, "$.tsx"),
|
|
146
|
-
content: generatePageRoute(
|
|
146
|
+
content: generatePageRoute()
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
path: resolve(camoxDir, "og.tsx"),
|
|
@@ -8,6 +8,7 @@ import { homedir } from "node:os";
|
|
|
8
8
|
import { dirname, join, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { createServer } from "vite";
|
|
11
|
+
import { z } from "zod";
|
|
11
12
|
|
|
12
13
|
//#region src/features/vite/vite.ts
|
|
13
14
|
const sdkRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
@@ -18,18 +19,33 @@ const RESOLVED_VIRTUAL_OVERLAY_CSS = "\0" + VIRTUAL_OVERLAY_CSS;
|
|
|
18
19
|
const PRODUCTION_API_URL = "https://api.camox.ai";
|
|
19
20
|
/** Authentication URL to use for Camox authentication (production Camox web app) */
|
|
20
21
|
const DEFAULT_AUTHENTICATION_URL = "https://camox.ai";
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const authTokenSchema = z.object({
|
|
23
|
+
token: z.string(),
|
|
24
|
+
name: z.string(),
|
|
25
|
+
email: z.string()
|
|
26
|
+
});
|
|
27
|
+
const authFileSchema = z.record(z.string(), authTokenSchema);
|
|
28
|
+
function readAuthEmail(authenticationUrl) {
|
|
23
29
|
const authFile = join(homedir(), ".camox", "auth.json");
|
|
24
30
|
const key = authenticationUrl.replace(/\/+$/, "");
|
|
25
|
-
let auth;
|
|
26
31
|
try {
|
|
27
|
-
|
|
32
|
+
const raw = JSON.parse(readFileSync(authFile, "utf-8"));
|
|
33
|
+
return authFileSchema.parse(raw)[key]?.email ?? null;
|
|
28
34
|
} catch {
|
|
29
|
-
|
|
35
|
+
return null;
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
}
|
|
38
|
+
function resolveEnvironmentName(command, authenticationUrl) {
|
|
39
|
+
if (command === "serve") {
|
|
40
|
+
const email = readAuthEmail(authenticationUrl);
|
|
41
|
+
if (!email) throw new Error("Camox: not authenticated. Run `npx camox login` to create your personal dev environment.");
|
|
42
|
+
return `dev:${email}`;
|
|
43
|
+
}
|
|
44
|
+
const envFromProcess = process.env.CAMOX_ENV;
|
|
45
|
+
if (envFromProcess) return envFromProcess;
|
|
46
|
+
const email = readAuthEmail(authenticationUrl);
|
|
47
|
+
const suggestion = email ? ` CAMOX_ENV=dev:${email} (your personal dev environment)\n CAMOX_ENV=production (release build)` : ` CAMOX_ENV=production (release build)\n\nIf you want to build against a dev environment, run \`npx camox login\` first.`;
|
|
48
|
+
throw new Error(`Camox: CAMOX_ENV is required on build. Set it to one of:\n${suggestion}`);
|
|
33
49
|
}
|
|
34
50
|
function camox(options) {
|
|
35
51
|
const apiUrl = options._internal?.apiUrl ?? PRODUCTION_API_URL;
|
|
@@ -65,7 +81,7 @@ function camox(options) {
|
|
|
65
81
|
},
|
|
66
82
|
config(_config, env) {
|
|
67
83
|
isBuild = env.command === "build";
|
|
68
|
-
environmentName = resolveEnvironmentName(env.command
|
|
84
|
+
environmentName = resolveEnvironmentName(env.command, authenticationUrl);
|
|
69
85
|
return {
|
|
70
86
|
define: {
|
|
71
87
|
__CAMOX_ANALYTICS_DISABLED__: JSON.stringify(!!options.disableAnalytics),
|
|
@@ -74,51 +90,55 @@ function camox(options) {
|
|
|
74
90
|
__CAMOX_API_URL__: JSON.stringify(apiUrl),
|
|
75
91
|
__CAMOX_PROJECT_SLUG__: JSON.stringify(options.projectSlug)
|
|
76
92
|
},
|
|
77
|
-
optimizeDeps: {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
93
|
+
optimizeDeps: { include: [
|
|
94
|
+
"react",
|
|
95
|
+
"react-dom",
|
|
96
|
+
"react-dom/client",
|
|
97
|
+
"react/jsx-runtime",
|
|
98
|
+
"react/jsx-dev-runtime",
|
|
99
|
+
"camox > @base-ui/react/accordion",
|
|
100
|
+
"camox > @base-ui/react/alert-dialog",
|
|
101
|
+
"camox > @base-ui/react/avatar",
|
|
102
|
+
"camox > @base-ui/react/dialog",
|
|
103
|
+
"camox > @base-ui/react/input",
|
|
104
|
+
"camox > @base-ui/react/menu",
|
|
105
|
+
"camox > @base-ui/react/merge-props",
|
|
106
|
+
"camox > @base-ui/react/popover",
|
|
107
|
+
"camox > @base-ui/react/select",
|
|
108
|
+
"camox > @base-ui/react/separator",
|
|
109
|
+
"camox > @base-ui/react/switch",
|
|
110
|
+
"camox > @base-ui/react/tabs",
|
|
111
|
+
"camox > @base-ui/react/toggle",
|
|
112
|
+
"camox > @base-ui/react/tooltip",
|
|
113
|
+
"camox > @base-ui/react/use-render",
|
|
114
|
+
"camox > @dnd-kit/core",
|
|
115
|
+
"camox > @dnd-kit/modifiers",
|
|
116
|
+
"camox > @dnd-kit/sortable",
|
|
117
|
+
"camox > @dnd-kit/utilities",
|
|
118
|
+
"camox > @lexical/react/LexicalComposer",
|
|
119
|
+
"camox > @lexical/react/LexicalComposerContext",
|
|
120
|
+
"camox > @lexical/react/LexicalContentEditable",
|
|
121
|
+
"camox > @lexical/react/LexicalOnChangePlugin",
|
|
122
|
+
"camox > @lexical/react/LexicalRichTextPlugin",
|
|
123
|
+
"camox > @orpc/client",
|
|
124
|
+
"camox > @orpc/client/fetch",
|
|
125
|
+
"camox > @orpc/tanstack-query",
|
|
126
|
+
"camox > @sinclair/typebox",
|
|
127
|
+
"camox > @takumi-rs/image-response",
|
|
128
|
+
"camox > @tanstack/react-form",
|
|
129
|
+
"camox > @xstate/store",
|
|
130
|
+
"camox > @xstate/store/react",
|
|
131
|
+
"camox > @camox/ui > cmdk",
|
|
132
|
+
"camox > fractional-indexing",
|
|
133
|
+
"camox > lexical",
|
|
134
|
+
"camox > posthog-js",
|
|
135
|
+
"camox > shiki",
|
|
136
|
+
"camox > @camox/ui > sonner",
|
|
137
|
+
"camox > @camox/ui > lucide-react",
|
|
138
|
+
"camox > lucide-react",
|
|
139
|
+
"camox > @tanstack/react-query-devtools/production",
|
|
140
|
+
"camox > partysocket/react"
|
|
141
|
+
] }
|
|
122
142
|
};
|
|
123
143
|
},
|
|
124
144
|
configResolved(config) {
|
|
@@ -128,10 +148,7 @@ function camox(options) {
|
|
|
128
148
|
generateAppFile(config.root);
|
|
129
149
|
generateRouteFiles({
|
|
130
150
|
routesDir,
|
|
131
|
-
authenticationUrl
|
|
132
|
-
apiUrl,
|
|
133
|
-
projectSlug: options.projectSlug,
|
|
134
|
-
environmentName
|
|
151
|
+
authenticationUrl
|
|
135
152
|
});
|
|
136
153
|
generateSkillFiles(config.root);
|
|
137
154
|
}
|
|
@@ -146,10 +163,7 @@ function camox(options) {
|
|
|
146
163
|
watchRouteFiles({
|
|
147
164
|
server,
|
|
148
165
|
routesDir,
|
|
149
|
-
authenticationUrl
|
|
150
|
-
apiUrl,
|
|
151
|
-
projectSlug: options.projectSlug,
|
|
152
|
-
environmentName
|
|
166
|
+
authenticationUrl
|
|
153
167
|
});
|
|
154
168
|
watchSkillFiles(server, server.config.root);
|
|
155
169
|
watchNewBlockFiles(server);
|
|
@@ -159,7 +173,8 @@ function camox(options) {
|
|
|
159
173
|
projectSlug: options.projectSlug,
|
|
160
174
|
syncSecret: options.syncSecret,
|
|
161
175
|
apiUrl,
|
|
162
|
-
environmentName
|
|
176
|
+
environmentName,
|
|
177
|
+
autoCreate: true
|
|
163
178
|
});
|
|
164
179
|
});
|
|
165
180
|
},
|
|
@@ -182,6 +197,7 @@ function camox(options) {
|
|
|
182
197
|
apiUrl,
|
|
183
198
|
syncSecret: options.syncSecret,
|
|
184
199
|
environmentName,
|
|
200
|
+
autoCreate: false,
|
|
185
201
|
logger: resolvedConfig.logger
|
|
186
202
|
});
|
|
187
203
|
} finally {
|
package/dist/studio-overlays.css
CHANGED
|
@@ -118,8 +118,7 @@
|
|
|
118
118
|
.camox-sheet-overlay {
|
|
119
119
|
position: absolute;
|
|
120
120
|
inset: 0;
|
|
121
|
-
background: rgba(0, 0, 0, 0.
|
|
122
|
-
backdrop-filter: blur(4px);
|
|
121
|
+
background: rgba(0, 0, 0, 0.66);
|
|
123
122
|
opacity: 0;
|
|
124
123
|
transition: opacity 0.3s ease-in-out;
|
|
125
124
|
pointer-events: none;
|