camox 0.71.0 → 0.74.1

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 (104) hide show
  1. package/dist/core/createApp.d.ts +4 -103
  2. package/dist/core/createApp.js +3 -24
  3. package/dist/core/createBlock.d.ts +2 -2
  4. package/dist/core/createBlock.js +41 -185
  5. package/dist/core/createLayout.d.ts +6 -77
  6. package/dist/core/createLayout.js +12 -50
  7. package/dist/core/editing/createEditableBlock.d.ts +9 -75
  8. package/dist/core/editing/createEditableBlock.js +258 -640
  9. package/dist/core/hooks/useIsEditable.js +2 -2
  10. package/dist/core/lib/artifactData.js +8 -3
  11. package/dist/core/lib/contentType.d.ts +23 -38
  12. package/dist/core/lib/contentType.js +34 -62
  13. package/dist/core/lib/iconTypes.d.ts +0 -3
  14. package/dist/core/lib/icons.js +63 -51
  15. package/dist/core/lib/recordScope.js +1 -1
  16. package/dist/features/canvas/CanvasOverlays.js +94 -176
  17. package/dist/features/canvas/CanvasPageFrame.js +62 -68
  18. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -3
  19. package/dist/features/canvas/canvasOverlayGeometry.js +1 -7
  20. package/dist/features/canvas/sidebar/CuratedPageInspector.js +3 -21
  21. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +1 -2
  22. package/dist/features/canvas/useCanvasCamera.js +1 -1
  23. package/dist/features/canvas/useCanvasFrame.js +1 -1
  24. package/dist/features/content/ContentCollection.js +2 -2
  25. package/dist/features/content/collection-form.js +6 -7
  26. package/dist/features/page/CuratedPageContent.js +15 -24
  27. package/dist/features/page/UnplacedBlockContent.js +1 -6
  28. package/dist/features/preview/CamoxPreview.js +30 -33
  29. package/dist/features/preview/EditablePageContent.js +131 -151
  30. package/dist/features/preview/components/BlockActionsPopover.js +43 -152
  31. package/dist/features/preview/components/CanvasPreview.js +2 -2
  32. package/dist/features/preview/components/Frame.js +3 -3
  33. package/dist/features/preview/components/IconFieldEditor.js +1 -1
  34. package/dist/features/preview/components/ItemFieldsEditor.js +1 -3
  35. package/dist/features/preview/components/Overlays.js +18 -84
  36. package/dist/features/preview/components/PageInfo.js +121 -98
  37. package/dist/features/preview/components/PageTree.js +57 -103
  38. package/dist/features/preview/components/PreviewPanel.js +8 -11
  39. package/dist/features/preview/components/RecordCombobox.js +65 -78
  40. package/dist/features/preview/components/ReferenceFieldEditor.js +1 -19
  41. package/dist/features/preview/components/ReferenceListFieldEditor.js +1 -19
  42. package/dist/features/preview/components/RemovedBlockPlaceholder.js +2 -2
  43. package/dist/features/preview/components/SelectionEditor.js +6 -5
  44. package/dist/features/preview/components/useRepeatableItemActions.js +18 -24
  45. package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
  46. package/dist/features/preview/overlayMessages.js +1 -4
  47. package/dist/features/preview/previewStore.js +3 -23
  48. package/dist/features/renderer/RendererApp.js +17 -141
  49. package/dist/features/renderer/definitionAnswers.js +1 -8
  50. package/dist/features/renderer/rendererHost.js +1 -31
  51. package/dist/features/renderer/studioConnection.js +1 -25
  52. package/dist/features/routes/pageRuntime.js +15 -4
  53. package/dist/features/runtime/ClaimCard.js +192 -0
  54. package/dist/features/runtime/pageApp.js +5 -4
  55. package/dist/features/runtime/runtime.js +29 -8
  56. package/dist/features/runtime/worker.js +2 -2
  57. package/dist/features/runtime/workerRequest.d.ts +6 -0
  58. package/dist/features/runtime/workerRequest.js +13 -1
  59. package/dist/features/runtime/workerRuntime.d.ts +2 -2
  60. package/dist/features/runtime/workerRuntime.js +2 -2
  61. package/dist/features/studio/components/AgentSidebar.js +7 -7
  62. package/dist/features/studio/components/ChatWidget.js +142 -106
  63. package/dist/features/studio/components/ContentFindings.js +127 -230
  64. package/dist/features/studio/components/DeleteDraftDialog.js +112 -0
  65. package/dist/features/studio/components/DraftBar.js +98 -53
  66. package/dist/features/studio/components/DraftsControl.js +203 -190
  67. package/dist/features/studio/components/ReviewDraftDialog.js +670 -0
  68. package/dist/features/studio/components/{Patrol.js → Specialists.js} +291 -330
  69. package/dist/features/studio/components/Squareling.js +5 -5
  70. package/dist/features/studio/components/ThreadComposer.js +106 -127
  71. package/dist/features/studio/components/ThreadMessages.js +137 -45
  72. package/dist/features/studio/findingSentence.js +14 -12
  73. package/dist/features/studio/fixMessage.js +23 -0
  74. package/dist/features/studio/reportSentence.js +1 -1
  75. package/dist/features/studio/{patrollerBio.js → specialistBio.js} +8 -5
  76. package/dist/features/studio/studioStore.js +29 -15
  77. package/dist/features/studio/workingSentence.js +1 -1
  78. package/dist/features/vite/blockBoilerplate.js +1 -1
  79. package/dist/features/vite/definitionsSync.js +1 -8
  80. package/dist/lib/api-client.js +5 -1
  81. package/dist/lib/queries.js +5 -6
  82. package/dist/lib/studio-definitions.js +0 -2
  83. package/dist/lib/studio-reads.d.ts +1 -1
  84. package/dist/lib/studio-reads.js +5 -10
  85. package/dist/lib/use-thread.js +196 -138
  86. package/dist/package.js +1 -1
  87. package/dist/studio-overlays.css +0 -29
  88. package/dist/studio.css +1 -1
  89. package/package.json +4 -4
  90. package/skills/camox/SKILL.md +1 -1
  91. package/skills/camox/references/artifacts.md +4 -4
  92. package/skills/camox/references/block-definitions.md +21 -25
  93. package/skills/camox/references/dom-integrations.md +1 -1
  94. package/skills/camox/references/layout-definitions.md +3 -12
  95. package/skills/camox/references/routed-layouts.md +2 -2
  96. package/skills/camox-expand/SKILL.md +5 -5
  97. package/skills/camoxify/SKILL.md +4 -4
  98. package/dist/features/canvas/canvasInsertionSeams.js +0 -44
  99. package/dist/features/preview/components/AddBlockSheet.js +0 -314
  100. package/dist/features/preview/components/BlockPeek.js +0 -66
  101. package/dist/features/preview/components/BlockThumbnail.js +0 -281
  102. package/dist/features/preview/components/blockGridNavigation.js +0 -15
  103. package/dist/features/preview/referencePickerFocus.js +0 -29
  104. package/dist/features/studio/components/ReviewDraftSheet.js +0 -1587
