camox 0.40.0 → 0.41.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 (97) hide show
  1. package/dist/components/PlatformLabel.js +62 -0
  2. package/dist/core/components/lexical/GradientNode.js +126 -0
  3. package/dist/core/components/lexical/InlineLexicalEditor.js +37 -56
  4. package/dist/core/components/lexical/InlineStylesPlugin.js +183 -0
  5. package/dist/core/components/lexical/SelectionBroadcaster.js +49 -46
  6. package/dist/core/components/lexical/SidebarLexicalEditor.js +197 -151
  7. package/dist/core/components/lexical/editorConfig.js +25 -7
  8. package/dist/core/components/lexical/selectTextLink.js +21 -0
  9. package/dist/core/createApp.d.ts +30 -17
  10. package/dist/core/createApp.js +14 -3
  11. package/dist/core/createBlock.d.ts +2 -2
  12. package/dist/core/createBlock.js +18 -15
  13. package/dist/core/createLayout.d.ts +48 -4
  14. package/dist/core/createLayout.js +51 -23
  15. package/dist/core/derivedRoutes.js +36 -0
  16. package/dist/core/editing/createEditableBlock.d.ts +9 -8
  17. package/dist/core/editing/createEditableBlock.js +167 -168
  18. package/dist/core/lib/lexicalReact.d.ts +1 -26
  19. package/dist/core/lib/lexicalReact.js +46 -60
  20. package/dist/core/lib/lexicalState.js +164 -81
  21. package/dist/core/lib/modifierFormats.js +4 -15
  22. package/dist/core/lib/textStyles.d.ts +27 -0
  23. package/dist/core/lib/textStyles.js +47 -0
  24. package/dist/features/navigation/navigation.d.ts +2 -0
  25. package/dist/features/navigation/navigation.js +51 -46
  26. package/dist/features/page/DerivedPageContent.js +61 -0
  27. package/dist/features/page/PublishedPageContent.js +52 -83
  28. package/dist/features/page/SharedLayoutContent.js +66 -0
  29. package/dist/features/preview/CamoxPreview.d.ts +16 -1
  30. package/dist/features/preview/CamoxPreview.js +242 -265
  31. package/dist/features/preview/DerivedPreview.js +137 -0
  32. package/dist/features/preview/EditablePageExperience.js +25 -8
  33. package/dist/features/preview/components/AddBlockSidebar.js +43 -33
  34. package/dist/features/preview/components/AssetLightbox.js +1 -1
  35. package/dist/features/preview/components/BlockThumbnail.js +192 -0
  36. package/dist/features/preview/components/CreatePageModal.js +123 -115
  37. package/dist/features/preview/components/DerivedLayoutDialog.js +100 -0
  38. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +5 -0
  39. package/dist/features/preview/components/DerivedLayoutSidebar.js +163 -0
  40. package/dist/features/preview/components/DerivedPageInfoSidebar.js +57 -0
  41. package/dist/features/preview/components/FieldToolbar.js +42 -20
  42. package/dist/features/preview/components/Frame.js +133 -140
  43. package/dist/features/preview/components/LeftSidebar.js +42 -38
  44. package/dist/features/preview/components/MobilePreviewDrawer.js +4 -4
  45. package/dist/features/preview/components/Overlays.js +57 -42
  46. package/dist/features/preview/components/PageInfoSidebar.js +63 -55
  47. package/dist/features/preview/components/PageNavigatorSidebar.js +34 -483
  48. package/dist/features/preview/components/PagePicker.js +205 -153
  49. package/dist/features/preview/components/PageTree.js +298 -206
  50. package/dist/features/preview/components/PreviewActivation.js +78 -0
  51. package/dist/features/preview/components/PreviewPanel.js +67 -35
  52. package/dist/features/preview/components/PreviewToolbar.js +218 -232
  53. package/dist/features/preview/components/PublicationRow.js +431 -0
  54. package/dist/features/preview/components/PublishDialog.js +180 -97
  55. package/dist/features/preview/components/RightSidebar.js +19 -34
  56. package/dist/features/preview/components/previewStyles.js +8 -0
  57. package/dist/features/preview/components/useUpdateBlockPosition.js +7 -13
  58. package/dist/features/preview/previewConstants.js +8 -0
  59. package/dist/features/preview/previewStore.js +0 -10
  60. package/dist/features/preview/publication.js +71 -0
  61. package/dist/features/provider/AuthenticatedCamoxProvider.js +2 -1
  62. package/dist/features/provider/CoreCamoxProvider.js +11 -2
  63. package/dist/features/provider/LocalhostPreviewProvider.js +6 -7
  64. package/dist/features/routes/pageRuntime.d.ts +10 -0
  65. package/dist/features/routes/pageRuntime.js +7 -9
  66. package/dist/features/runtime/PreviewDocumentContext.js +7 -0
  67. package/dist/features/runtime/RuntimeChrome.js +137 -0
  68. package/dist/features/runtime/SharedChromeContext.js +7 -0
  69. package/dist/features/runtime/navigationRequest.js +21 -0
  70. package/dist/features/runtime/navigationTarget.js +23 -0
  71. package/dist/features/runtime/pageApp.js +196 -121
  72. package/dist/features/runtime/pageNavigation.js +79 -130
  73. package/dist/features/runtime/runtime.d.ts +11 -0
  74. package/dist/features/runtime/runtime.js +183 -47
  75. package/dist/features/runtime/studioApp.d.ts +2 -11
  76. package/dist/features/runtime/studioApp.js +14 -177
  77. package/dist/features/studio/CamoxStudio.js +27 -13
  78. package/dist/features/studio/components/Navbar.js +80 -74
  79. package/dist/features/studio/studioTheme.js +25 -0
  80. package/dist/features/studio/useTheme.js +94 -109
  81. package/dist/features/vite/appGeneration.js +12 -3
  82. package/dist/features/vite/definitionsSync.js +4 -2
  83. package/dist/features/vite/layoutGeneration.js +38 -0
  84. package/dist/features/vite/runtimeDev.js +3 -1
  85. package/dist/lib/auth.js +15 -13
  86. package/dist/lib/normalized-data.js +4 -4
  87. package/dist/lib/platform.js +13 -0
  88. package/dist/lib/utils.js +90 -38
  89. package/dist/studio-overlays.css +5 -40
  90. package/dist/studio.css +1 -1
  91. package/package.json +8 -4
  92. package/skills/camox/SKILL.md +2 -0
  93. package/skills/camox/references/block-definitions.md +31 -12
  94. package/skills/camox/references/layout-definitions.md +2 -0
  95. package/dist/core/lib/modifiers.js +0 -24
  96. package/dist/features/preview/previewQueryFns.js +0 -23
  97. package/dist/features/runtime/studioNavigation.js +0 -120
