camox 0.47.0 → 0.51.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 (117) hide show
  1. package/dist/core/createApp.d.ts +19 -6
  2. package/dist/core/createApp.js +7 -2
  3. package/dist/core/createBlock.js +54 -43
  4. package/dist/core/createCollection.d.ts +25 -7
  5. package/dist/core/createCollection.js +28 -9
  6. package/dist/core/createLayout.d.ts +98 -71
  7. package/dist/core/createLayout.js +10 -16
  8. package/dist/core/derivedRoutes.js +1 -1
  9. package/dist/core/editing/BlockEditingRuntime.d.ts +3 -2
  10. package/dist/core/editing/CompleteBlockEditingRuntime.js +2 -1
  11. package/dist/core/editing/createEditableBlock.d.ts +59 -19
  12. package/dist/core/editing/createEditableBlock.js +162 -151
  13. package/dist/core/editing/useLatestRecord.js +7 -7
  14. package/dist/core/layoutContext.d.ts +55 -0
  15. package/dist/core/layoutContext.js +66 -0
  16. package/dist/core/lib/contentType.d.ts +91 -4
  17. package/dist/core/lib/contentType.js +84 -7
  18. package/dist/core/lib/recordScope.js +19 -17
  19. package/dist/core/lib/reference.d.ts +6 -2
  20. package/dist/core/recordItem.js +55 -2
  21. package/dist/core/requestContext.d.ts +18 -0
  22. package/dist/core/structuredData.d.ts +126 -0
  23. package/dist/core/structuredData.js +40 -0
  24. package/dist/features/canvas/canvasCamera.js +1 -1
  25. package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
  26. package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +1 -1
  27. package/dist/features/content/ContentAssets.js +84 -88
  28. package/dist/features/content/ContentCollection.js +13 -7
  29. package/dist/features/content/collection-form.js +6 -2
  30. package/dist/features/content/components/MissingFromLocale.js +1 -1
  31. package/dist/features/og/pageImage.js +89 -0
  32. package/dist/features/page/CuratedPageContent.js +45 -32
  33. package/dist/features/page/DerivedPageContent.js +49 -22
  34. package/dist/features/page/SharedLayoutContent.js +26 -22
  35. package/dist/features/preview/CamoxPreview.js +41 -27
  36. package/dist/features/preview/EditablePageExperience.js +17 -9
  37. package/dist/features/preview/components/AddBlockSheet.js +1 -0
  38. package/dist/features/preview/components/BlockActionsPopover.js +19 -41
  39. package/dist/features/preview/components/FieldToolbar.js +1 -1
  40. package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
  41. package/dist/features/preview/components/PageInfo.js +244 -264
  42. package/dist/features/preview/components/PageTree.js +63 -53
  43. package/dist/features/preview/components/PreviewPanel.js +10 -64
  44. package/dist/features/preview/components/PreviewToolbar.js +1 -1
  45. package/dist/features/preview/components/RecordCard.js +50 -34
  46. package/dist/features/preview/components/RecordCombobox.js +12 -5
  47. package/dist/features/preview/components/ReferenceFieldEditor.js +4 -2
  48. package/dist/features/preview/components/ReferenceListFieldEditor.js +24 -19
  49. package/dist/features/preview/components/RepeatableItemsList.js +62 -46
  50. package/dist/features/preview/components/SelectionEditor.js +3 -2
  51. package/dist/features/preview/components/useFieldWriter.js +2 -4
  52. package/dist/features/preview/components/useRecordView.js +2 -2
  53. package/dist/features/preview/entityName.js +11 -0
  54. package/dist/features/preview/pageRecord.js +17 -18
  55. package/dist/features/preview/previewConstants.js +2 -8
  56. package/dist/features/preview/usePreviewViewportActions.js +1 -1
  57. package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
  58. package/dist/features/provider/components/CommandPalette.js +2 -5
  59. package/dist/features/provider/signInShortcut.js +1 -2
  60. package/dist/features/provider/useAdminShortcuts.js +47 -53
  61. package/dist/features/routes/pageRuntime.d.ts +17 -2
  62. package/dist/features/routes/pageRuntime.js +61 -57
  63. package/dist/features/runtime/RuntimeChrome.js +1 -1
  64. package/dist/features/runtime/breadcrumbs.js +68 -0
  65. package/dist/features/runtime/livePages.js +3 -2
  66. package/dist/features/runtime/pageGraph.js +92 -0
  67. package/dist/features/runtime/projectLogo.js +110 -0
  68. package/dist/features/runtime/publicCache.js +83 -0
  69. package/dist/features/runtime/runtime.d.ts +24 -9
  70. package/dist/features/runtime/runtime.js +447 -130
  71. package/dist/features/runtime/runtimeRoutes.d.ts +1 -0
  72. package/dist/features/runtime/runtimeRoutes.js +1 -0
  73. package/dist/features/runtime/structuredData.js +213 -0
  74. package/dist/features/runtime/worker.d.ts +5 -1
  75. package/dist/features/runtime/worker.js +20 -3
  76. package/dist/features/settings/AnalyticsSettings.js +622 -0
  77. package/dist/features/settings/CamoxSettings.js +95 -0
  78. package/dist/features/settings/FaviconSettings.js +254 -0
  79. package/dist/features/settings/ProjectNameSettings.js +145 -0
  80. package/dist/features/settings/SettingsPart.js +73 -0
  81. package/dist/features/settings/SocialProfilesSettings.js +245 -0
  82. package/dist/features/settings/useProjectMutation.js +61 -0
  83. package/dist/features/studio/components/ChatSidebar.js +5 -5
  84. package/dist/features/studio/components/DraftsControl.js +136 -131
  85. package/dist/features/studio/components/LocalePicker.js +178 -174
  86. package/dist/features/studio/components/Navbar.js +107 -66
  87. package/dist/features/studio/components/ProjectMenu.js +85 -110
  88. package/dist/features/studio/components/ReviewDraftSheet.js +1046 -990
  89. package/dist/features/studio/components/ThreadMessages.js +2 -2
  90. package/dist/features/studio/components/UserButton.js +1 -1
  91. package/dist/features/studio/routes.js +3 -1
  92. package/dist/features/studio/showInCanvas.js +129 -0
  93. package/dist/features/vite/blockBoilerplate.js +3 -2
  94. package/dist/features/vite/definitionsSync.js +4 -5
  95. package/dist/features/vite/layoutGeneration.js +3 -2
  96. package/dist/features/vite/runtimeDev.js +32 -17
  97. package/dist/features/vite/staticAssetHeaders.js +52 -0
  98. package/dist/features/vite/vite.d.ts +1 -1
  99. package/dist/features/vite/vite.js +17 -4
  100. package/dist/lib/api-client-server.d.ts +1 -1
  101. package/dist/lib/api-client.d.ts +2 -1
  102. package/dist/lib/draft-scope.d.ts +1 -0
  103. package/dist/lib/locale-path.d.ts +3 -11
  104. package/dist/lib/locale-path.js +3 -27
  105. package/dist/lib/queries.d.ts +4 -0
  106. package/dist/lib/queries.js +8 -1
  107. package/dist/lib/shortcuts.js +29 -0
  108. package/dist/lib/studio-reads.d.ts +1 -0
  109. package/dist/lib/studio-reads.js +3 -1
  110. package/dist/lib/utils.js +1 -104
  111. package/dist/package.js +1 -1
  112. package/dist/studio.css +1 -1
  113. package/package.json +6 -4
  114. package/skills/camox/references/block-definitions.md +105 -25
  115. package/skills/camox/references/layout-definitions.md +53 -17
  116. package/skills/camox/references/routed-layouts.md +17 -14
  117. package/dist/features/studio/components/FaviconDialog.js +0 -318
