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.
- package/dist/core/createApp.d.ts +19 -6
- package/dist/core/createApp.js +7 -2
- package/dist/core/createBlock.js +54 -43
- package/dist/core/createCollection.d.ts +25 -7
- package/dist/core/createCollection.js +28 -9
- package/dist/core/createLayout.d.ts +98 -71
- package/dist/core/createLayout.js +10 -16
- package/dist/core/derivedRoutes.js +1 -1
- package/dist/core/editing/BlockEditingRuntime.d.ts +3 -2
- package/dist/core/editing/CompleteBlockEditingRuntime.js +2 -1
- package/dist/core/editing/createEditableBlock.d.ts +59 -19
- package/dist/core/editing/createEditableBlock.js +162 -151
- package/dist/core/editing/useLatestRecord.js +7 -7
- package/dist/core/layoutContext.d.ts +55 -0
- package/dist/core/layoutContext.js +66 -0
- package/dist/core/lib/contentType.d.ts +91 -4
- package/dist/core/lib/contentType.js +84 -7
- package/dist/core/lib/recordScope.js +19 -17
- package/dist/core/lib/reference.d.ts +6 -2
- package/dist/core/recordItem.js +55 -2
- package/dist/core/requestContext.d.ts +18 -0
- package/dist/core/structuredData.d.ts +126 -0
- package/dist/core/structuredData.js +40 -0
- package/dist/features/canvas/canvasCamera.js +1 -1
- package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
- package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +1 -1
- package/dist/features/content/ContentAssets.js +84 -88
- package/dist/features/content/ContentCollection.js +13 -7
- package/dist/features/content/collection-form.js +6 -2
- package/dist/features/content/components/MissingFromLocale.js +1 -1
- package/dist/features/og/pageImage.js +89 -0
- package/dist/features/page/CuratedPageContent.js +45 -32
- package/dist/features/page/DerivedPageContent.js +49 -22
- package/dist/features/page/SharedLayoutContent.js +26 -22
- package/dist/features/preview/CamoxPreview.js +41 -27
- package/dist/features/preview/EditablePageExperience.js +17 -9
- package/dist/features/preview/components/AddBlockSheet.js +1 -0
- package/dist/features/preview/components/BlockActionsPopover.js +19 -41
- package/dist/features/preview/components/FieldToolbar.js +1 -1
- package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
- package/dist/features/preview/components/PageInfo.js +244 -264
- package/dist/features/preview/components/PageTree.js +63 -53
- package/dist/features/preview/components/PreviewPanel.js +10 -64
- package/dist/features/preview/components/PreviewToolbar.js +1 -1
- package/dist/features/preview/components/RecordCard.js +50 -34
- package/dist/features/preview/components/RecordCombobox.js +12 -5
- package/dist/features/preview/components/ReferenceFieldEditor.js +4 -2
- package/dist/features/preview/components/ReferenceListFieldEditor.js +24 -19
- package/dist/features/preview/components/RepeatableItemsList.js +62 -46
- package/dist/features/preview/components/SelectionEditor.js +3 -2
- package/dist/features/preview/components/useFieldWriter.js +2 -4
- package/dist/features/preview/components/useRecordView.js +2 -2
- package/dist/features/preview/entityName.js +11 -0
- package/dist/features/preview/pageRecord.js +17 -18
- package/dist/features/preview/previewConstants.js +2 -8
- package/dist/features/preview/usePreviewViewportActions.js +1 -1
- package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
- package/dist/features/provider/components/CommandPalette.js +2 -5
- package/dist/features/provider/signInShortcut.js +1 -2
- package/dist/features/provider/useAdminShortcuts.js +47 -53
- package/dist/features/routes/pageRuntime.d.ts +17 -2
- package/dist/features/routes/pageRuntime.js +61 -57
- package/dist/features/runtime/RuntimeChrome.js +1 -1
- package/dist/features/runtime/breadcrumbs.js +68 -0
- package/dist/features/runtime/livePages.js +3 -2
- package/dist/features/runtime/pageGraph.js +92 -0
- package/dist/features/runtime/projectLogo.js +110 -0
- package/dist/features/runtime/publicCache.js +83 -0
- package/dist/features/runtime/runtime.d.ts +24 -9
- package/dist/features/runtime/runtime.js +447 -130
- package/dist/features/runtime/runtimeRoutes.d.ts +1 -0
- package/dist/features/runtime/runtimeRoutes.js +1 -0
- package/dist/features/runtime/structuredData.js +213 -0
- package/dist/features/runtime/worker.d.ts +5 -1
- package/dist/features/runtime/worker.js +20 -3
- package/dist/features/settings/AnalyticsSettings.js +622 -0
- package/dist/features/settings/CamoxSettings.js +95 -0
- package/dist/features/settings/FaviconSettings.js +254 -0
- package/dist/features/settings/ProjectNameSettings.js +145 -0
- package/dist/features/settings/SettingsPart.js +73 -0
- package/dist/features/settings/SocialProfilesSettings.js +245 -0
- package/dist/features/settings/useProjectMutation.js +61 -0
- package/dist/features/studio/components/ChatSidebar.js +5 -5
- package/dist/features/studio/components/DraftsControl.js +136 -131
- package/dist/features/studio/components/LocalePicker.js +178 -174
- package/dist/features/studio/components/Navbar.js +107 -66
- package/dist/features/studio/components/ProjectMenu.js +85 -110
- package/dist/features/studio/components/ReviewDraftSheet.js +1046 -990
- package/dist/features/studio/components/ThreadMessages.js +2 -2
- package/dist/features/studio/components/UserButton.js +1 -1
- package/dist/features/studio/routes.js +3 -1
- package/dist/features/studio/showInCanvas.js +129 -0
- package/dist/features/vite/blockBoilerplate.js +3 -2
- package/dist/features/vite/definitionsSync.js +4 -5
- package/dist/features/vite/layoutGeneration.js +3 -2
- package/dist/features/vite/runtimeDev.js +32 -17
- package/dist/features/vite/staticAssetHeaders.js +52 -0
- package/dist/features/vite/vite.d.ts +1 -1
- package/dist/features/vite/vite.js +17 -4
- package/dist/lib/api-client-server.d.ts +1 -1
- package/dist/lib/api-client.d.ts +2 -1
- package/dist/lib/draft-scope.d.ts +1 -0
- package/dist/lib/locale-path.d.ts +3 -11
- package/dist/lib/locale-path.js +3 -27
- package/dist/lib/queries.d.ts +4 -0
- package/dist/lib/queries.js +8 -1
- package/dist/lib/shortcuts.js +29 -0
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/studio-reads.js +3 -1
- package/dist/lib/utils.js +1 -104
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +6 -4
- package/skills/camox/references/block-definitions.md +105 -25
- package/skills/camox/references/layout-definitions.md +53 -17
- package/skills/camox/references/routed-layouts.md +17 -14
- 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";
|
|
@@ -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
|
-
|
|
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
|
|
34
|
+
const response = await respond(request, context);
|
|
18
35
|
if (!response) return notFound();
|
|
19
36
|
return withDevHtml(response, devHtml);
|
|
20
37
|
} };
|