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
@@ -3,6 +3,7 @@ import { repeatableItemMutations } from "../../../lib/queries.js";
3
3
  import { previewStore, selectionItemId } from "../previewStore.js";
4
4
  import { useEditingFrame } from "../editingFrame.js";
5
5
  import { PreviewEditingOwnerContext, usePreviewTargetSelection } from "../previewSelection.js";
6
+ import { entityName } from "../entityName.js";
6
7
  import { useRepeatableItemActions } from "./useRepeatableItemActions.js";
7
8
  import { c } from "react/compiler-runtime";
8
9
  import { useContext } from "react";
@@ -18,7 +19,7 @@ import { CSS } from "@dnd-kit/utilities";
18
19
 
19
20
  //#region src/features/preview/components/RepeatableItemsList.tsx
20
21
  const SortableRepeatableItem = (t0) => {
21
- const $ = c(47);
22
+ const $ = c(53);
22
23
  const { item, fieldName, blockId, canRemove, onRemove } = t0;
23
24
  const owner = useContext(PreviewEditingOwnerContext);
24
25
  let t1;
@@ -113,9 +114,17 @@ const SortableRepeatableItem = (t0) => {
113
114
  $[23] = listeners;
114
115
  $[24] = t13;
115
116
  } else t13 = $[24];
116
- let t14;
117
- if ($[25] !== blockId || $[26] !== handleMouseLeave || $[27] !== item.id || $[28] !== owner) {
118
- t14 = () => {
117
+ const t14 = `${fieldName} (${item.id})`;
118
+ let t15;
119
+ if ($[25] !== item || $[26] !== t14) {
120
+ t15 = entityName(item, t14);
121
+ $[25] = item;
122
+ $[26] = t14;
123
+ $[27] = t15;
124
+ } else t15 = $[27];
125
+ let t16;
126
+ if ($[28] !== blockId || $[29] !== handleMouseLeave || $[30] !== item.id || $[31] !== owner) {
127
+ t16 = () => {
119
128
  if (owner === null) return;
120
129
  handleMouseLeave();
121
130
  previewStore.send({
@@ -125,32 +134,39 @@ const SortableRepeatableItem = (t0) => {
125
134
  itemId: item.id
126
135
  });
127
136
  };
128
- $[25] = blockId;
129
- $[26] = handleMouseLeave;
130
- $[27] = item.id;
131
- $[28] = owner;
132
- $[29] = t14;
133
- } else t14 = $[29];
134
- const t15 = item.summary || `${fieldName} (${item.id})`;
135
- let t16;
136
- if ($[30] !== item.summary || $[31] !== t14 || $[32] !== t15) {
137
- t16 = /* @__PURE__ */ jsx("div", {
137
+ $[28] = blockId;
138
+ $[29] = handleMouseLeave;
139
+ $[30] = item.id;
140
+ $[31] = owner;
141
+ $[32] = t16;
142
+ } else t16 = $[32];
143
+ const t17 = `${fieldName} (${item.id})`;
144
+ let t18;
145
+ if ($[33] !== item || $[34] !== t17) {
146
+ t18 = entityName(item, t17);
147
+ $[33] = item;
148
+ $[34] = t17;
149
+ $[35] = t18;
150
+ } else t18 = $[35];
151
+ let t19;
152
+ if ($[36] !== t15 || $[37] !== t16 || $[38] !== t18) {
153
+ t19 = /* @__PURE__ */ jsx("div", {
138
154
  className: "flex flex-1 items-center gap-1 overflow-x-hidden",
139
155
  children: /* @__PURE__ */ jsx("p", {
140
156
  className: "flex-1 cursor-default truncate py-1 text-sm",
141
- title: item.summary,
142
- onClick: t14,
143
- children: t15
157
+ title: t15,
158
+ onClick: t16,
159
+ children: t18
144
160
  })
145
161
  });
146
- $[30] = item.summary;
147
- $[31] = t14;
148
- $[32] = t15;
149
- $[33] = t16;
150
- } else t16 = $[33];
151
- let t17;
152
- if ($[34] !== canRemove || $[35] !== item.id || $[36] !== onRemove) {
153
- t17 = canRemove && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
162
+ $[36] = t15;
163
+ $[37] = t16;
164
+ $[38] = t18;
165
+ $[39] = t19;
166
+ } else t19 = $[39];
167
+ let t20;
168
+ if ($[40] !== canRemove || $[41] !== item.id || $[42] !== onRemove) {
169
+ t20 = canRemove && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
154
170
  render: /* @__PURE__ */ jsx(Button, {
155
171
  type: "button",
156
172
  variant: "ghost",
@@ -166,14 +182,14 @@ const SortableRepeatableItem = (t0) => {
166
182
  side: "right",
167
183
  children: "Remove item"
168
184
  })] });
169
- $[34] = canRemove;
170
- $[35] = item.id;
171
- $[36] = onRemove;
172
- $[37] = t17;
173
- } else t17 = $[37];
174
- let t18;
175
- if ($[38] !== handleMouseEnter || $[39] !== handleMouseLeave || $[40] !== setNodeRef || $[41] !== style || $[42] !== t11 || $[43] !== t13 || $[44] !== t16 || $[45] !== t17) {
176
- t18 = /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", {
185
+ $[40] = canRemove;
186
+ $[41] = item.id;
187
+ $[42] = onRemove;
188
+ $[43] = t20;
189
+ } else t20 = $[43];
190
+ let t21;
191
+ if ($[44] !== handleMouseEnter || $[45] !== handleMouseLeave || $[46] !== setNodeRef || $[47] !== style || $[48] !== t11 || $[49] !== t13 || $[50] !== t19 || $[51] !== t20) {
192
+ t21 = /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", {
177
193
  ref: setNodeRef,
178
194
  style,
179
195
  className: t11,
@@ -181,21 +197,21 @@ const SortableRepeatableItem = (t0) => {
181
197
  onMouseLeave: handleMouseLeave,
182
198
  children: [
183
199
  t13,
184
- t16,
185
- t17
200
+ t19,
201
+ t20
186
202
  ]
187
203
  }) });
188
- $[38] = handleMouseEnter;
189
- $[39] = handleMouseLeave;
190
- $[40] = setNodeRef;
191
- $[41] = style;
192
- $[42] = t11;
193
- $[43] = t13;
194
- $[44] = t16;
195
- $[45] = t17;
196
- $[46] = t18;
197
- } else t18 = $[46];
198
- return t18;
204
+ $[44] = handleMouseEnter;
205
+ $[45] = handleMouseLeave;
206
+ $[46] = setNodeRef;
207
+ $[47] = style;
208
+ $[48] = t11;
209
+ $[49] = t13;
210
+ $[50] = t19;
211
+ $[51] = t20;
212
+ $[52] = t21;
213
+ } else t21 = $[52];
214
+ return t21;
199
215
  };