@@ -48,9 +48,22 @@ export { myBlock as block };
48
48
  | `content` | yes | An object where each key is a field name and each value is a `Type.*` call. These fields are inline-editable in the CMS. |
49
49
  | `settings` | no | Same shape as `content`, but for configuration that lives in a settings panel (not inline). Only `Type.Enum` and `Type.Boolean` should be used here. |
50
50
  | `layoutOnly` | no | If `true`, the block won't appear in the "add block" sheet — it can only be placed inside layouts (e.g. navbar, footer). |
51
+ | `synced` | no | Boolean, defaults to `false`. Share content and settings across every instance of this type within an environment. Ideal for a navbar/footer reused across layouts. Synced blocks have purple editor highlights. |
51
52
  | `component` | yes | A named React function component that renders the block. |
52
53
  | `toMarkdown` | yes | A builder function `(c, s) => (...)[]` that renders block content as markdown. `c` is a proxy typed on your `content` keys; `s` is a proxy typed on your `settings` keys used to wrap lines in conditionals. Each returned entry becomes a paragraph (joined with `\n\n`). Lines where all referenced fields resolve to empty — or whose condition is false — are omitted. |
53
54
 
55
+ ## Synced blocks (`synced`, optional)
56
+
57
+ `createBlock({ synced: true, ... })` makes every instance of that block **type** share content and settings, including nested repeater items and their settings. It defaults to `false`: instances are independent unless you opt in. Sharing is scoped to one project environment; development and production never share edits automatically.
58
+
59
+ Use `layoutOnly: true, synced: true` for a navbar or footer that must have identical data across different layouts. `layoutOnly` controls where a block can be placed; it does **not** enable syncing. A synced block can also be a normal page-content block.
60
+
61
+ - Editing any instance updates the shared draft data everywhere. New instances reuse existing data rather than resetting it to defaults.
62
+ - Positions, page/layout placement, and deletion remain per instance. Deleting one placement doesn't delete the others.
63
+ - Publishing a page or layout containing the block publishes its shared data for all live instances. Unpublished draft edits never leak into the live site.
64
+ - Enabling syncing on an existing type keeps the oldest instance's data and applies it to the others. Disabling it leaves each instance with a copy that can then be edited independently.
65
+ - Synced blocks have **purple highlights and overlays** in the editor.
66
+
54
67
  ## Markdown Template (`toMarkdown`)