@@ -10,11 +10,11 @@ import { useSelector } from "@xstate/store-react";
10
10
  * the Page. Frames keep the editing runtime either way, so toggling never remounts the page.
11
11
  */
12
12
  const EditableSurfaceContext = React.createContext(true);
13
- function useIsEditable(mode) {
13
+ function useIsEditable() {
14
14
  const isAuthenticated = useIsAuthenticated();
15
15
  const isCanvasShown = useSelector(previewStore, selectIsCanvasShown);
16
16
  const surface = React.use(EditableSurfaceContext);
17
- return isAuthenticated && (mode === "site" || mode === "layout") && isCanvasShown && surface;
17
+ return isAuthenticated && isCanvasShown && surface;
18
18
  }
19
19
 
20
20
  //#endregion
@@ -1,3 +1,5 @@
1
+ import { emptyFieldValue } from "@camox/api-contract";
2
+
1
3
  //#region src/core/lib/artifactData.ts
2
4
  const DATA_SOURCES = Symbol("camox.artifactDataSources");
3
5
  /** Content fields that can't be Artifact Data. */
@@ -38,7 +40,10 @@ function valueSchema(schema) {
38
40
  newTab: { type: "boolean" }
39
41
  }, title);
40
42
  case "Image":
41
- case "File": return objectSchema(assetProperties[schema.fieldType], title);
43
+ case "File": return {
44
+ ...titled,
45
+ anyOf: [objectSchema(assetProperties[schema.fieldType]), { type: "null" }]
46
+ };
42
47
  case "ImageList":
