camox 0.43.0 → 0.44.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.
Files changed (63) hide show
  1. package/bin/camox.mjs +9 -1
  2. package/dist/core/components/lexical/InlineStylesPlugin.js +22 -6
  3. package/dist/core/components/lexical/SelectionBroadcaster.js +4 -4
  4. package/dist/core/components/lexical/SidebarLexicalEditor.js +15 -15
  5. package/dist/core/createApp.d.ts +28 -2
  6. package/dist/core/createBlock.js +24 -10
  7. package/dist/core/editing/createEditableBlock.d.ts +135 -1
  8. package/dist/core/editing/createEditableBlock.js +209 -74
  9. package/dist/core/lib/contentType.d.ts +5 -0
  10. package/dist/core/lib/contentType.js +11 -0
  11. package/dist/core/lib/fieldTypes.js +1 -0
  12. package/dist/core/lib/iconTypes.d.ts +13 -0
  13. package/dist/core/lib/icons.d.ts +6 -0
  14. package/dist/core/lib/icons.js +130 -0
  15. package/dist/core/lib/lexicalState.js +6 -6
  16. package/dist/core/lib/modifierFormats.js +1 -1
  17. package/dist/core/lib/textStyles.d.ts +1 -1
  18. package/dist/core/lib/textStyles.js +3 -3
  19. package/dist/features/content/components/AssetCard.js +104 -68
  20. package/dist/features/preview/CamoxPreview.js +120 -95
  21. package/dist/features/preview/commentsEnabled.js +2 -2
  22. package/dist/features/preview/components/AssetLightbox.js +17 -6
  23. package/dist/features/preview/components/AttachedComments.js +224 -87
  24. package/dist/features/preview/components/CommentTargetQuote.js +22 -21
  25. package/dist/features/preview/components/DerivedLayoutDialog.js +36 -21
  26. package/dist/features/preview/components/FieldOverlayStyles.js +29 -9
  27. package/dist/features/preview/components/FieldToolbar.js +8 -8
  28. package/dist/features/preview/components/IconFieldEditor.js +231 -0
  29. package/dist/features/preview/components/ItemFieldsEditor.js +21 -14
  30. package/dist/features/preview/components/OverlayTracker.js +1 -1
  31. package/dist/features/preview/components/PagePicker.js +44 -44
  32. package/dist/features/preview/components/PreviewPanel.js +1 -1
  33. package/dist/features/preview/components/PreviewToolbar.js +224 -225
  34. package/dist/features/preview/components/SendFeedbackDialog.js +83 -0
  35. package/dist/features/preview/overlayHighlights.js +54 -0
  36. package/dist/features/preview/previewCommentsStore.js +54 -34
  37. package/dist/features/preview/previewSelection.js +28 -23
  38. package/dist/features/preview/usePageComments.js +39 -0
  39. package/dist/features/provider/CoreCamoxProvider.js +37 -5
  40. package/dist/features/provider/LocalhostPreviewProvider.js +81 -45
  41. package/dist/features/provider/components/CommandPalette.js +35 -84
  42. package/dist/features/runtime/pageApp.js +32 -28
  43. package/dist/features/runtime/pageServer.js +4 -1
  44. package/dist/features/runtime/runtime.d.ts +2 -0
  45. package/dist/features/runtime/runtime.js +25 -0
  46. package/dist/features/studio/components/EnvironmentMenu.js +75 -63
  47. package/dist/features/vite/auth.js +1 -50
  48. package/dist/features/vite/iconGeneration.js +16 -0
  49. package/dist/features/vite/vite.d.ts +4 -2
  50. package/dist/features/vite/vite.js +19 -2
  51. package/dist/lib/auth.js +39 -16
  52. package/dist/lib/preview-handoff.js +13 -0
  53. package/dist/lib/queries.js +12 -1
  54. package/dist/lib/use-project-room.js +5 -2
  55. package/dist/studio-overlays.css +19 -15
  56. package/dist/studio.css +1 -1
  57. package/package.json +5 -4
  58. package/skills/camox/SKILL.md +6 -0
  59. package/skills/camox/references/block-definitions.md +27 -0
  60. package/skills/camox/references/cli-common.md +14 -0
  61. package/skills/camox/references/cli-content.md +11 -0
  62. package/skills/camox/references/field-styling.md +4 -4
  63. package/skills/camoxify/SKILL.md +14 -0