55
68
 
56
69
  `toMarkdown` is a builder function that controls how block content is rendered as markdown for AI features (summaries, SEO). It receives:
@@ -146,7 +159,7 @@ Import `Type` from `"camox/createBlock"`. Every field requires a default value.
146
159
 
147
160
  ### Type.String
148
161
 
149
- Inline-editable text. The workhorse field type. String fields support inline markdown-style formatting in rendered site output: `**bold**`, `*italic*`, `***bold italic***`, and text links like `[label](https://example.com)` or internal page links inserted by the editor.
162
+ Inline-editable text. The workhorse field type. String fields support inline formatting in both editing and published output: `**bold**`, `*italic*` (or `_italic_`), `***bold italic***`, `<u>underlined</u>`, `<gradient>gradient text</gradient>`, and text links like `[label](https://example.com)` or internal page links inserted by the editor. Gradient can contain other formatting, links, and line breaks. Content remains a string. The editor uses explicit `<strong>` and `<em>` tags when Markdown delimiters cannot represent a selection losslessly (for example, formatting that includes leading/trailing whitespace). These are built-in formatting tags, not arbitrary HTML.
150
163
 
151
164
  ```tsx
152
165
  Type.String({
@@ -300,27 +313,33 @@ The component is a regular React function. It uses methods on the block constant
300
313
 
301
314
  The `name` must match a key in `content` that is a `Type.String`. Spread `props` onto the element — `props.children` contains the rendered content. This is what makes the field inline-editable in the CMS.
302
315
 
303
- In site output, `props.children` renders supported inline formatting (`**bold**`, `*italic*`, text links). By default, text links render as underlined `<a>` elements. Customize inline rendering with the optional `components` prop:
316
+ Camox owns inline markup so editing and published output have the same appearance. Customize appearance with `textStyle` and `linkStyle` (these replace the old `components` prop):
304
317
 
305
318
  ```tsx
306
319
  <myBlock.Field
307
320
  name="description"