43
48
  case "FileList": {
44
49
  const single = schema.fieldType === "ImageList" ? "Image" : "File";
@@ -111,10 +116,10 @@ function normalizeValue(schema, value, resolve) {
111
116
  };
112
117
  }
113
118
  case "Image":
114
- case "File": return normalizeAsset(schema.fieldType, resolve.asset(value) ?? schema.default);
119
+ case "File": return normalizeAsset(schema.fieldType, resolve.asset(value));
115
120
  case "ImageList":
116
121
  case "FileList": return (Array.isArray(value) ? value : []).map((item) => normalizeAsset(schema.fieldType, resolve.asset(item))).filter(Boolean);
117
- default: return value ?? schema.default;
122
+ default: return value ?? emptyFieldValue(schema);
118
123
  }
119
124
  }
120
125
  /**
@@ -1,6 +1,6 @@
1
1
  import { BlockStructuredData, KnownNodesOnly, RepeatedNodes } from "../structuredData.js";
2
2
  import { Collection } from "../createCollection.js";
3
- import { IconId, IconValue } from "./iconTypes.js";
3
+ import { IconValue } from "./iconTypes.js";
4
4
  import { SystemOrderKey } from "@camox/api-contract";
5
5
  import { Static, TArray, TBoolean, TObject, TSchema, TUnsafe } from "@sinclair/typebox";
6
6
  //#region src/core/lib/contentType.d.ts
@@ -208,29 +208,23 @@ type StringOptions = {
208
208
  /**
209
209
  * Builds the fields of a block or repeatable item's content.
210
210
  * `enum` and `boolean` are not content: declare them in `settings` instead.
211
+ * Fields have no default: an unset field holds the empty value of its kind.
211
212
  */