@@ -2,6 +2,7 @@ import { generateAppFile, watchAppFile } from "./appGeneration.js";
2
2
  import { readAuthTokenForUrl } from "./auth.js";
3
3
  import { watchNewBlockFiles } from "./blockBoilerplate.js";
4
4
  import { installDevAuthenticationMiddleware } from "./devAuthentication.js";
5
+ import { generateIconTypes } from "./iconGeneration.js";
5
6
  import { syncDefinitions, syncDefinitionsToApi } from "./definitionsSync.js";
6
7
  import { cleanupGeneratedRouteFiles } from "./routeGeneration.js";
7
8
  import { installRuntimeNitroRoutes, loadRuntimeDevModule, resolveRuntimeDevId } from "./runtimeDev.js";
@@ -95,7 +96,8 @@ function camox(options) {
95
96
  cpSync(serverAssets, join(options.output.publicDir, "assets"), { recursive: true });
96
97
  });
97
98
  } },
98
- config(_config, env) {
99
+ async config(_config, env) {
100
+ const iconIds = await generateIconTypes(resolve(_config.root ?? process.cwd()), options.icons);
99
101
  isBuild = env.command === "build";
100
102
  isRelease = isBuild && process.env.CAMOX_INTERNAL_RELEASE === "1";
101
103
  if (isRelease) environmentName = "production";
@@ -107,9 +109,10 @@ function camox(options) {
107
109
  return {
108
110
  build: { emitAssets: true },
109
111
  define: {
112
+ __CAMOX_ICON_IDS__: JSON.stringify(iconIds),
110
113
  __CAMOX_TELEMETRY_DISABLED__: JSON.stringify(!!options.disableTelemetry),
111
114
  __ENABLE_TANSTACK_DEVTOOLS__: JSON.stringify(enableTanstackDevtools),
112
- __CAMOX_ENABLE_COMMENTS__: JSON.stringify(options._internal?.enableComments ?? false),
115
+ __CAMOX_ENABLE_EXPERIMENTAL_FEATURES__: JSON.stringify(options._internal?.enableExperimentalFeatures ?? false),
113
116
  __CAMOX_ENVIRONMENT_NAME__: JSON.stringify(environmentName),
114
117
  __CAMOX_API_URL__: JSON.stringify(apiUrl),
115
118
  __CAMOX_AUTHENTICATION_URL__: JSON.stringify(authenticationUrl),
@@ -139,6 +142,8 @@ function camox(options) {
139
142
  "camox > @base-ui/react/accordion",
140
143
  "camox > @base-ui/react/alert-dialog",
141
144
  "camox > @base-ui/react/avatar",
145
+ "camox > @base-ui/react/button",
146
+ "camox > @base-ui/react/combobox",
142
147
  "camox > @base-ui/react/dialog",
143
148
  "camox > @base-ui/react/input",
144
149
  "camox > @base-ui/react/menu",
@@ -153,9 +158,12 @@ function camox(options) {
153
158
  "camox > @base-ui/react/use-render",
154
159
  "camox > @camox/api-contract",
155
160
  "camox > @camox/api-contract/query-keys",
161
+ "camox > @camox/ui > class-variance-authority",
162
+ "camox > @camox/ui > clsx",
156
163
  "camox > @camox/ui > cmdk",
157
164
  "camox > @camox/ui > lucide-react",
158
165
  "camox > @camox/ui > sonner",
166
+ "camox > @camox/ui > tailwind-merge",
159
167
  "camox > @dnd-kit/core",
160
168
  "camox > @dnd-kit/modifiers",
161
169
  "camox > @dnd-kit/sortable",
@@ -179,7 +187,14 @@ function camox(options) {
179
187
  "camox > @sinclair/typebox",
180
188
  "camox > @takumi-rs/image-response",
181
189
  "camox > @tanstack/react-form",
190
+ "camox > @tanstack/react-query",
191
+ "camox > @tanstack/react-router",
192
+ "camox > @tanstack/react-virtual",
193
+ "camox > @unhead/react/client",
194
+ "camox > @unhead/react/server",
182
195
  "camox > @xstate/store-react",
196
+ "camox > better-auth/client/plugins",
197
+ "camox > better-auth/react",
183
198
  "camox > better-auth > @better-auth/core/env",
184
199
  "camox > better-auth > @better-auth/core/error",
185
200
  "camox > better-auth > @better-auth/core/utils/error-codes",
@@ -190,6 +205,7 @@ function camox(options) {
190
205
  "camox > fractional-indexing",
191
206
  "camox > lexical",
192
207
  "camox > lucide-react",
208
+ "camox > marked",
193
209
  "camox > @tanstack/react-query-devtools/production",
194
210
  "camox > partysocket/react"
195
211
  ] }
@@ -246,6 +262,7 @@ function camox(options) {
246
262
  root: resolvedConfig.root,
247
263
  cacheDir: resolve(resolvedConfig.root, "node_modules", ".vite-camox-temp"),
248
264
  resolve: resolvedConfig.resolve,
265
+ define: resolvedConfig.define,
249
266
  server: { middlewareMode: true },
250
267
  logLevel: "silent"
251
268
  });
package/dist/lib/auth.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { actionsStore } from "../features/provider/actionsStore.js";
2
+ import { validatePreviewTarget } from "./preview-handoff.js";
2
3
  import { SERVER_AUTH_COOKIE_NAME, buildClearServerAuthCookieHeader, getServerAuthCookieHeader } from "./server-auth-cookie.js";
3
4
  import { c } from "react/compiler-runtime";
4
5
  import * as React from "react";
@@ -181,21 +182,32 @@ function createCamoxAuthClient(apiUrl) {
181
182
  * Process a `?ott=` one-time token from the URL before the provider mounts.
182
183
  * Verifies the token against the API backend and notifies the session store.
183
184
  *
184
- * Returns `true` once processing is complete (or if there was no OTT).
185
+ * Reports completion and explicit handoff failures instead of silently showing live content.
185
186
  */
186
- function useProcessOtt(authClient) {
187
+ function useProcessOtt(authClient, target) {
188
+ const [error, setError] = React.useState(null);
189
+ const processing = React.useRef(false);
187
190
  const [ready, setReady] = React.useState(() => {
188
191
  if (typeof window === "undefined") return true;
189
192
  return !new URL(window.location.href).searchParams.has("ott");
190
193
  });
191
194
  React.useEffect(() => {
192
- if (ready) return;
193
195
  const url = new URL(window.location.href);
196
+ const targetError = validatePreviewTarget(url.searchParams.get("camox-preview"), target);
197
+ if (targetError) {
198
+ url.searchParams.delete("ott");
199
+ window.history.replaceState({}, "", url);
200
+ setError(targetError);
201
+ setReady(true);
202
+ return;
203
+ }
204
+ if (ready || processing.current) return;
194
205
  const ott = url.searchParams.get("ott");
195
206
  if (!ott) {
196
207
  setReady(true);
197
208
  return;
198
209
  }
210
+ processing.current = true;
199
211
  url.searchParams.delete("ott");
200
212
  window.history.replaceState({}, "", url);
201
213
  (async () => {
@@ -205,18 +217,29 @@ function useProcessOtt(authClient) {
205
217
  if (!verification.data) throw new Error("OTT verification returned no session");
206
218
  window.location.reload();
207
219
  return;
208
- } catch {}
220
+ } catch {
221
+ setError("Preview sign-in failed. The link may have expired or already been used. Run camox preview again for a fresh link.");
222
+ }
209
223
  setReady(true);
210
224
  })();
211
- }, [authClient, ready]);
212
- return ready;
225
+ }, [
226
+ authClient,
227
+ ready,
228
+ target.projectSlug,
229
+ target.environmentName,
230
+ target.apiUrl
231
+ ]);
232
+ return {
233
+ ready,
234
+ error
235
+ };
213
236
  }
214
237
  const AuthContext = React.createContext(null);
215
238
  function useAuthContext() {
216
239
  const $ = c(1);
217
- if ($[0] !== "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3") {
240
+ if ($[0] !== "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525") {
218
241
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
219
- $[0] = "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3";
242
+ $[0] = "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525";
220
243
  }
221
244
  const ctx = React.useContext(AuthContext);
222
245
  if (!ctx) throw new Error("Missing CamoxProvider");
@@ -224,9 +247,9 @@ function useAuthContext() {
224
247
  }
225
248
  function useProjectSlug() {
226
249
  const $ = c(1);
227
- if ($[0] !== "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3") {
250
+ if ($[0] !== "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525") {
228
251
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
229
- $[0] = "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3";
252
+ $[0] = "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525";
230
253
  }
231
254
  return useAuthContext().projectSlug;
232
255
  }
@@ -242,18 +265,18 @@ function useAuthState() {
242
265
  }
243
266
  function useIsAuthenticated() {
244
267
  const $ = c(1);
245
- if ($[0] !== "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3") {
268
+ if ($[0] !== "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525") {
246
269
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
247
- $[0] = "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3";
270
+ $[0] = "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525";
248
271
  }
249
272
  const { isAuthenticated } = useAuthState();
250
273
  return isAuthenticated;
251
274
  }
252
275
  function useSignInRedirect(callbackHref) {
253
276
  const $ = c(4);
254
- if ($[0] !== "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3") {
277
+ if ($[0] !== "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525") {
255
278
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
256
- $[0] = "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3";
279
+ $[0] = "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525";
257
280
  }
258
281
  const { authenticationUrl } = useAuthContext();
259
282
  let t0;
@@ -275,9 +298,9 @@ function useSignInRedirect(callbackHref) {
275
298
  */
276
299
  function useAuthActions() {
277
300
  const $ = c(5);
278
- if ($[0] !== "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3") {
301
+ if ($[0] !== "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525") {
279
302
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
280
- $[0] = "49cbba734e7826ee7cc1d1d95d5d7895a7a71f25806548ac312288b5b8a802f3";
303
+ $[0] = "2870dba5d074cbe2d9a238f9ae39bb51493bf31c0dd30caa79404003309e1525";
281
304
  }
282
305
  const { authClient, authenticationUrl } = useAuthContext();
283
306
  let t0;
@@ -0,0 +1,13 @@
1
+ //#region src/lib/preview-handoff.ts
2
+ /** Validate the destination before exchanging any credential with its backend. */
3
+ function validatePreviewTarget(raw, target) {
4
+ if (raw === null) return null;
5
+ try {
6
+ const expected = JSON.parse(raw);
7
+ if (expected.projectSlug === target.projectSlug && expected.environmentName === target.environmentName && typeof expected.apiUrl === "string" && expected.apiUrl.replace(/\/+$/, "") === target.apiUrl.replace(/\/+$/, "")) return null;
8
+ } catch {}
9
+ return "Preview destination does not match the CLI project, backend, or environment. Check --cwd and --url, then run camox preview again.";
10
+ }
11
+
12
+ //#endregion
13
+ export { validatePreviewTarget };
@@ -3,6 +3,17 @@ import { getApiUrl, getEnvironmentName, getOrpc } from "./api-client.js";
3
3
  import { queryKeys } from "@camox/api-contract/query-keys";
4
4
 
5
5
  //#region src/lib/queries.ts
6
+ const commentQueries = { list: (pageId) => ({
7
+ ...getOrpc().comments.list.queryOptions({
8
+ input: { pageId },
9
+ staleTime: 3e4
10
+ }),
11
+ queryKey: [...queryKeys.comments.list(pageId), getEnvironmentName() ?? "production"]
12
+ }) };
13
+ const commentMutations = {
14
+ create: () => getOrpc().comments.create.mutationOptions(),
15
+ setResolved: () => getOrpc().comments.setResolved.mutationOptions()
16
+ };
6
17
  const pageQueries = {
7
18
  list: (projectId) => ({
8
19
  ...getOrpc().pages.list.queryOptions({
@@ -183,4 +194,4 @@ const fileMutations = {
183
194
  };
184
195
 
185
196
  //#endregion
186
- export { blockMutations, blockQueries, environmentMutations, environmentQueries, fileMutations, fileQueries, layoutQueries, pageMutations, pageQueries, projectQueries, repeatableItemMutations };
197
+ export { blockMutations, blockQueries, commentMutations, commentQueries, environmentMutations, environmentQueries, fileMutations, fileQueries, layoutQueries, pageMutations, pageQueries, projectQueries, repeatableItemMutations };
@@ -1,16 +1,18 @@
1
1
  import { getAuthCookieHeader } from "./auth.js";
2
+ import { areCommentsEnabled } from "../features/preview/commentsEnabled.js";
2
3
  import { c } from "react/compiler-runtime";
3
4
  import { useQueryClient } from "@tanstack/react-query";
4
5
  import { useRef } from "react";
6
+ import { queryKeys } from "@camox/api-contract/query-keys";
5
7
  import { usePartySocket } from "partysocket/react";
6
8
 
7
9
  //#region src/lib/use-project-room.ts
8
10
  const DEBOUNCE_MS = 300;
9
11
  function useProjectRoom(apiUrl, projectId) {
10
12
  const $ = c(10);
11
- if ($[0] !== "c94fdacdaeb446d7db77b7fee7efa4a24c7433c9c1f518f586565506a52bbbd7") {
13
+ if ($[0] !== "67c19533e16ee15335bcc3b4e1e8e5b7b5ec3b876c8d1b4511e85727b85792f9") {
12
14
  for (let $i = 0; $i < 10; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
13
- $[0] = "c94fdacdaeb446d7db77b7fee7efa4a24c7433c9c1f518f586565506a52bbbd7";
15
+ $[0] = "67c19533e16ee15335bcc3b4e1e8e5b7b5ec3b876c8d1b4511e85727b85792f9";
14
16
  }
15
17
  const queryClient = useQueryClient();
16
18
  const timerRef = useRef(void 0);
@@ -44,6 +46,7 @@ function useProjectRoom(apiUrl, projectId) {
44
46
  query: _temp,
45
47
  enabled: t3,
46
48
  onOpen() {
49
+ if (areCommentsEnabled()) queryClient.invalidateQueries({ queryKey: queryKeys.comments.all });
47
50
  if (process.env.NODE_ENV !== "production") console.debug("[useProjectRoom] WebSocket connected");
48
51
  },
49
52
  onClose(event) {
@@ -2,8 +2,8 @@
2
2
  * Visual editing overlay styles injected into the preview iframe.
3
3
  *
4
4
  * State is driven by data attributes on the elements:
5
- * - data-camox-hovered: element is hovered
6
- * - data-camox-focused: element is focused/selected
5
+ * - data-camox-highlight-hovered: deepest hovered element (local or remote)
6
+ * - data-camox-highlight-focused: deepest focused/selected element
7
7
  * - data-camox-overlay-mode="synced": use synced-block (purple) colors
8
8
  *
9
9
  * Elements are targeted via their existing data attributes:
@@ -35,9 +35,9 @@
35
35
 
36
36
  /* ---- Hovered state ---- */
37
37
 
38
- [data-camox-field-id][data-camox-field-type][data-camox-hovered]::after,
39
- [data-camox-block-id][data-camox-hovered]:not([data-camox-detached])::after,
40
- [data-camox-repeater-item-id][data-camox-hovered]::after {
38
+ [data-camox-field-id][data-camox-field-type][data-camox-highlight-hovered]::after,
39
+ [data-camox-block-id][data-camox-highlight-hovered]:not([data-camox-detached])::after,
40
+ [data-camox-repeater-item-id][data-camox-highlight-hovered]::after {
41
41
  content: "";
42
42
  position: absolute;
43
43
  pointer-events: none;
@@ -48,8 +48,9 @@
48
48
 
49
49
  /* ---- Focused state (overrides hovered via source order) ---- */
50
50
 
51
- [data-camox-field-id][data-camox-field-type][data-camox-focused]::after,
52
- [data-camox-block-id][data-camox-focused]:not([data-camox-detached])::after {
51
+ [data-camox-field-id][data-camox-field-type][data-camox-highlight-focused]::after,
52
+ [data-camox-block-id][data-camox-highlight-focused]:not([data-camox-detached])::after,
53
+ [data-camox-repeater-item-id][data-camox-highlight-focused]::after {
53
54
  content: "";
54
55
  position: absolute;
55
56
  pointer-events: none;
@@ -63,17 +64,20 @@
63
64
  fragment when the inline wraps. Applied directly to the element via attribute
64
65
  selectors with !important so user-supplied className/style box-shadows can't
65
66
  clobber it. */
66
- [data-camox-field-id]:not([data-camox-field-type])[data-camox-hovered],
67
- [data-camox-field-id]:not([data-camox-field-type])[data-camox-focused] {
67
+ [data-camox-field-id]:not([data-camox-field-type])[data-camox-highlight-hovered],
68
+ [data-camox-field-id]:not([data-camox-field-type])[data-camox-highlight-focused] {
68
69
  -webkit-box-decoration-break: clone;
69
70
  box-decoration-break: clone;
70
71
  }
71
72
 
72
- [data-camox-field-id]:not([data-camox-field-type])[data-camox-hovered] {
73
+ /* SVG roots cannot render ::after; use the same direct outline as text fields. */
74
+ svg[data-camox-field-type="icon"][data-camox-highlight-hovered],
75
+ [data-camox-field-id]:not([data-camox-field-type])[data-camox-highlight-hovered] {
73
76
  box-shadow: 0 0 0 var(--camox-overlay-width-hover) var(--camox-overlay-color-hover) !important;
74
77
  }
75
78
 
76
- [data-camox-field-id]:not([data-camox-field-type])[data-camox-focused] {
79
+ svg[data-camox-field-type="icon"][data-camox-highlight-focused],
80
+ [data-camox-field-id]:not([data-camox-field-type])[data-camox-highlight-focused] {
77
81
  box-shadow: 0 0 0 var(--camox-overlay-width-selected) var(--camox-overlay-color-selected) !important;
78
82
  }
79
83
 
@@ -85,20 +89,20 @@
85
89
 
86
90
  /* ---- Embed: z-index above click-interceptor ---- */
87
91
 
88
- [data-camox-field-type="embed"][data-camox-hovered]::after,
89
- [data-camox-field-type="embed"][data-camox-focused]::after {
92
+ [data-camox-field-type="embed"][data-camox-highlight-hovered]::after,
93
+ [data-camox-field-type="embed"][data-camox-highlight-focused]::after {
90
94
  z-index: 11;
91
95
  }
92
96
 
93
97
  /* ---- Detached: styled directly (not ::after) because the parent element
94
98
  (e.g. fixed navbar) must not get position: relative ---- */
95
99
 
96
- [data-camox-detached][data-camox-hovered] {
100
+ [data-camox-detached][data-camox-highlight-hovered] {
97
101
  inset: var(--camox-overlay-inset-block-hover);
98
102
  border: var(--camox-overlay-width-hover) solid var(--camox-overlay-color-hover);
99
103
  }
100
104
 
101
- [data-camox-detached][data-camox-focused] {
105
+ [data-camox-detached][data-camox-highlight-focused] {
102
106
  inset: var(--camox-overlay-inset-block-selected);
103
107
  border: var(--camox-overlay-width-selected) solid var(--camox-overlay-color-selected);
104
108
  }