308
- components={{
309
- link: (props, data) => (
310
- <a
311
- {...props}
312
- className={data.external ? "text-blue-600 underline" : "text-primary font-medium"}
313
- />
314
- ),
315
- strong: (props) => <strong {...props} className="font-semibold" />,
316
- emphasis: (props) => <em {...props} className="italic text-muted-foreground" />,
321
+ textStyle={({ bold, italic, gradient }) => {
322
+ if (gradient) {
323
+ return { style: { backgroundImage: "linear-gradient(to right, orange, deeppink)" } };
324
+ }
325
+ return {
326
+ className: bold ? "text-primary" : undefined,
327
+ style: bold && italic ? { letterSpacing: "0.02em" } : undefined,
328
+ };
317
329
  }}
330
+ linkStyle={({ external }) => ({
331
+ className: external ? "text-blue-600" : "text-primary font-medium",
332
+ })}
318
333
  >
319
334
  {(props) => <p {...props} />}
320
335
  </myBlock.Field>
321
336
  ```
322
337
 
323
- The `components.link` callback receives `(props, data)`. Spread `props` onto your link element; `data` exposes `{ target, href, external, pageId }` for styling external vs. internal links. `components.strong` customizes `**bold**`; `components.emphasis` customizes `*italic*`. The same `components` prop is available on `item.Field` inside repeaters.
338
+ - `textStyle` receives `{ bold, italic, underline, gradient }` and returns `{ className?, style? }` or `undefined`. For text runs, `gradient` is false and the other flags describe explicit content formatting, not inherited heading styles. Unmarked text keeps its parent's typography.
339
+ - `linkStyle` receives `{ target, href, external, pageId }` and returns the same appearance-only object. Links are underlined by default; override `style.textDecorationLine` to change that.
340
+ - Camox also calls `textStyle` once for each shared gradient wrapper with `{ gradient: true, bold: false, italic: false, underline: false }`. That result styles the whole range; its children receive their own text-run calls. `gradient` identifies the wrapper being styled, not whether a run is inside a gradient. This keeps one continuous background when bold/italic formatting changes within the range. The default gradient uses the site's shadcn chart palette: `--chart-1` → `--chart-2`, following light/dark theme changes. Missing chart tokens fall back to `--primary` / `--muted-foreground`, then `currentColor`, never a hardcoded palette. Set `--camox-gradient-from` and `--camox-gradient-to` in app CSS for app-wide colors, or return `style.backgroundImage` when `gradient` is true.
341
+ - Styles apply to inline children; the outer element's `className` stays on that element and is inherited normally. Explicit formatting defaults (combined italic, underline, gradient clipping) use inline styles; use `style` to override those defaults rather than competing utility classes.
342
+ - These props accept appearance only, never replacement elements, children, or event handlers. They also work on `item.Field` inside repeaters. The sidebar editor shows default formatting; field-specific styles apply on the page.
324
343
 
325
344
  When the content needs to be placed inside a more complex structure, use `props.children` explicitly:
326
345
 
@@ -71,6 +71,8 @@ blocks: {
71
71
  }
72
72
  ```
73
73
 
74
+ For a navbar or footer whose content must stay identical **across different layouts**, also set `synced: true` in its `createBlock` options. `layoutOnly: true` alone only restricts placement; each layout otherwise has independent data. Synced blocks share content, settings, and repeaters and appear purple in the editor. See [Synced blocks](block-definitions.md#synced-blocks-synced-optional).
75
+
74
76
  You can have multiple blocks in either group, or leave one empty:
75
77
 
76
78
  ```tsx
@@ -1,24 +0,0 @@
1
- import { FORMAT_FLAGS, lexicalTextToMarkdown } from "./modifierFormats.js";
2
- import "react";
3
- import { jsx } from "react/jsx-runtime";
4
-
5
- //#region src/core/lib/modifiers.tsx
6
- const TEXT_MODIFIERS = {
7
- bold: {
8
- formatFlag: FORMAT_FLAGS.bold,
9
- label: "Bold",
10
- icon: "B",
11
- render: (children) => /* @__PURE__ */ jsx("strong", { children }),
12
- toMarkdown: (text) => `**${text}**`
13
- },
14
- italic: {
15
- formatFlag: FORMAT_FLAGS.italic,
16
- label: "Italic",
17
- icon: "I",
18
- render: (children) => /* @__PURE__ */ jsx("em", { children }),
19
- toMarkdown: (text) => `*${text}*`
20
- }
21
- };
22
-
23
- //#endregion
24
- export { TEXT_MODIFIERS };
@@ -1,23 +0,0 @@
1
- import { getApiClient } from "../../lib/api-client.js";
2
- import { seedBlockCaches } from "../../lib/normalized-data.js";
3
-
4
- //#region src/features/preview/previewQueryFns.ts
5
- function pageFullQueryFn(queryClient, path, projectSlug, source) {
6
- return async () => {
7
- const data = await getApiClient().pages.getByPath({
8
- path,
9
- projectSlug,
10
- source
11
- });
12
- seedBlockCaches(queryClient, data, source);
13
- return {
14
- page: data.page,
15
- layout: data.layout,
16
- projectName: data.projectName,
17
- project: data.project
18
- };
19
- };
20
- }
21
-
22
- //#endregion
23
- export { pageFullQueryFn };
@@ -1,120 +0,0 @@
1
- import { NavigationProvider } from "../navigation/navigation.js";
2
- import { c } from "react/compiler-runtime";
3
- import "react";
4
- import { jsx } from "react/jsx-runtime";
5
-
6
- //#region src/features/runtime/studioNavigation.tsx
7
- function normalizeRuntimeBasePath(basePath) {
8
- if (!basePath || basePath === "/") return "";
9
- return basePath.startsWith("/") ? basePath.replace(/\/+$/, "") : `/${basePath.replace(/\/+$/, "")}`;
10
- }
11
- function normalizeRuntimePathname(pathname, runtimeBasePath) {
12
- const basePath = normalizeRuntimeBasePath(runtimeBasePath);
13
- if (!basePath) return pathname || "/";
14
- if (pathname === basePath) return "/";
15
- if (pathname.startsWith(`${basePath}/`)) return pathname.slice(basePath.length) || "/";
16
- return pathname || "/";
17
- }
18
- function toRuntimePathname(pathname, runtimeBasePath) {
19
- const basePath = normalizeRuntimeBasePath(runtimeBasePath);
20
- const normalizedPathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
21
- if (!basePath) return normalizedPathname;
22
- if (normalizedPathname === "/") return `${basePath}/`;
23
- if (normalizedPathname.startsWith(`${basePath}/`)) return normalizedPathname;
24
- return `${basePath}${normalizedPathname}`;
25
- }
26
- function getStudioLocation(runtimeBasePath) {
27
- return {
28
- hash: window.location.hash,
29
- href: window.location.href,
30
- pathname: normalizeRuntimePathname(window.location.pathname, runtimeBasePath),
31
- search: window.location.search
32
- };
33
- }
34
- function getStudioTarget(to, runtimeBasePath) {
35
- const url = new URL(to, window.location.href);
36
- if (url.origin !== window.location.origin) return null;
37
- url.pathname = toRuntimePathname(normalizeRuntimePathname(url.pathname, runtimeBasePath), runtimeBasePath);
38
- return url;
39
- }
40
- function StudioNavigationProvider(t0) {
41
- const $ = c(19);
42
- if ($[0] !== "8131144acb7b9faccf5f5924d863b9186349bfc7c842e7feba7a3ac1e01ed910") {
43
- for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
44
- $[0] = "8131144acb7b9faccf5f5924d863b9186349bfc7c842e7feba7a3ac1e01ed910";
45
- }
46
- const { children, href, pathname, runtimeBasePath } = t0;
47
- let t1;
48
- if ($[1] !== runtimeBasePath) {
49
- t1 = async (t2) => {
50
- const { replace, to } = t2;
51
- const target = getStudioTarget(to, runtimeBasePath);
52
- if (!target) {
53
- window.location.assign(to);
54
- return;
55
- }
56
- const currentPathname = normalizeRuntimePathname(window.location.pathname, runtimeBasePath);
57
- if (normalizeRuntimePathname(target.pathname, runtimeBasePath) !== currentPathname) {
58
- window.location.assign(target.href);
59
- return;
60
- }
61
- if (replace) window.history.replaceState(null, "", target.href);
62
- else window.history.pushState(null, "", target.href);
63
- window.dispatchEvent(new Event("camox:navigation"));
64
- };
65
- $[1] = runtimeBasePath;
66
- $[2] = t1;
67
- } else t1 = $[2];
68
- const navigate = t1;
69
- let t2;
70
- if ($[3] !== runtimeBasePath) {
71
- t2 = () => getStudioLocation(runtimeBasePath);
72
- $[3] = runtimeBasePath;
73
- $[4] = t2;
74
- } else t2 = $[4];
75
- const getLocation = t2;
76
- let t3;
77
- if ($[5] !== href) {
78
- t3 = new URL(href);
79
- $[5] = href;
80
- $[6] = t3;
81
- } else t3 = $[6];
82
- let t4;
83
- if ($[7] !== href) {
84
- t4 = new URL(href);
85
- $[7] = href;
86
- $[8] = t4;
87
- } else t4 = $[8];
88
- let t5;
89
- if ($[9] !== href || $[10] !== pathname || $[11] !== t3.hash || $[12] !== t4.search) {
90
- t5 = {
91
- hash: t3.hash,
92
- href,
93
- pathname,
94
- search: t4.search
95
- };
96
- $[9] = href;
97
- $[10] = pathname;
98
- $[11] = t3.hash;
99
- $[12] = t4.search;
100
- $[13] = t5;
101
- } else t5 = $[13];
102
- let t6;
103
- if ($[14] !== children || $[15] !== getLocation || $[16] !== navigate || $[17] !== t5) {
104
- t6 = /* @__PURE__ */ jsx(NavigationProvider, {
105
- getLocation,
106
- initialLocation: t5,
107
- navigate,
108
- children
109
- });
110
- $[14] = children;
111
- $[15] = getLocation;
112
- $[16] = navigate;
113
- $[17] = t5;
114
- $[18] = t6;
115
- } else t6 = $[18];
116
- return t6;
117
- }
118
-
119
- //#endregion
120
- export { StudioNavigationProvider };