camox 0.9.0 → 0.9.1
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/features/preview/CamoxPreview.js +1 -1
- package/dist/features/preview/components/EditPageSheet.js +6 -2
- package/dist/features/preview/components/PagePicker.js +12 -5
- package/dist/features/provider/CamoxProvider.js +41 -9
- package/dist/features/studio/components/EnvironmentMenu.js +2 -2
- package/dist/features/studio/components/UserButton.js +3 -4
- package/dist/features/vite/blockBoilerplate.js +2 -1
- package/dist/features/vite/definitionsSync.js +36 -5
- package/dist/lib/auth.js +6 -4
- package/dist/lib/use-project-room.js +25 -13
- package/package.json +4 -4
|
@@ -147,7 +147,7 @@ function usePreviewedPage() {
|
|
|
147
147
|
$[20] = t9;
|
|
148
148
|
} else t9 = $[20];
|
|
149
149
|
const { data: peekedPage } = useQuery(t9);
|
|
150
|
-
return peekedPage ?? currentPage;
|
|
150
|
+
return peekedPagePathname ? peekedPage ?? currentPage : currentPage;
|
|
151
151
|
}
|
|
152
152
|
function _temp(state) {
|
|
153
153
|
return state.context.peekedPagePathname;
|
|
@@ -22,6 +22,7 @@ import { Globe, Info } from "lucide-react";
|
|
|
22
22
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
|
|
23
23
|
import { Spinner } from "@camox/ui/spinner";
|
|
24
24
|
import { useForm } from "@tanstack/react-form";
|
|
25
|
+
import { Alert, AlertDescription, AlertTitle } from "@camox/ui/alert";
|
|
25
26
|
import { Switch } from "@camox/ui/switch";
|
|
26
27
|
|
|
27
28
|
//#region src/features/preview/components/EditPageSheet.tsx
|
|
@@ -149,7 +150,11 @@ const EditPageSheetContent = ({ pageId }) => {
|
|
|
149
150
|
},
|
|
150
151
|
className: "space-y-4",
|
|
151
152
|
children: [
|
|
152
|
-
/* @__PURE__ */
|
|
153
|
+
isRootPage ? /* @__PURE__ */ jsxs(Alert, { children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Info, { className: "size-4" }),
|
|
155
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: "Homepage" }),
|
|
156
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: "You can't change the path of the home page." })
|
|
157
|
+
] }) : /* @__PURE__ */ jsx(form.Field, {
|
|
153
158
|
name: "parentPageId",
|
|
154
159
|
children: (parentField) => /* @__PURE__ */ jsx(form.Field, {
|
|
155
160
|
name: "pathSegment",
|
|
@@ -158,7 +163,6 @@ const EditPageSheetContent = ({ pageId }) => {
|
|
|
158
163
|
onParentPageIdChange: parentField.handleChange,
|
|
159
164
|
pathSegment: pathField.state.value,
|
|
160
165
|
onPathSegmentChange: pathField.handleChange,
|
|
161
|
-
disabled: isRootPage,
|
|
162
166
|
pages,
|
|
163
167
|
excludePageId: page.id
|
|
164
168
|
})
|
|
@@ -30,6 +30,10 @@ const PagePicker = () => {
|
|
|
30
30
|
});
|
|
31
31
|
const { pathname } = useLocation();
|
|
32
32
|
const navigate = useNavigate();
|
|
33
|
+
const closePopover = () => {
|
|
34
|
+
previewStore.send({ type: "clearPeekedPage" });
|
|
35
|
+
setOpen(false);
|
|
36
|
+
};
|
|
33
37
|
const handleDeletePage = async (page) => {
|
|
34
38
|
const displayName = page.metaTitle ?? formatPathSegment(page.pathSegment);
|
|
35
39
|
try {
|
|
@@ -54,7 +58,10 @@ const PagePicker = () => {
|
|
|
54
58
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Popover, {
|
|
55
59
|
open,
|
|
56
60
|
onOpenChange: (value) => {
|
|
57
|
-
|
|
61
|
+
if (!value) {
|
|
62
|
+
closePopover();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
58
65
|
setOpen(value);
|
|
59
66
|
},
|
|
60
67
|
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
@@ -97,7 +104,7 @@ const PagePicker = () => {
|
|
|
97
104
|
hideCheck: true,
|
|
98
105
|
onSelect: () => {
|
|
99
106
|
navigate({ to: page_1.fullPath });
|
|
100
|
-
|
|
107
|
+
closePopover();
|
|
101
108
|
},
|
|
102
109
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
103
110
|
className: "flex min-w-0 flex-1 items-start gap-2",
|
|
@@ -122,7 +129,7 @@ const PagePicker = () => {
|
|
|
122
129
|
type: "openEditPageSheet",
|
|
123
130
|
pageId: page_1.id
|
|
124
131
|
});
|
|
125
|
-
|
|
132
|
+
closePopover();
|
|
126
133
|
},
|
|
127
134
|
onKeyDown: (e_0) => {
|
|
128
135
|
if (e_0.key === "Enter" || e_0.key === " ") {
|
|
@@ -132,7 +139,7 @@ const PagePicker = () => {
|
|
|
132
139
|
type: "openEditPageSheet",
|
|
133
140
|
pageId: page_1.id
|
|
134
141
|
});
|
|
135
|
-
|
|
142
|
+
closePopover();
|
|
136
143
|
}
|
|
137
144
|
},
|
|
138
145
|
children: /* @__PURE__ */ jsx(Pencil, { className: "size-4" })
|
|
@@ -158,7 +165,7 @@ const PagePicker = () => {
|
|
|
158
165
|
/* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
159
166
|
/* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsxs(CommandItem, {
|
|
160
167
|
onSelect: () => {
|
|
161
|
-
|
|
168
|
+
closePopover();
|
|
162
169
|
previewStore.send({ type: "openCreatePageSheet" });
|
|
163
170
|
},
|
|
164
171
|
value: CREATE_PAGE_VALUE,
|
|
@@ -10,7 +10,7 @@ import { useProjectRoom } from "../../lib/use-project-room.js";
|
|
|
10
10
|
import { CommandPalette, useCommandPaletteActions } from "./components/CommandPalette.js";
|
|
11
11
|
import { useAdminShortcuts } from "./useAdminShortcuts.js";
|
|
12
12
|
import { c } from "react/compiler-runtime";
|
|
13
|
-
import { Toaster } from "@camox/ui/toaster";
|
|
13
|
+
import { Toaster, toast } from "@camox/ui/toaster";
|
|
14
14
|
import { useQuery } from "@tanstack/react-query";
|
|
15
15
|
import * as React from "react";
|
|
16
16
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -62,7 +62,7 @@ const AuthenticatedCamoxProvider = (t0) => {
|
|
|
62
62
|
return t4;
|
|
63
63
|
};
|
|
64
64
|
const UnauthenticatedCamoxProvider = (t0) => {
|
|
65
|
-
const $ = c(
|
|
65
|
+
const $ = c(11);
|
|
66
66
|
const { children } = t0;
|
|
67
67
|
const signInRedirect = useSignInRedirect();
|
|
68
68
|
const { authenticationUrl } = useAuthContext();
|
|
@@ -90,15 +90,47 @@ const UnauthenticatedCamoxProvider = (t0) => {
|
|
|
90
90
|
} else t2 = $[4];
|
|
91
91
|
React.useEffect(t1, t2);
|
|
92
92
|
let t3;
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
let t4;
|
|
94
|
+
if ($[5] !== signInRedirect) {
|
|
95
|
+
t3 = () => {
|
|
96
|
+
if (!import.meta.env.PROD) return;
|
|
97
|
+
const toastId = toast("Sign in to open Camox Studio", {
|
|
98
|
+
duration: Infinity,
|
|
99
|
+
action: {
|
|
100
|
+
label: "Sign in",
|
|
101
|
+
onClick: () => signInRedirect()
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return () => void toast.dismiss(toastId);
|
|
105
|
+
};
|
|
106
|
+
t4 = [signInRedirect];
|
|
107
|
+
$[5] = signInRedirect;
|
|
108
|
+
$[6] = t3;
|
|
109
|
+
$[7] = t4;
|
|
110
|
+
} else {
|
|
111
|
+
t3 = $[6];
|
|
112
|
+
t4 = $[7];
|
|
113
|
+
}
|
|
114
|
+
React.useEffect(t3, t4);
|
|
115
|
+
let t5;
|
|
116
|
+
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
|
117
|
+
t5 = /* @__PURE__ */ jsx(Toaster, {
|
|
118
|
+
theme: "light",
|
|
119
|
+
position: "bottom-right",
|
|
120
|
+
offset: { bottom: "1rem" }
|
|
121
|
+
});
|
|
122
|
+
$[8] = t5;
|
|
123
|
+
} else t5 = $[8];
|
|
124
|
+
let t6;
|
|
125
|
+
if ($[9] !== children) {
|
|
126
|
+
t6 = /* @__PURE__ */ jsxs(Fragment, { children: [t5, /* @__PURE__ */ jsx("div", {
|
|
95
127
|
className: "bg-background min-h-screen",
|
|
96
128
|
children
|
|
97
|
-
}) });
|
|
98
|
-
$[
|
|
99
|
-
$[
|
|
100
|
-
} else
|
|
101
|
-
return
|
|
129
|
+
})] });
|
|
130
|
+
$[9] = children;
|
|
131
|
+
$[10] = t6;
|
|
132
|
+
} else t6 = $[10];
|
|
133
|
+
return t6;
|
|
102
134
|
};
|
|
103
135
|
function CamoxProvider({ children, camoxApp, authenticationUrl, apiUrl, projectSlug, environmentName }) {
|
|
104
136
|
const authClient = React.useMemo(() => createCamoxAuthClient(apiUrl), [apiUrl]);
|
|
@@ -4,7 +4,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
|
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Button } from "@camox/ui/button";
|
|
7
|
-
import {
|
|
7
|
+
import { ChevronDown } from "lucide-react";
|
|
8
8
|
import { Badge } from "@camox/ui/badge";
|
|
9
9
|
|
|
10
10
|
//#region src/features/studio/components/EnvironmentMenu.tsx
|
|
@@ -36,7 +36,7 @@ const EnvironmentMenu = () => {
|
|
|
36
36
|
} else t1 = $[3];
|
|
37
37
|
let t2;
|
|
38
38
|
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
|
39
|
-
t2 = /* @__PURE__ */ jsx(
|
|
39
|
+
t2 = /* @__PURE__ */ jsx(ChevronDown, { className: "shrink-0 opacity-50" });
|
|
40
40
|
$[4] = t2;
|
|
41
41
|
} else t2 = $[4];
|
|
42
42
|
let t3;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useAuthContext, useAuthState } from "../../../lib/auth.js";
|
|
2
2
|
import { useTheme } from "../useTheme.js";
|
|
3
3
|
import { c } from "react/compiler-runtime";
|
|
4
|
-
import { useContext } from "react";
|
|
5
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { Button } from "@camox/ui/button";
|
|
7
6
|
import { LogOut, Monitor, Moon, Settings, Sun, User } from "lucide-react";
|
|
@@ -34,7 +33,7 @@ const UserButton = () => {
|
|
|
34
33
|
return t0;
|
|
35
34
|
};
|
|
36
35
|
function AuthenticatedUserButton({ setTheme }) {
|
|
37
|
-
const authCtx =
|
|
36
|
+
const authCtx = useAuthContext();
|
|
38
37
|
const { data: session } = authCtx.authClient.useSession();
|
|
39
38
|
const authenticationUrl = authCtx.authenticationUrl;
|
|
40
39
|
const userName = session?.user?.name || "User";
|
|
@@ -100,7 +99,7 @@ function AuthenticatedUserButton({ setTheme }) {
|
|
|
100
99
|
})
|
|
101
100
|
] })] }),
|
|
102
101
|
/* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
103
|
-
onClick: () => authCtx.authClient.signOut(),
|
|
102
|
+
onClick: () => void authCtx.authClient.signOut(),
|
|
104
103
|
children: [/* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", { children: "Sign out" })]
|
|
105
104
|
})
|
|
106
105
|
]
|
|
@@ -27,13 +27,14 @@ const ${camelName} = createBlock({
|
|
|
27
27
|
title: Type.String({ default: "Title" }),
|
|
28
28
|
},
|
|
29
29
|
component: ${pascalName}Component,
|
|
30
|
+
toMarkdown: ['{{title}}']
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
function ${pascalName}Component() {
|
|
33
34
|
return (
|
|
34
35
|
<section>
|
|
35
36
|
<${camelName}.Field name="title">
|
|
36
|
-
{(
|
|
37
|
+
{(props) => <h1 {...props} />}
|
|
37
38
|
</${camelName}.Field>
|
|
38
39
|
</section>
|
|
39
40
|
);
|
|
@@ -14,7 +14,11 @@ function throwIfSyncAuthError(error) {
|
|
|
14
14
|
async function syncDefinitionsToApi(options) {
|
|
15
15
|
const { camoxApp, projectSlug, apiUrl, syncSecret, environmentName, logger } = options;
|
|
16
16
|
const client = createServerApiClient(apiUrl, environmentName);
|
|
17
|
-
const
|
|
17
|
+
const blocks = camoxApp.getBlocks();
|
|
18
|
+
const layoutDefinitions = camoxApp.getSerializableLayoutDefinitions();
|
|
19
|
+
const typesUsedInLayouts = /* @__PURE__ */ new Set();
|
|
20
|
+
for (const layout of layoutDefinitions) for (const blockDef of layout.blocks) typesUsedInLayouts.add(blockDef.type);
|
|
21
|
+
const definitions = blocks.filter((block) => !block.layoutOnly || typesUsedInLayouts.has(block.id)).map((block) => ({
|
|
18
22
|
blockId: block.id,
|
|
19
23
|
title: block.title,
|
|
20
24
|
description: block.description,
|
|
@@ -37,10 +41,10 @@ async function syncDefinitionsToApi(options) {
|
|
|
37
41
|
}
|
|
38
42
|
if (environmentCreated && environmentName) logger.info(`[camox] Created environment "${environmentName}" (forked from production)`, { timestamp: true });
|
|
39
43
|
logger.info(`[camox] Synced ${definitions.length} block definition${definitions.length === 1 ? "" : "s"}`, { timestamp: true });
|
|
40
|
-
const layoutDefinitions = camoxApp.getSerializableLayoutDefinitions();
|
|
41
44
|
if (layoutDefinitions.length > 0) {
|
|
45
|
+
let layoutSyncResults;
|
|
42
46
|
try {
|
|
43
|
-
await client.layouts.sync({
|
|
47
|
+
layoutSyncResults = await client.layouts.sync({
|
|
44
48
|
projectSlug,
|
|
45
49
|
syncSecret,
|
|
46
50
|
layouts: layoutDefinitions
|
|
@@ -50,6 +54,26 @@ async function syncDefinitionsToApi(options) {
|
|
|
50
54
|
throw error;
|
|
51
55
|
}
|
|
52
56
|
logger.info(`[camox] Synced ${layoutDefinitions.length} layout${layoutDefinitions.length === 1 ? "" : "s"} to Camox API`, { timestamp: true });
|
|
57
|
+
for (const result of layoutSyncResults.layouts) {
|
|
58
|
+
if (result.wasExisting && result.createdBlockTypes.length > 0) {
|
|
59
|
+
const blockList = result.createdBlockTypes.map((t) => `"${t}"`).join(", ");
|
|
60
|
+
logger.info(`[camox] Added ${result.createdBlockTypes.length} block${result.createdBlockTypes.length === 1 ? "" : "s"} to existing layout "${result.layout.layoutId}": ${blockList}`, { timestamp: true });
|
|
61
|
+
}
|
|
62
|
+
if (result.removedBlockTypes.length > 0) {
|
|
63
|
+
const blockList = result.removedBlockTypes.map((t) => `"${t}"`).join(", ");
|
|
64
|
+
logger.info(`[camox] Removed ${result.removedBlockTypes.length} layoutOnly block${result.removedBlockTypes.length === 1 ? "" : "s"} from layout "${result.layout.layoutId}": ${blockList}`, { timestamp: true });
|
|
65
|
+
}
|
|
66
|
+
if (result.skippedOrphanTypes.length > 0) {
|
|
67
|
+
const blockList = result.skippedOrphanTypes.map((t) => `"${t}"`).join(", ");
|
|
68
|
+
logger.info(`[camox] Layout "${result.layout.layoutId}" has ${result.skippedOrphanTypes.length} block${result.skippedOrphanTypes.length === 1 ? "" : "s"} still in DB but removed from code: ${blockList} (kept because not layoutOnly)`, { timestamp: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const layoutId of layoutSyncResults.deletedLayoutIds) logger.info(`[camox] Deleted layout "${layoutId}"`, { timestamp: true });
|
|
72
|
+
for (const blocked of layoutSyncResults.blockedLayoutDeletions) logger.warn(`[camox] Cannot delete layout "${blocked.layoutId}": still used by ${blocked.pageCount} page${blocked.pageCount === 1 ? "" : "s"}. Reassign or delete those pages first.`, { timestamp: true });
|
|
73
|
+
if (layoutSyncResults.deletedDefinitionTypes.length > 0) {
|
|
74
|
+
const blockList = layoutSyncResults.deletedDefinitionTypes.map((t) => `"${t}"`).join(", ");
|
|
75
|
+
logger.info(`[camox] Removed ${layoutSyncResults.deletedDefinitionTypes.length} layoutOnly block definition${layoutSyncResults.deletedDefinitionTypes.length === 1 ? "" : "s"} (no layout uses ${layoutSyncResults.deletedDefinitionTypes.length === 1 ? "it" : "them"} anymore): ${blockList}`, { timestamp: true });
|
|
76
|
+
}
|
|
53
77
|
}
|
|
54
78
|
const initialPage = camoxApp.getInitialPageBundles();
|
|
55
79
|
if (initialPage) try {
|
|
@@ -99,6 +123,8 @@ async function syncDefinitions(server, options) {
|
|
|
99
123
|
const blocksDir = path.resolve(server.config.root, "src/camox/blocks");
|
|
100
124
|
const client = createServerApiClient(apiUrl, environmentName);
|
|
101
125
|
async function performInitialSync() {
|
|
126
|
+
const appModule = server.moduleGraph.getModuleById(CAMOX_APP_PATH);
|
|
127
|
+
if (appModule) server.moduleGraph.invalidateModule(appModule);
|
|
102
128
|
const camoxModule = await ssrLoadModule(server, CAMOX_APP_PATH);
|
|
103
129
|
if (!camoxModule.camoxApp) {
|
|
104
130
|
server.config.logger.warn(`[camox] No camoxApp export found in ${CAMOX_APP_PATH}`, { timestamp: true });
|
|
@@ -123,6 +149,7 @@ async function syncDefinitions(server, options) {
|
|
|
123
149
|
return;
|
|
124
150
|
}
|
|
125
151
|
const block = blockModule.block;
|
|
152
|
+
if (block.layoutOnly) return;
|
|
126
153
|
let result;
|
|
127
154
|
try {
|
|
128
155
|
result = await client.blockDefinitions.upsert({
|
|
@@ -141,7 +168,7 @@ async function syncDefinitions(server, options) {
|
|
|
141
168
|
throwIfSyncAuthError(error);
|
|
142
169
|
throw error;
|
|
143
170
|
}
|
|
144
|
-
server.config.logger.info(`[camox] ${result.action === "created" ? "Created" : "Updated"} block "${block.id}"`, { timestamp: true });
|
|
171
|
+
server.config.logger.info(`[camox] ${result.action === "created" ? "Created" : "Updated"} block definition "${block.id}"`, { timestamp: true });
|
|
145
172
|
}
|
|
146
173
|
async function deleteBlock(filePath) {
|
|
147
174
|
const blockId = getBlockIdFromFilePath(filePath);
|
|
@@ -156,7 +183,7 @@ async function syncDefinitions(server, options) {
|
|
|
156
183
|
throwIfSyncAuthError(error);
|
|
157
184
|
throw error;
|
|
158
185
|
}
|
|
159
|
-
if (result.deleted) server.config.logger.info(`[camox] Deleted block "${blockId}"`, { timestamp: true });
|
|
186
|
+
if (result.deleted) server.config.logger.info(`[camox] Deleted block definition "${blockId}"`, { timestamp: true });
|
|
160
187
|
}
|
|
161
188
|
try {
|
|
162
189
|
await performInitialSync();
|
|
@@ -202,6 +229,9 @@ async function syncDefinitions(server, options) {
|
|
|
202
229
|
let layoutSyncTimer = null;
|
|
203
230
|
const handleLayoutFileChange = (filePath) => {
|
|
204
231
|
if (!isLayoutFile(filePath)) return;
|
|
232
|
+
const relativePath = "./" + path.relative(server.config.root, filePath);
|
|
233
|
+
const moduleNode = server.moduleGraph.getModuleById(relativePath);
|
|
234
|
+
if (moduleNode) server.moduleGraph.invalidateModule(moduleNode);
|
|
205
235
|
if (layoutSyncTimer) clearTimeout(layoutSyncTimer);
|
|
206
236
|
layoutSyncTimer = setTimeout(async () => {
|
|
207
237
|
layoutSyncTimer = null;
|
|
@@ -217,6 +247,7 @@ async function syncDefinitions(server, options) {
|
|
|
217
247
|
server.watcher.on("add", handleBlockFileUpsert);
|
|
218
248
|
server.watcher.on("add", handleLayoutFileChange);
|
|
219
249
|
server.watcher.on("unlink", handleBlockFileDelete);
|
|
250
|
+
server.watcher.on("unlink", handleLayoutFileChange);
|
|
220
251
|
}
|
|
221
252
|
|
|
222
253
|
//#endregion
|
package/dist/lib/auth.js
CHANGED
|
@@ -46,10 +46,12 @@ function getCookie(cookie) {
|
|
|
46
46
|
try {
|
|
47
47
|
parsed = JSON.parse(cookie);
|
|
48
48
|
} catch {}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const parts = [];
|
|
50
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
51
|
+
if (value.expires && new Date(value.expires) < /* @__PURE__ */ new Date()) continue;
|
|
52
|
+
parts.push(`${key}=${value.value}`);
|
|
53
|
+
}
|
|
54
|
+
return parts.join("; ");
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Read the cross-domain auth cookie from localStorage and return it as a
|
|
@@ -20,10 +20,10 @@ function useProjectRoom(apiUrl, projectId) {
|
|
|
20
20
|
if ($[1] !== apiUrl) {
|
|
21
21
|
let t2;
|
|
22
22
|
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
23
|
-
t2 =
|
|
23
|
+
t2 = /\/+$/;
|
|
24
24
|
$[3] = t2;
|
|
25
25
|
} else t2 = $[3];
|
|
26
|
-
t1 = apiUrl.replace(t2, "");
|
|
26
|
+
t1 = apiUrl.replace(/^https?:\/\//, "").replace(t2, "");
|
|
27
27
|
$[1] = apiUrl;
|
|
28
28
|
$[2] = t1;
|
|
29
29
|
} else t1 = $[2];
|
|
@@ -39,18 +39,30 @@ function useProjectRoom(apiUrl, projectId) {
|
|
|
39
39
|
prefix: "parties",
|
|
40
40
|
query: _temp,
|
|
41
41
|
enabled: t3,
|
|
42
|
-
|
|
42
|
+
onOpen() {
|
|
43
|
+
if (process.env.NODE_ENV !== "production") console.debug("[useProjectRoom] WebSocket connected");
|
|
44
|
+
},
|
|
45
|
+
onClose(event) {
|
|
46
|
+
console.warn(`[useProjectRoom] WebSocket closed (code=${event.code}, reason=${event.reason || "none"})`);
|
|
47
|
+
},
|
|
48
|
+
onError(event_0) {
|
|
49
|
+
console.error("[useProjectRoom] WebSocket error:", event_0);
|
|
50
|
+
},
|
|
51
|
+
onMessage(event_1) {
|
|
52
|
+
let data;
|
|
43
53
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
data = JSON.parse(event_1.data);
|
|
55
|
+
} catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (data.type !== "invalidate") return;
|
|
59
|
+
pendingRef.current.push(...data.targets);
|
|
60
|
+
clearTimeout(timerRef.current);
|
|
61
|
+
timerRef.current = setTimeout(() => {
|
|
62
|
+
const targets = pendingRef.current;
|
|
63
|
+
pendingRef.current = [];
|
|
64
|
+
for (const queryKey of targets) queryClient.invalidateQueries({ queryKey });
|
|
65
|
+
}, DEBOUNCE_MS);
|
|
54
66
|
}
|
|
55
67
|
};
|
|
56
68
|
$[4] = host;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "camox",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"bin": {
|
|
5
5
|
"camox": "./bin/camox.mjs"
|
|
6
6
|
},
|
|
@@ -97,9 +97,9 @@
|
|
|
97
97
|
"react-dom": "^19.2.5",
|
|
98
98
|
"react-og-preview": "^0.2.0",
|
|
99
99
|
"shiki": "^4.0.2",
|
|
100
|
-
"@camox/api-contract": "0.9.
|
|
101
|
-
"@camox/ui": "0.9.
|
|
102
|
-
"@camox/cli": "0.9.
|
|
100
|
+
"@camox/api-contract": "0.9.1",
|
|
101
|
+
"@camox/ui": "0.9.1",
|
|
102
|
+
"@camox/cli": "0.9.1"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@babel/core": "^7.29.0",
|