212
213
  export interface ContentFieldBuilder {
213
214
  /**
214
215
  * An inline-editable text field.
215
216
  *
216
217
  * @example
217
- * field.string({ default: "Hello", maxLength: 100, title: "Title" })
218
+ * field.string({ maxLength: 100, title: "Title" })
218
219
  */
219
- string: (options: StringOptions & {
220
- default: string;
221
- }) => StringField;
220
+ string: (options?: StringOptions) => StringField;
222
221
  /**
223
222
  * A link with text, a target (external URL or internal page) and a new-tab flag.
224
223
  *
225
224
  * @example
226
- * field.link({ default: { text: "Learn more", href: "/", newTab: false }, title: "CTA" })
225
+ * field.link({ title: "CTA" })
227
226
  */
228
- link: (options: {
229
- default: {
230
- text: string;
231
- href: string;
232
- newTab: boolean;
233
- };
227
+ link: (options?: {
234
228
  title?: string;
235
229
  }) => LinkField;
236
230
  /**
@@ -247,11 +241,10 @@ export interface ContentFieldBuilder {
247
241
  * A list of image assets.
248
242
  *
249
243
  * @example
250
- * field.imageList({ title: "Gallery", defaultItems: 3 })
244
+ * field.imageList({ title: "Gallery" })
251
245
  */
252
246
  imageList: (options?: {
253
247
  title?: string;
254
- defaultItems?: number;
255
248
  }) => ImageListField;
256
249
  /**
257
250
  * A file asset.
@@ -273,7 +266,6 @@ export interface ContentFieldBuilder {
273
266
  fileList: (options: {
274
267
  accept: string[];
275
268
  title?: string;
276
- defaultItems?: number;
277
269
  }) => FileListField;
278
270
  /**
279
271
  * A URL that must match `pattern`, rendered as an embed.
@@ -281,13 +273,11 @@ export interface ContentFieldBuilder {
281
273
  * @example
282
274
  * field.embed({
283
275
  * pattern: "https:\\/\\/(www\\.)?youtube\\.com\\/watch\\?v=.+",
284
- * default: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
285
276
  * title: "YouTube URL",
286
277
  * })
287
278
  */
288
279
  embed: (options: {
289
280
  pattern: string;
290
- default: string;
291
281
  title?: string;
292
282
  }) => EmbedField;
293
283
  /**
@@ -305,17 +295,15 @@ export interface ContentFieldBuilder {
305
295
  * repeater, the siblings are the item's own fields and settings.
306
296
  */
307
297
  data?: readonly D[];
308
- default?: string;
309
298
  title?: string;
310
299
  }) => ArtifactField<NoInfer<D>>;
311
300
  /**
312
301
  * An icon from the set configured in `camox()`.
313
302
  *
314
303
  * @example
315
- * field.icon({ default: "lucide:star", title: "Icon" })
304
+ * field.icon({ title: "Icon" })
316
305
  */
317
- icon: (options: {
318
- default: IconId;
306
+ icon: (options?: {
319
307
  title?: string;
320
308
  }) => IconField;
321
309
  /**
@@ -355,11 +343,11 @@ export interface ContentFieldBuilder {
355
343
  * @example
356
344
  * field.repeater({
357
345
  * content: (field) => ({
358
- * title: field.string({ default: "Item" }),
359
- * description: field.string({ default: "Description" }),
346
+ * title: field.string(),
347
+ * description: field.string(),
360
348
  * }),
361
349
  * settings: (setting) => ({
362
- * highlighted: setting.boolean({ default: false, title: "Highlighted" }),
350
+ * highlighted: setting.boolean({ title: "Highlighted" }),
363
351
  * }),
364
352
  * minItems: 1,
365
353
  * maxItems: 10,
@@ -416,30 +404,29 @@ type ArtifactKey<T extends Record<string, TSchema>> = { [K in keyof T & string]-
416
404
  */
417
405
  export interface SettingBuilder {
418
406
  /**
419
- * One of a fixed set of options; keys are stored, values are editor labels.
407
+ * One of a fixed set of options; keys are stored, values are editor labels. Unset, it holds
408
+ * the first option.
420
409
  *
421
410
  * @example
422
- * setting.enum({ default: "left", options: { left: "Left", right: "Right" }, title: "Alignment" })
411
+ * setting.enum({ options: { left: "Left", right: "Right" }, title: "Alignment" })
423
412
  */
424
413
  enum: <const O extends Record<string, string>>(options: {
425
- default: keyof O & string;
426
414
  options: O;
427
415
  title?: string;
428
416
  }) => EnumSetting<O>;
429
417
  /**
430
- * A toggle.
418
+ * A toggle. Unset, it's off.
431
419
  *
432
420
  * @example
433
- * setting.boolean({ default: false, title: "Show background" })
421
+ * setting.boolean({ title: "Show background" })
434
422
  */
435
- boolean: (options: {
436
- default: boolean;
423
+ boolean: (options?: {
437
424
  title?: string;
438
425
  }) => BooleanSetting;
439
426
  }
440
427
  /**
441
- * Builds the fields of a collection record. Records are created through an authoring form, so
442
- * `string` needs no default. Collections have no settings: `enum` and `boolean` are content here.
428
+ * Builds the fields of a collection record. Collections have no settings: `enum` and `boolean`
429
+ * are content here.
443
430
  */
444
431
  export interface CollectionFieldBuilder extends Pick<ContentFieldBuilder, "imageList" | "fileList" | "embed"> {
445
432
  /**
@@ -490,21 +477,19 @@ export interface CollectionFieldBuilder extends Pick<ContentFieldBuilder, "image
490
477
  * @example
491
478
  * field.string({ minLength: 1, title: "Name" })
492
479
  */
493
- string: (options?: StringOptions & {
494
- default?: string;
495
- }) => StringField;
480
+ string: (options?: StringOptions) => StringField;
496
481
  /**
497
482
  * One of a fixed set of options; keys are stored, values are editor labels.
498
483
  *
499
484
  * @example
500
- * field.enum({ default: "draft", options: { draft: "Draft", final: "Final" } })
485
+ * field.enum({ options: { draft: "Draft", final: "Final" } })
501
486
  */
502
487
  enum: SettingBuilder["enum"];
503
488
  /**
504
489
  * A toggle.
505
490
  *
506
491
  * @example
507
- * field.boolean({ default: false, title: "Featured" })
492
+ * field.boolean({ title: "Featured" })
508
493
  */
509
494
  boolean: SettingBuilder["boolean"];
510
495
  }
@@ -156,6 +156,20 @@ function resolveRecordStructuredData(builder, contentShape) {
156
156
  /** The longest artifact source content writes accept (256 KB). */
157
157
  const ARTIFACT_MAX_LENGTH = 262144;
158
158
  const IMAGE_PLACEHOLDER = Symbol("camox.imagePlaceholder");
159
+ /**
160
+ * What an empty image field renders in a block: an illustrated placeholder labeled with the
161
+ * field's title. Its URL is the fallback for callbacks that don't render a plain `<img>`.
162
+ */
163
+ function placeholderImage(title) {
164
+ const label = title || "image";
165
+ return {
166
+ url: `https://placehold.co/1200x800/f4f4f5/a1a1aa.png?text=${encodeURIComponent(label)}`,
167
+ alt: "",
168
+ filename: "placeholder.png",
169
+ mimeType: "image/png",
170
+ [IMAGE_PLACEHOLDER]: label
171
+ };
172
+ }
159
173
  function isImagePlaceholder(value) {
160
174
  return typeof value[IMAGE_PLACEHOLDER] === "string" && !value._fileId;
161
175
  }
@@ -173,13 +187,6 @@ function _imageSingle(options) {
173
187
  },
174
188
  accept: ["image/*"],
175
189
  required: options.required ?? false,
176
- default: {
177
- url: `https://placehold.co/1200x800/f4f4f5/a1a1aa.png?text=${options?.title || "image"}`,
178
- alt: "",
179
- filename: "placeholder.png",
180
- mimeType: "image/png",
181
- [IMAGE_PLACEHOLDER]: options.title || "image"
182
- },
183
190
  title: options.title,
184
191
  fieldType: "Image"
185
192
  });
@@ -188,8 +195,6 @@ function _imageList(options) {
188
195
  return Type.Array(_imageSingle({ title: options.title }), {
189
196
  minItems: 0,
190
197
  maxItems: 100,
191
- default: [],
192
- defaultItems: options.defaultItems ?? 0,
193
198
  title: options.title,
194
199
  fieldType: "ImageList"
195
200
  });
@@ -205,12 +210,6 @@ function _fileSingle(options) {
205
210
  },
206
211
  accept: options.accept,
207
212
  required: options.required ?? false,
208
- default: {
209
- url: "https://placehold.co/file-placeholder",
210
- alt: "",
211
- filename: "placeholder",
212
- mimeType: "application/octet-stream"
213
- },
214
213
  title: options.title,
215
214
  fieldType: "File"
216
215
  });
@@ -222,8 +221,6 @@ function _fileList(options) {
222
221
  }), {
223
222
  minItems: 0,
224
223
  maxItems: 100,
225
- default: [],
226
- defaultItems: options.defaultItems ?? 0,
227
224
  title: options.title,
228
225
  fieldType: "FileList"
229
226
  });
@@ -246,21 +243,14 @@ function assertFieldKinds(shape, context, owner) {
246
243
  if (context === "content" && isSetting) throw new Error(`${owner} field "${key}": ${field.fieldType} is a setting, not content`);
247
244
  }
248
245
  }