@@ -11,6 +11,7 @@ export declare const RUNTIME_ROUTE_PATHS: {
11
11
  readonly health: "/_camox/health";
12
12
  readonly registry: "/_camox/registry";
13
13
  readonly routes: "/_camox/routes";
14
+ readonly logo: "/_camox/logo.png";
14
15
  readonly og: "/og";
15
16
  readonly sitemap: "/sitemap.xml";
16
17
  readonly "llms-txt": "/llms.txt";
@@ -11,6 +11,7 @@ const RUNTIME_ROUTE_PATHS = {
11
11
  health: "/_camox/health",
12
12
  registry: "/_camox/registry",
13
13
  routes: "/_camox/routes",
14
+ logo: "/_camox/logo.png",
14
15
  og: "/og",
15
16
  sitemap: "/sitemap.xml",
16
17
  "llms-txt": "/llms.txt"
@@ -0,0 +1,213 @@
1
+ import { isArticle, isCreativeWork, isWebPageKind } from "../../core/structuredData.js";
2
+ import { isPageUrlMarker, linkedRecordId } from "@camox/api-contract";
3
+ import { createSchemaOrgGraph, defineBreadcrumb, defineOrganization, defineWebPage, defineWebSite } from "@unhead/schema-org";
4
+
5
+ //#region src/features/runtime/structuredData.ts
6
+ /**
7
+ * A value of a node the API evaluated, with the absolute URL of each page its markers name.
8
+ * Only the runtime knows the website's address.
9
+ */
10
+ function withPageUrls(value, pageUrl) {
11
+ if (isPageUrlMarker(value)) return pageUrl(value);
12
+ if (Array.isArray(value)) return value.map((entry) => withPageUrls(entry, pageUrl));
13
+ if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, withPageUrls(entry, pageUrl)]));
14
+ return value;
15
+ }
16
+ /** The `@id`s of the site's nodes, derived from the website's address. */
17
+ function siteNodeIds(siteUrl) {
18
+ return {
19
+ organization: `${siteUrl}#identity`,
20
+ website: `${siteUrl}#website`
21
+ };
22
+ }
23
+ /** The Organization publishing the website, and the WebSite itself. */
24
+ function siteNodes(site, inLanguage) {
25
+ const ids = siteNodeIds(site.url);
26
+ return [defineOrganization({
27
+ "@id": ids.organization,
28
+ name: site.name,
29
+ url: site.url,
30
+ logo: {
31
+ url: site.logo.url,
32
+ width: site.logo.width,
33
+ height: site.logo.height
34
+ },
35
+ ...site.socialProfiles.length > 0 && { sameAs: [...site.socialProfiles] }
36
+ }), defineWebSite({
37
+ "@id": ids.website,
38
+ name: site.name,
39
+ url: site.url,
40
+ inLanguage,
41
+ publisher: { "@id": ids.organization }
42
+ })];
43
+ }
44
+ /** The `@id`s of a page's nodes, derived from its URL. */
45
+ function pageNodeIds(pageUrl) {
46
+ return {
47
+ page: `${pageUrl}#webpage`,
48
+ mainEntity: `${pageUrl}#main-entity`
49
+ };
50
+ }
51
+ /**
52
+ * The page's own node, part of the WebSite, of the kind its layout says, or a plain WebPage,
53
+ * linking its main entity and the records it mentions.
54
+ */
55
+ function pageNode(page, site, declared, { mainEntity, mentions }) {
56
+ const kind = declared && isWebPageKind(declared) ? declared : void 0;
57
+ return defineWebPage({
58
+ ...kind,
59
+ "@type": kind?.["@type"] ?? "WebPage",
60
+ "@id": pageNodeIds(page.url).page,
61
+ url: page.url,
62
+ name: page.name,
63
+ ...page.description && { description: page.description },
64
+ inLanguage: page.inLanguage,
65
+ isPartOf: { "@id": siteNodeIds(site.url).website },
66
+ ...page.image && { primaryImageOfPage: { url: page.image } },
67
+ ...page.datePublished && { datePublished: page.datePublished.toISOString() },
68
+ ...page.dateModified && { dateModified: page.dateModified.toISOString() },
69
+ ...mainEntity && { mainEntity: { "@id": mainEntity } },
70
+ ...mentions.length > 0 && { mentions: mentions.map((id) => ({ "@id": id })) }
71
+ });
72
+ }
73
+ /**
74
+ * The thing a page is about, as its own node, filled from the page where its layout leaves
75
+ * properties out: its name or headline, description, URL and image for every type, and its
76
+ * dates, publisher and language for creative works.
77
+ */
78
+ function mainEntityNode(entity, page, site, id) {
79
+ const work = isCreativeWork(entity);
80
+ return {
81
+ ...isArticle(entity) ? { headline: page.name } : { name: page.name },
82
+ ...page.description && { description: page.description },
83
+ url: page.url,
84
+ ...page.image && { image: page.image },
85
+ ...work && {
86
+ ...page.datePublished && { datePublished: page.datePublished.toISOString() },
87
+ ...page.dateModified && { dateModified: page.dateModified.toISOString() },
88
+ publisher: { "@id": siteNodeIds(site.url).organization },
89
+ inLanguage: page.inLanguage
90
+ },
91
+ ...entity,
92
+ "@id": id,
93
+ mainEntityOfPage: { "@id": pageNodeIds(page.url).page }
94
+ };
95
+ }
96
+ /** The page's breadcrumb, which the page node links. None for a page without crumbs. */
97
+ function breadcrumbNodes(page) {
98
+ if (!page.breadcrumb?.length) return [];
99
+ return [defineBreadcrumb({
100
+ "@id": `${page.url}#breadcrumb`,
101
+ itemListElement: page.breadcrumb.map((crumb) => ({
102
+ name: crumb.name,
103
+ item: crumb.url
104
+ }))
105
+ })];
106
+ }
107
+ /**
108
+ * The things the page's blocks show, each its own node, linked to the page: creative works are
109
+ * part of it, and it is about anything else. Every placement's nodes have their own `@id`s.
110
+ */
111
+ function blockNodes(page, blocks = []) {
112
+ const pageId = { "@id": pageNodeIds(page.url).page };
113
+ return blocks.flatMap(({ id, nodes }) => nodes.map((node, index) => ({
114
+ ...node,
115
+ "@id": `${page.url}#block-${id}-${index}`,
116
+ [isCreativeWork(node) ? "isPartOf" : "subjectOf"]: pageId
117
+ })));
118
+ }
119
+ /**
120
+ * The records a graph places: links to records with nodes become their `@id`s and place the
121
+ * records, and links to records without nodes read as their titles, or are left out.
122
+ */
123
+ function recordPlacement(records) {
124
+ const placed = /* @__PURE__ */ new Set();
125
+ const pending = [];
126
+ const place = (id) => {
127
+ if (placed.has(id)) return;
128
+ placed.add(id);
129
+ pending.push(id);
130
+ };
131
+ const link = (value) => {
132
+ const id = linkedRecordId(value);
133
+ if (id !== void 0) {
134
+ if (!records?.nodes[id]) return records?.titles?.[id];
135
+ place(id);
136
+ return { "@id": records.id(id) };
137
+ }
138
+ if (Array.isArray(value)) return value.map(link).filter((entry) => entry !== void 0);
139
+ if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).flatMap(([key, entry]) => {
140
+ const linked = link(entry);
141
+ return linked === void 0 ? [] : [[key, linked]];
142
+ }));
143
+ return value;
144
+ };
145
+ return {
146
+ placed,
147
+ place,
148
+ /** A value with its links resolved. */
149
+ link: (value) => link(value),
150
+ /** The nodes of the records placed since last asked, and of those they link in turn. */
151
+ nodes: () => {
152
+ const nodes = [];
153
+ for (let id = pending.shift(); id !== void 0; id = pending.shift()) {
154
+ const { "@id": _own, ...node } = records.nodes[id];
155
+ nodes.push({
156
+ ...link(node),
157
+ "@id": records.id(id)
158
+ });
159
+ }
160
+ return nodes;
161
+ }
162
+ };
163
+ }
164
+ /** Every node a page's graph holds, before they are resolved and linked. */
165
+ function graphNodes({ site, page, declared, blocks, records }) {
166
+ const placement = recordPlacement(records);
167
+ const linked = declared && placement.link(declared);
168
+ const blockList = blockNodes(page, blocks).map(placement.link);
169
+ const mainRecord = !declared && records?.main && records.nodes[records.main] && records.main;
170
+ let mainEntity;
171
+ if (linked && !isWebPageKind(linked)) mainEntity = mainEntityNode(linked, page, site, pageNodeIds(page.url).mainEntity);
172
+ if (mainRecord) placement.place(mainRecord);
173
+ const placedRecords = placement.nodes();
174
+ if (mainRecord) {
175
+ const index = placedRecords.findIndex((node) => node["@id"] === records.id(mainRecord));
176
+ const [node] = placedRecords.splice(index, 1);
177
+ mainEntity = mainEntityNode(node, page, site, records.id(mainRecord));
178
+ }
179
+ const mentions = (records?.referenced ?? []).filter((id) => records.nodes[id] && !placement.placed.has(id));
180
+ for (const id of mentions) placement.place(id);
181
+ const recordNodes = [...placedRecords, ...placement.nodes()];
182
+ return [
183
+ ...siteNodes(site, page.inLanguage),
184
+ pageNode(page, site, linked, {
185
+ mainEntity: mainEntity?.["@id"],
186
+ mentions: mentions.map((id) => records.id(id))
187
+ }),
188
+ ...mainEntity ? [mainEntity] : [],
189
+ ...breadcrumbNodes(page),
190
+ ...blockList,
191
+ ...recordNodes
192
+ ];
193
+ }
194
+ /** A page's graph, resolved into JSON-LD. */
195
+ function buildPageGraph(input) {
196
+ const graph = createSchemaOrgGraph();
197
+ graph.push(graphNodes(input));
198
+ return {
199
+ "@context": "https://schema.org",
200
+ "@graph": graph.resolveGraph({
201
+ host: input.site.url,
202
+ url: input.page.url,
203
+ inLanguage: input.page.inLanguage
204
+ })
205
+ };
206
+ }
207
+ /** JSON-LD as a script's text: `<` is escaped, so content can't close the script. */
208
+ function serializeStructuredData(data) {
209
+ return JSON.stringify(data).replaceAll("<", "\\u003c");
210
+ }
211
+
212
+ //#endregion
213
+ export { buildPageGraph, serializeStructuredData, siteNodeIds, withPageUrls };
@@ -1,6 +1,10 @@
1
1
  //#region src/features/runtime/worker.d.ts