200
216
  const RepeatableItemsList = (t0) => {
201
217
  const $ = c(24);
@@ -9,6 +9,7 @@ import { editorAssetField } from "../../content/collection-form.js";
9
9
  import { isBlockPlacement, previewStore, recordPlacement, selectionBlockId, selectionField, selectionForOwner, selectionItemId } from "../previewStore.js";
10
10
  import { useEditingFrame } from "../editingFrame.js";
11
11
  import { PreviewEditingOwnerContext } from "../previewSelection.js";
12
+ import { entityName } from "../entityName.js";
12
13
  import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
13
14
  import { selectionHoverMessage } from "../overlayMessages.js";
14
15
  import { usePageRecordPlacer } from "../pageRecord.js";
@@ -605,7 +606,7 @@ const SelectionEditorContent = (t0) => {
605
606
  })
606
607
  }, {
607
608
  key: `item-${ancestor.id}`,
608
- label: ancestor.summary || "Item",
609
+ label: entityName(ancestor, "Item"),
609
610
  hoverTarget: {
610
611
  type: "item",
611
612
  blockId: block_0.id,
@@ -685,7 +686,7 @@ const SelectionEditorContent = (t0) => {
685
686
  ]
686
687
  }), item.onClick ? /* @__PURE__ */ jsx("button", {
687
688
  type: "button",
688
- className: cn("hover:text-foreground flex h-7 min-w-0 cursor-pointer items-center truncate text-left transition-colors", item.isCurrent && "text-foreground font-medium"),
689
+ className: cn("hover:text-foreground block h-7 max-w-full cursor-pointer truncate text-left leading-7 transition-colors", item.isCurrent && "text-foreground font-medium"),
689
690
  onClick: item.onClick,
690
691
  children: item.label
691
692
  }) : /* @__PURE__ */ jsx("span", {
@@ -2,8 +2,8 @@ import { useProjectUuid } from "../../../lib/auth.js";
2
2
  import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
3
3
  import { referenceList } from "../../../lib/normalized-data.js";
4
4
  import { reads, useStudioCache } from "../../../lib/studio-reads.js";
5
- import { lexicalStateToPlainText } from "../../../core/lib/lexicalState.js";
6
5
  import { referenceListIds } from "../../../core/lib/reference.js";
6
+ import { recordText } from "../../../core/recordItem.js";
7
7
  import { blockMutations, collectionMutations, repeatableItemMutations } from "../../../lib/queries.js";
8
8
  import { invalidateCollectionRecordViews } from "../../../lib/collection-cache.js";
9
9
  import { serializeAssetField } from "../../content/collection-form.js";
@@ -134,13 +134,11 @@ function useFieldWriter(target, schema) {
134
134
  collectionId,
135
135
  id: recordId
136
136
  }));