249
- function collectSchemaDefaults(properties) {
250
- const defaults = {};
251
- for (const [key, prop] of Object.entries(properties)) if ("default" in prop) defaults[key] = prop.default;
252
- return defaults;
253
- }
254
246
  const enumSetting = (options) => Type.Unsafe({
255
247
  type: "string",
256
248
  enum: Object.keys(options.options),
257
- default: options.default,
258
249
  title: options.title,
259
250
  enumLabels: options.options,
260
251
  fieldType: "Enum"
261
252
  });
262
- const booleanSetting = (options) => Type.Boolean({
263
- default: options.default,
253
+ const booleanSetting = (options = {}) => Type.Boolean({
264
254
  title: options.title,
265
255
  fieldType: "Boolean"
266
256
  });
@@ -268,13 +258,14 @@ const settingBuilder = {
268
258
  enum: enumSetting,
269
259
  boolean: booleanSetting
270
260
  };
261
+ const stringField = (options = {}) => Type.Unsafe({
262
+ type: "string",
263
+ ...options,
264
+ fieldType: "String"
265
+ });
271
266
  const contentFieldBuilder = {
272
- string: (options) => Type.Unsafe({
273
- type: "string",
274
- ...options,
275
- fieldType: "String"
276
- }),
277
- link: (options) => Type.Unsafe({
267
+ string: stringField,
268
+ link: (options = {}) => Type.Unsafe({
278
269
  type: "object",
279
270
  properties: {
280
271
  type: {
@@ -286,10 +277,6 @@ const contentFieldBuilder = {
286
277
  pageId: { anyOf: [{ const: "" }, uuidv7JsonSchema] },
287
278
  newTab: { type: "boolean" }
288
279
  },
289
- default: {
290
- ...options.default,
291
- type: "external"
292
- },
293
280
  title: options.title,
294
281
  fieldType: "Link"
295
282
  }),
@@ -297,33 +284,27 @@ const contentFieldBuilder = {
297
284
  imageList: (options = {}) => _imageList(options),
298
285
  file: (options) => _fileSingle(options),
299
286
  fileList: (options) => _fileList(options),
300
- embed: (options) => {
301
- if (!new RegExp(options.pattern).test(options.default)) throw new Error(`Embed default value "${options.default}" does not match pattern "${options.pattern}"`);
302
- return Type.Unsafe({
303
- type: "string",
304
- pattern: options.pattern,
305
- default: options.default,
306
- title: options.title,
307
- fieldType: "Embed"
308
- });
309
- },
287
+ embed: (options) => Type.Unsafe({
288
+ type: "string",
289
+ pattern: options.pattern,
290
+ title: options.title,
291
+ fieldType: "Embed"
292
+ }),
310
293
  artifact: (options) => Type.Unsafe({
311
294
  type: "string",
312
295
  maxLength: ARTIFACT_MAX_LENGTH,
313
296
  size: options.size,
314
297
  ...options.data?.length ? { data: [...options.data] } : {},
315
- default: options.default ?? "",
316
298
  title: options.title,
317
299
  fieldType: "Artifact"
318
300
  }),
319
- icon: (options) => {
301
+ icon: (options = {}) => {
320
302
  const ids = typeof __CAMOX_ICON_IDS__ === "undefined" ? [] : __CAMOX_ICON_IDS__;
321
- if (!ids.includes(options.default)) throw new Error(`Invalid icon default "${String(options.default)}". Configure icons in camox() first.`);
303
+ if (ids.length === 0) throw new Error("Icon fields need icons: configure them in camox() first.");
322
304
  return Type.Unsafe({
323
305
  type: "string",
324
306
  fieldType: "Icon",
325
307
  enum: [...ids],
326
- default: options.default,
327
308
  title: options.title
328
309
  });
329
310
  },
@@ -336,7 +317,6 @@ const contentFieldBuilder = {
336
317
  collectionId: collection._internal.id,
337
318
  required: options.required ?? false,
338
319
  title: options.title ?? collection._internal.title,
339
- default: null,
340
320
  referenceSchema: collection._internal.contentSchema
341
321
  }),
342
322
  referenceList: (collection, options = {}) => Type.Unsafe({
@@ -350,7 +330,7 @@ const contentFieldBuilder = {
350
330
  title: options.title ?? collection._internal.title,
351
331
  ...options.description === void 0 ? {} : { description: options.description },
352
332
  ...options.maxItems === void 0 ? {} : { maxItems: options.maxItems },
353
- ...options.query === void 0 ? { default: [] } : { query: validateQuery(options.query, collection) },
333
+ ...options.query === void 0 ? {} : { query: validateQuery(options.query, collection) },
354
334
  ...options.toMarkdown === void 0 ? {} : { toMarkdown: resolveToMarkdown(options.toMarkdown, void 0, "item", collection._internal.contentSchema.properties) },
355
335
  referenceSchema: collection._internal.contentSchema
356
336
  }),
@@ -363,8 +343,6 @@ const contentFieldBuilder = {
363
343
  if (settings) assertFieldKinds(settings, "settings", owner);
364
344
  attachArtifactData(content, settings, owner);
365
345
  const objectSchema = Type.Object(content);
366
- const defaultItem = collectSchemaDefaults(objectSchema.properties);
367
- const defaultArray = Array(options.minItems).fill(null).map(() => ({ ...defaultItem }));
368
346
  const settingsObjectSchema = settings ? Type.Object(settings) : null;
369
347
  const itemSettingsSchema = settingsObjectSchema ? {
370
348
  type: "object",
@@ -374,23 +352,17 @@ const contentFieldBuilder = {
374
352
  return Type.Array(objectSchema, {
375
353
  minItems: options.minItems,
376
354
  ...Number.isFinite(options.maxItems) ? { maxItems: options.maxItems } : {},
377
- default: defaultArray,
378
355
  title: options.title,
379
356
  fieldType: "Repeater",
380
357
  toTitle: resolveToTitle(options.toTitle, settings, "item", content),
381
358
  toMarkdown: resolveToMarkdown(options.toMarkdown, settings, "item", content),
382
359
  ...options.toStructuredData && { toStructuredData: resolveToStructuredData(options.toStructuredData, settings, "item", content) },
383
- itemSettingsSchema,
384
- defaultItemSettings: settingsObjectSchema ? collectSchemaDefaults(settingsObjectSchema.properties) : void 0
360
+ itemSettingsSchema
385
361
  });
386
362
  }
387
363
  };
388
364
  const collectionFieldBuilder = {
389
- string: (options = {}) => Type.Unsafe({
390
- type: "string",
391
- ...options,
392
- fieldType: "String"
393
- }),
365
+ string: stringField,
394
366
  enum: enumSetting,
395
367
  boolean: booleanSetting,
396
368
  image: (options = {}) => contentFieldBuilder.image({ title: options.title }),
@@ -422,4 +394,4 @@ function resolveBlockFields(options) {
422
394
  }
423
395
 
424
396
  //#endregion
425
- export { ARTIFACT_MAX_LENGTH, Conditional, FieldToken, assertFieldKinds, collectionField, collectionFieldBuilder, contentFieldBuilder, getImagePlaceholderTitle, isImagePlaceholder, resolveBlockFields, resolveRecordStructuredData, resolveRecordTemplate, resolveToMarkdown, resolveToStructuredData, resolveToTitle, settingBuilder };
397
+ export { ARTIFACT_MAX_LENGTH, Conditional, FieldToken, assertFieldKinds, collectionField, collectionFieldBuilder, contentFieldBuilder, getImagePlaceholderTitle, isImagePlaceholder, placeholderImage, resolveBlockFields, resolveRecordStructuredData, resolveRecordTemplate, resolveToMarkdown, resolveToStructuredData, resolveToTitle, settingBuilder };
@@ -2,9 +2,6 @@
2
2
  declare global {
3
3
  interface CamoxIconRegistry {}
4
4
  }
5
- export type IconId = CamoxIconRegistry extends {
6
- ids: infer IDs extends string;
7
- } ? IDs : never;
8
5
  declare const iconBrand: unique symbol;
9
6
  export type IconValue = string & {
10
7
  readonly [iconBrand]: true;
@@ -19,8 +19,9 @@ const iconQuery = (id) => ({
19
19
  return response.json();
20
20
  }
21
21
  });
22
+ /** An icon by ID; an unset icon field (`""`) renders an empty SVG without fetching. */
22
23
  function IconSvg(t0) {
23
- const $ = c(21);
24
+ const $ = c(24);
24
25
  let iconId;
25
26
  let props;
26
27
  if ($[0] !== t0) {
@@ -38,64 +39,75 @@ function IconSvg(t0) {
38
39
  $[3] = iconId;
39
40
  $[4] = t1;
40
41
  } else t1 = $[4];
41
- const { data } = useQuery(t1);
42
- const t2 = useId();
42
+ const t2 = iconId !== "";
43
43
  let t3;
44
- if ($[5] !== t2) {
45
- let t4;
46
- if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
47
- t4 = /[^a-zA-Z0-9_-]/g;
48
- $[7] = t4;
49
- } else t4 = $[7];
50
- t3 = t2.replace(t4, "");
51
- $[5] = t2;
52
- $[6] = t3;
53
- } else t3 = $[6];
54
- const instance = t3;
55
- const t4 = data?.body ?? "";
56
- const t5 = `camox-${instance}-`;
57
- let t6;
58
- if ($[8] !== t4 || $[9] !== t5) {
59
- t6 = scopeIconSvgIds(t4, t5);
44
+ if ($[5] !== t1 || $[6] !== t2) {
45
+ t3 = {
46
+ ...t1,
47
+ enabled: t2
48
+ };
49
+ $[5] = t1;
50
+ $[6] = t2;
51
+ $[7] = t3;
52
+ } else t3 = $[7];
53
+ const { data } = useQuery(t3);
54
+ const t4 = useId();
55
+ let t5;
56
+ if ($[8] !== t4) {
57
+ let t6;
58
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
59
+ t6 = /[^a-zA-Z0-9_-]/g;
60
+ $[10] = t6;
61
+ } else t6 = $[10];
62
+ t5 = t4.replace(t6, "");
60
63
  $[8] = t4;
61
64
  $[9] = t5;
62
- $[10] = t6;
63
- } else t6 = $[10];
64
- const body = t6;
65
+ } else t5 = $[9];
66
+ const instance = t5;
67
+ const t6 = data?.body ?? "";
68
+ const t7 = `camox-${instance}-`;
69
+ let t8;
70
+ if ($[11] !== t6 || $[12] !== t7) {
71
+ t8 = scopeIconSvgIds(t6, t7);
72
+ $[11] = t6;
73
+ $[12] = t7;
74
+ $[13] = t8;
75
+ } else t8 = $[13];
76
+ const body = t8;
65
77
  const labeled = !!(props["aria-label"] || props["aria-labelledby"]);
66
- const t7 = data?.attributes;
67
- const t8 = labeled ? void 0 : true;
68
- const t9 = labeled ? "img" : void 0;
69
- const t10 = data?.attribution?.url ?? data?.attribution?.license;
70
- const t11 = data?.attribution?.author;
71
- let t12;
72
- if ($[11] !== body) {
73
- t12 = { __html: body };
74
- $[11] = body;
75
- $[12] = t12;
76
- } else t12 = $[12];
77
- let t13;
78
- if ($[13] !== props || $[14] !== t10 || $[15] !== t11 || $[16] !== t12 || $[17] !== t7 || $[18] !== t8 || $[19] !== t9) {
79
- t13 = /* @__PURE__ */ jsx("svg", {
80
- ...t7,
81
- "aria-hidden": t8,
82
- role: t9,
78
+ const t9 = data?.attributes;
79
+ const t10 = labeled ? void 0 : true;
80
+ const t11 = labeled ? "img" : void 0;
81
+ const t12 = data?.attribution?.url ?? data?.attribution?.license;
82
+ const t13 = data?.attribution?.author;
83
+ let t14;
84
+ if ($[14] !== body) {
85
+ t14 = { __html: body };
86
+ $[14] = body;
87
+ $[15] = t14;
88
+ } else t14 = $[15];
89
+ let t15;
90
+ if ($[16] !== props || $[17] !== t10 || $[18] !== t11 || $[19] !== t12 || $[20] !== t13 || $[21] !== t14 || $[22] !== t9) {
91
+ t15 = /* @__PURE__ */ jsx("svg", {
92
+ ...t9,
93
+ "aria-hidden": t10,
94
+ role: t11,
83
95
  focusable: "false",
84
- "data-icon-license": t10,
85
- "data-icon-author": t11,
96
+ "data-icon-license": t12,
97
+ "data-icon-author": t13,
86
98
  ...props,
87
- dangerouslySetInnerHTML: t12
99
+ dangerouslySetInnerHTML: t14
88
100
  });
89
- $[13] = props;
90
- $[14] = t10;
91
- $[15] = t11;
92
- $[16] = t12;
93
- $[17] = t7;
94
- $[18] = t8;
95
- $[19] = t9;
101
+ $[16] = props;
102
+ $[17] = t10;
103
+ $[18] = t11;
104
+ $[19] = t12;
96
105
  $[20] = t13;
97
- } else t13 = $[20];
98
- return t13;
106
+ $[21] = t14;
107
+ $[22] = t9;
108
+ $[23] = t15;
109
+ } else t15 = $[23];
110
+ return t15;
99
111
  }
100
112
  async function prefetchIcons(client, ...sources) {
101
113
  const ids = /* @__PURE__ */ new Set();
@@ -64,7 +64,7 @@ function renderFile(value, children) {
64
64
  download: value.filename
65
65
  }, value);
66
66
  }
67
- /** An asset field's value; unset assets render nothing rather than a fabricated default. */
67
+ /** An asset field's value, or null: an unset asset renders nothing in a record. */
68
68
  function useAsset(name) {
69
69
  const $ = c(3);
70
70
  const { filesMap } = useNormalizedData();