2
+ /** What the Worker gets with each request: only `waitUntil` matters here. */
3
+ interface WorkerContext {
4
+ waitUntil(promise: Promise<unknown>): void;
5
+ }
2
6
  declare const _default: {
3
- fetch(request: Request): Promise<Response>;
7
+ fetch(request: Request, _env?: unknown, context?: WorkerContext): Promise<Response>;
4
8
  };
5
9
  //#endregion
6
10
  export { _default as default };
@@ -1,6 +1,7 @@
1
1
  import { fetchLocalhostOnLoopback } from "./loopbackFetch.js";
2
+ import { readLiveCacheKey, respondThroughPublicCache } from "./publicCache.js";
2
3
  import { shouldHandleRequest, withDevHtml } from "./workerRequest.js";
3
- import { devHtml, handleCamoxRequest, runtimeBasePath } from "virtual:camox/server";
4
+ import { devHtml, handleCamoxRequest, isPublicRequest, publicCacheSource, runtimeBasePath } from "virtual:camox/server";
4
5
 
5
6
  //#region src/features/runtime/worker.ts
6
7
  /**
@@ -12,9 +13,25 @@ if (devHtml) globalThis.fetch = fetchLocalhostOnLoopback(globalThis.fetch.bind(g
12
13
  function notFound() {
13
14
  return new Response("Not found", { status: 404 });
14
15
  }
15
- var worker_default = { async fetch(request) {
16
+ /**
17
+ * Answers a request, from the edge cache when every visitor sees the same. Dev renders the
18
+ * developer's development draft, which nothing shares, and a Worker calling this one without
19
+ * its context can't keep anything past the response.
20
+ */
21
+ function respond(request, context) {
22
+ const cache = globalThis.caches?.default;
23
+ if (devHtml || !context || !cache || !isPublicRequest(request)) return handleCamoxRequest(request);
24
+ const { apiUrl, projectUuid, build } = publicCacheSource;
25
+ return respondThroughPublicCache(request, {
26
+ cache,
27
+ liveCacheKey: () => readLiveCacheKey(apiUrl, projectUuid),
28
+ build,
29
+ waitUntil: (promise) => context.waitUntil(promise)
30
+ }, () => handleCamoxRequest(request));
31
+ }
32
+ var worker_default = { async fetch(request, _env, context) {
16
33
  if (!shouldHandleRequest(request, runtimeBasePath)) return notFound();
17
- const response = await handleCamoxRequest(request);
34
+ const response = await respond(request, context);
18
35
  if (!response) return notFound();
19
36
  return withDevHtml(response, devHtml);
20
37
  } };