137
- const labelField = camoxApp.getCollectionById(collectionId)?._internal.label;
138
- const label = labelField ? record_0.content[labelField] : void 0;
139
137
  return {
140
138
  id: record_0.id,
141
139
  collectionId,
142
140
  content: record_0.content,
143
- label: typeof label === "string" ? lexicalStateToPlainText(label) : ""
141
+ ...recordText(camoxApp.getCollectionById(collectionId), record_0.content)
144
142
  };
145
143
  };
146
144
  return (async () => {
@@ -302,7 +302,7 @@ function useRecordView(t0) {
302
302
  })
303
303
  }] : [], {
304
304
  key: "record",
305
- label: outerRecord.label,
305
+ label: outerRecord.title,
306
306
  isCurrent: !nested && !fieldHasOwnView,
307
307
  hoverTarget: outerTarget,
308
308
  onClick: nested || fieldHasOwnView ? () => previewStore.send({
@@ -332,7 +332,7 @@ function useRecordView(t0) {
332
332
  },
333
333
  {
334
334
  key: "nested-record",
335
- label: recordView.record.label,
335
+ label: recordView.record.title,
336
336
  isCurrent: !fieldHasOwnView,
337
337
  hoverTarget: nestedTarget,
338
338
  onClick: fieldHasOwnView ? () => previewStore.send({
@@ -0,0 +1,11 @@
1
+ //#region src/features/preview/entityName.ts
2
+ /**
3
+ * What the studio names a block or repeatable item by: its Summary, generated or hand-written,
4
+ * else its title, from its definition's `toTitle` on default-locale content, else `fallback`.
5
+ */
6
+ function entityName(entity, fallback) {
7
+ return entity.summary || entity.title || fallback;
8
+ }
9
+
10
+ //#endregion
11
+ export { entityName };
@@ -157,32 +157,31 @@ function usePageRecordPlacer() {
157
157
  const app = useCamoxApp();
158
158
  const collection = loaded ? app.getCollectionById(loaded.collectionId) : void 0;
159
159
  const collectionId = loaded?.collectionId ?? "";
160
- const t0 = collection?._internal.label ?? "";
161
- const t1 = collection ? loaded?.data : void 0;
162
- let t2;
163
- if ($[0] !== collectionId || $[1] !== t0 || $[2] !== t1) {
164
- t2 = {
160
+ const t0 = collection ? loaded?.data : void 0;
161
+ let t1;
162
+ if ($[0] !== collection || $[1] !== collectionId || $[2] !== t0) {
163
+ t1 = {
165
164
  collectionId,
166
- labelField: t0,
167
- loaded: t1
165
+ collection,
166
+ loaded: t0
168
167
  };
169
- $[0] = collectionId;
170
- $[1] = t0;
171
- $[2] = t1;
172
- $[3] = t2;
173
- } else t2 = $[3];
174
- const record = useLatestRecord(t2);
175
- let t3;
168
+ $[0] = collection;
169
+ $[1] = collectionId;
170
+ $[2] = t0;
171
+ $[3] = t1;
172
+ } else t1 = $[3];
173
+ const record = useLatestRecord(t1);
174
+ let t2;
176
175
  if ($[4] !== collectionId || $[5] !== record) {
177
- t3 = record ? {
176
+ t2 = record ? {
178
177
  collectionId,
179
178
  record
180
179
  } : null;
181
180
  $[4] = collectionId;
182
181
  $[5] = record;
183
- $[6] = t3;
184
- } else t3 = $[6];
185
- return t3;
182
+ $[6] = t2;
183
+ } else t2 = $[6];
184
+ return t2;
186
185
  }
187
186
 
188
187
  //#endregion
@@ -1,12 +1,6 @@
1
1
  //#region src/features/preview/previewConstants.ts
2
- const TOGGLE_CANVAS_SHORTCUT = {
3
- key: "Enter",
4
- withMeta: true
5
- };
6
- const SHOW_WHOLE_CANVAS_SHORTCUT = {
7
- key: "Escape",
8
- withMeta: true
9
- };
2
+ const TOGGLE_CANVAS_SHORTCUT = "Mod+Enter";
3
+ const SHOW_WHOLE_CANVAS_SHORTCUT = "Mod+Escape";
10
4
 
11
5
  //#endregion
12
6
  export { SHOW_WHOLE_CANVAS_SHORTCUT, TOGGLE_CANVAS_SHORTCUT };
@@ -27,7 +27,7 @@ function _temp10() {
27
27
  groupLabel: "Website",
28
28
  checkIfAvailable: _temp,
29
29
  execute: _temp2,
30
- shortcut: { key: "m" }
30
+ shortcut: "M"
31
31
  },
32
32
  {
33
33
  id: "set-viewport-full",
@@ -1,13 +1,13 @@
1
1
  import { AuthContext, useAuthActions } from "../../lib/auth.js";
2
2
  import { useDraftScope } from "../../lib/draft-scope.js";
3
3
  import { reads, useReadQuery, useStudioCache } from "../../lib/studio-reads.js";
4
+ import { useAdminShortcuts } from "./useAdminShortcuts.js";
4
5
  import { useApplyTheme, useThemeActions } from "../studio/useTheme.js";
5
6
  import { usePreviewPagesActions, useShowCanvasAction } from "../preview/CamoxPreview.js";
6
7
  import { useProjectRoom } from "../../lib/use-project-room.js";
7
8
  import { useNavbarActions } from "../studio/components/Navbar.js";
8
9
  import { draftNoticeMessage } from "../studio/draftNotice.js";
9
10
  import { CommandPalette, useCommandPaletteActions } from "./components/CommandPalette.js";
10
- import { useAdminShortcuts } from "./useAdminShortcuts.js";
11
11
  import { c } from "react/compiler-runtime";
12
12
  import * as React from "react";
13
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
1
  import { actionsStore } from "../actionsStore.js";
2
- import { formatShortcut } from "../../../lib/utils.js";
2
+ import { formatShortcut } from "../../../lib/shortcuts.js";
3
3
  import { studioStore } from "../../studio/studioStore.js";
4
4
  import { c } from "react/compiler-runtime";
5
5
  import * as React from "react";
@@ -249,10 +249,7 @@ function _temp7() {
249
249
  groupLabel: "Invisible",
250
250
  checkIfAvailable: _temp4,
251
251
  execute: _temp5,
252
- shortcut: {
253
- key: "k",
254
- withMeta: true
255
- }
252
+ shortcut: "Mod+K"
256
253
  }
257
254
  });
258
255
  return _temp6;
@@ -1,5 +1,4 @@
1
1
  import { useSignInRedirect } from "../../lib/auth.js";
2
- import { TOGGLE_CANVAS_SHORTCUT } from "../preview/previewConstants.js";
3
2
  import { c } from "react/compiler-runtime";
4
3
  import * as React from "react";
5
4
 
@@ -28,7 +27,7 @@ function useSignInShortcut(enabled) {
28
27
  t0 = () => {
29
28
  if (!enabled) return;
30
29
  const handleKeyDown = (event) => {
31
- if (event.key !== TOGGLE_CANVAS_SHORTCUT.key || !(event.metaKey || event.ctrlKey)) return;
30
+ if (event.key !== "Enter" || !(event.metaKey || event.ctrlKey)) return;
32
31
  if (event.altKey || event.shiftKey || event.defaultPrevented || isTyping()) return;
33
32
  event.preventDefault();
34
33
  signIn();
@@ -1,75 +1,69 @@
1
1
  import { actionsStore } from "./actionsStore.js";
2
2
  import { checkIfInputFocused } from "../../lib/utils.js";
3
3
  import { c } from "react/compiler-runtime";
4
- import * as React from "react";
5
4
  import { useSelector } from "@xstate/store-react";
5
+ import { parseHotkey, useHotkeys } from "@tanstack/react-hotkeys";
6
6
 
7
7
  //#region src/features/provider/useAdminShortcuts.tsx
8
+ /** Listens for the shortcuts of the actions in the actionsStore, on the Studio's window. */
9
+ function useAdminShortcuts() {
10
+ useActionShortcuts(typeof window === "undefined" ? null : window);
11
+ }
8
12
  /**
9
- * Hook that listens for global keyboard shortcuts defined in the actionsStore
13
+ * Runs the first available action whose shortcut is pressed in `win`, which may be a preview
14
+ * iframe's window. Shortcuts no available action claims leave the key to the page.
10
15
  */
11
- function useAdminShortcuts() {
12
- const $ = c(3);
16
+ function useActionShortcuts(win) {
17
+ const $ = c(4);
13
18
  const actions = useSelector(actionsStore, _temp);
14
19
  let t0;
15
20
  let t1;
16
- if ($[0] !== actions) {
17
- t0 = () => {
18
- const handleKeyDown = (event) => {
19
- const matchingAction = actions.find((action) => {
20
- if (!action.shortcut) return false;
21
- if (!action.checkIfAvailable()) return false;
22
- const { key, withMeta, withAlt, withShift } = action.shortcut;
23
- return (withAlt && key.length === 1 && /[a-z]/i.test(key) ? event.code === `Key${key.toUpperCase()}` : key.toLowerCase() === event.key.toLowerCase()) && !!withMeta === (event.metaKey || event.ctrlKey) && !!withAlt === event.altKey && !!withShift === event.shiftKey;
24
- });
25
- if (!matchingAction) return;
26
- const shortcut = matchingAction.shortcut;
27
- if (checkIfInputFocused()) {
28
- if (shortcut.key !== "Escape" && !shortcut.withMeta && !shortcut.withAlt) return;
29
- if (shortcut.key === "Backspace") return;
30
- }
31
- event.preventDefault();
32
- event.stopPropagation();
33
- if (shortcut.key === "Escape" && checkIfInputFocused()) document.activeElement.blur();
34
- matchingAction.execute();
35
- };
36
- const handleCapturedKeyDown = (event_0) => {
37
- if (event_0.key === "Escape") return;
38
- handleKeyDown(event_0);
39
- };
40
- const handleEscape = (event_1) => {
41
- if (event_1.key !== "Escape" || event_1.defaultPrevented) return;
42
- handleKeyDown(event_1);
43
- };
44
- const handleMessage = (event_2) => {
45
- if (event_2.data?.type === "executeAction") {
46
- const { actionId } = event_2.data;
47
- const action_0 = actions.find((a) => a.id === actionId);
48
- if (action_0) action_0.execute();
21
+ if ($[0] !== actions || $[1] !== win) {
22
+ const shortcuts = [...new Set(actions.flatMap(_temp2))];
23
+ t0 = useHotkeys;
24
+ t1 = win ? shortcuts.map((shortcut) => {
25
+ const { key, ctrl, meta, alt } = parseHotkey(shortcut);
26
+ const escape = key === "Escape";
27
+ return {
28
+ hotkey: shortcut,
29
+ callback: (event) => {
30
+ if (escape && event.defaultPrevented) return;
31
+ const action_0 = findAvailableAction(shortcut);
32
+ if (!action_0) return;
33
+ event.preventDefault();
34
+ event.stopPropagation();
35
+ if (escape && checkIfInputFocused(win.document)) win.document.activeElement.blur();
36
+ action_0.execute();
37
+ },
38
+ options: {
39
+ target: escape ? win : win.document,
40
+ capture: !escape,
41
+ ignoreInputs: key === "Backspace" || !ctrl && !meta && !alt && !escape,
42
+ preventDefault: false,
43
+ stopPropagation: false,
44
+ conflictBehavior: "allow"
49
45
  }
50
46
  };
51
- document.addEventListener("keydown", handleCapturedKeyDown, true);
52
- window.addEventListener("keydown", handleEscape);
53
- window.addEventListener("message", handleMessage);
54
- return () => {
55
- document.removeEventListener("keydown", handleCapturedKeyDown, true);
56
- window.removeEventListener("keydown", handleEscape);
57
- window.removeEventListener("message", handleMessage);
58
- };
59
- };
60
- t1 = [actions];
47
+ }) : [];
61
48
  $[0] = actions;
62
- $[1] = t0;
63
- $[2] = t1;
49
+ $[1] = win;
50
+ $[2] = t0;
51
+ $[3] = t1;
64
52
  } else {
65
- t0 = $[1];
66
- t1 = $[2];
53
+ t0 = $[2];
54
+ t1 = $[3];
67
55
  }
68
- React.useEffect(t0, t1);
56
+ t0(t1);
57
+ }
58
+ function _temp2(action) {
59
+ return action.shortcut ?? [];
69
60
  }
70
61
  function _temp(state) {
71
62
  return state.context.actions;
72
63
  }
64
+ function findAvailableAction(shortcut) {
65
+ return actionsStore.getSnapshot().context.actions.find((action) => action.shortcut === shortcut && action.checkIfAvailable());
66
+ }
73
67
 
74
68
  //#endregion
75
- export { useAdminShortcuts };
69
+ export { useActionShortcuts, useAdminShortcuts };
@@ -1,10 +1,12 @@
1
+ import { LocaleView } from "../../lib/locale-path.js";
2
+ import "../../core/layoutContext.js";
3
+ import "../../core/structuredData.js";
1
4
  import "../../core/createLayout.js";
2
5
  import "../../core/createApp.js";
3
- import { LocaleView } from "../../lib/locale-path.js";
4
6
  import "../../lib/studio-reads.js";
5
- import { Router } from "@camox/api-contract";
6
7
  import { EndedDraft } from "@camox/api-contract/drafts";
7
8
  import { QueryClient } from "@tanstack/react-query";
9
+ import { Router } from "@camox/api-contract/router";
8
10
  import { RouterClient } from "@orpc/server";
9
11
  //#region src/features/routes/pageRuntime.d.ts
10
12
  type PageWithBlocks = Awaited<ReturnType<RouterClient<Router>["pages"]["getByPath"]>>;
@@ -14,6 +16,10 @@ export type PageStructure = {
14
16
  layout: PageWithBlocks["layout"];
15
17
  projectName: string;
16
18
  project: PageWithBlocks["project"];
19
+ /** Each block's Structured Data nodes, from a full read: a structure read has none. */
20
+ structuredData?: PageWithBlocks["structuredData"];
21
+ /** The nodes of the records its blocks reference, from a full read. */
22
+ recordStructuredData?: PageWithBlocks["recordStructuredData"];
17
23
  };
18
24
  /**
19
25
  * Why a member can't see the draft a URL names: it doesn't exist, it's someone else's
@@ -46,5 +52,14 @@ export declare function createServerApiClient(apiUrl: string, options?: {
46
52
  draftId?: string;
47
53
  locale?: LocaleView;
48
54
  }): RouterClient<Router>;
55
+ export type PageHead = {
56
+ meta?: Array<Record<string, string>>;
57
+ links?: Array<Record<string, string>>;
58
+ /** Scripts with their text, such as the page's Structured Data. */
59
+ scripts?: Array<{
60
+ type: string;
61
+ text: string;
62
+ }>;
63
+ };
49
64
  //#endregion
50
65
  export {};