camox 0.42.0 → 0.44.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 (99) hide show
  1. package/bin/camox.mjs +9 -1
  2. package/dist/core/components/lexical/{GradientNode.js → HighlightNode.js} +34 -34
  3. package/dist/core/components/lexical/InlineLexicalEditor.js +14 -14
  4. package/dist/core/components/lexical/InlineStylesPlugin.js +25 -9
  5. package/dist/core/components/lexical/SelectionBroadcaster.js +7 -7
  6. package/dist/core/components/lexical/SidebarLexicalEditor.js +24 -24
  7. package/dist/core/components/lexical/TextLinkPopover.js +191 -208
  8. package/dist/core/components/lexical/editorConfig.js +2 -2
  9. package/dist/core/createApp.d.ts +31 -4
  10. package/dist/core/createApp.js +2 -1
  11. package/dist/core/createBlock.js +24 -10
  12. package/dist/core/createLayout.d.ts +16 -3
  13. package/dist/core/createLayout.js +15 -15
  14. package/dist/core/derivedRoutes.js +1 -1
  15. package/dist/core/dom.d.ts +17 -0
  16. package/dist/core/dom.js +22 -0
  17. package/dist/core/editing/createEditableBlock.d.ts +135 -1
  18. package/dist/core/editing/createEditableBlock.js +209 -74
  19. package/dist/core/lib/contentType.d.ts +5 -0
  20. package/dist/core/lib/contentType.js +11 -0
  21. package/dist/core/lib/fieldTypes.js +1 -0
  22. package/dist/core/lib/iconTypes.d.ts +13 -0
  23. package/dist/core/lib/icons.d.ts +6 -0
  24. package/dist/core/lib/icons.js +130 -0
  25. package/dist/core/lib/lexicalReact.js +4 -4
  26. package/dist/core/lib/lexicalState.js +8 -8
  27. package/dist/core/lib/modifierFormats.js +2 -2
  28. package/dist/core/lib/textLinks.js +5 -2
  29. package/dist/core/lib/textStyles.d.ts +3 -3
  30. package/dist/core/lib/textStyles.js +9 -14
  31. package/dist/core/pageDestinations.js +37 -0
  32. package/dist/features/content/components/AssetCard.js +104 -68
  33. package/dist/features/og/imageResponse.node.js +11 -9
  34. package/dist/features/preview/CamoxPreview.js +148 -146
  35. package/dist/features/preview/commentsEnabled.js +2 -2
  36. package/dist/features/preview/components/AddBlockSidebar.js +1 -1
  37. package/dist/features/preview/components/AssetLightbox.js +17 -6
  38. package/dist/features/preview/components/AttachedComments.js +224 -87
  39. package/dist/features/preview/components/BlockActionsPopover.js +1 -1
  40. package/dist/features/preview/components/CommentTargetQuote.js +22 -21
  41. package/dist/features/preview/components/CreatePageModal.js +4 -4
  42. package/dist/features/preview/components/DerivedLayoutDialog.js +36 -21
  43. package/dist/features/preview/components/DerivedLayoutSidebar.js +45 -26
  44. package/dist/features/preview/components/DerivedPageInfoSidebar.js +38 -26
  45. package/dist/features/preview/components/FieldOverlayStyles.js +29 -9
  46. package/dist/features/preview/components/FieldToolbar.js +12 -12
  47. package/dist/features/preview/components/IconFieldEditor.js +231 -0
  48. package/dist/features/preview/components/ItemFieldsEditor.js +21 -14
  49. package/dist/features/preview/components/LinkFieldEditor.js +212 -209
  50. package/dist/features/preview/components/OverlayTracker.js +1 -1
  51. package/dist/features/preview/components/PageEditorSidebar.js +1 -1
  52. package/dist/features/preview/components/PageInfoSidebar.js +28 -28
  53. package/dist/features/preview/components/PagePicker.js +46 -46
  54. package/dist/features/preview/components/PageTree.js +1 -1
  55. package/dist/features/preview/components/PreviewPanel.js +1 -1
  56. package/dist/features/preview/components/PreviewToolbar.js +224 -225
  57. package/dist/features/preview/components/PublicationRow.js +8 -4
  58. package/dist/features/preview/components/SendFeedbackDialog.js +83 -0
  59. package/dist/features/preview/components/useRepeatableItemActions.js +1 -1
  60. package/dist/features/preview/overlayHighlights.js +54 -0
  61. package/dist/features/preview/previewCommentsStore.js +54 -34
  62. package/dist/features/preview/previewSelection.js +28 -23
  63. package/dist/features/preview/publication.js +10 -0
  64. package/dist/features/preview/usePageComments.js +39 -0
  65. package/dist/features/provider/CoreCamoxProvider.js +38 -6
  66. package/dist/features/provider/LocalhostPreviewProvider.js +81 -45
  67. package/dist/features/provider/components/CommandPalette.js +35 -84
  68. package/dist/features/runtime/pageApp.js +32 -28
  69. package/dist/features/runtime/pageServer.js +4 -1
  70. package/dist/features/runtime/runtime.d.ts +2 -0
  71. package/dist/features/runtime/runtime.js +45 -21
  72. package/dist/features/studio/components/EnvironmentMenu.js +76 -63
  73. package/dist/features/studio/components/Navbar.js +53 -80
  74. package/dist/features/vite/auth.js +1 -50
  75. package/dist/features/vite/iconGeneration.js +16 -0
  76. package/dist/features/vite/layoutGeneration.js +7 -3
  77. package/dist/features/vite/vite.d.ts +4 -2
  78. package/dist/features/vite/vite.js +24 -3
  79. package/dist/hooks/use-page-destinations.js +53 -0
  80. package/dist/lib/auth.js +40 -20
  81. package/dist/lib/preview-handoff.js +13 -0
  82. package/dist/lib/queries.js +12 -1
  83. package/dist/lib/server-auth-cookie.js +21 -0
  84. package/dist/lib/use-project-room.js +5 -2
  85. package/dist/studio-overlays.css +19 -15
  86. package/dist/studio.css +1 -1
  87. package/package.json +9 -4
  88. package/skills/camox/SKILL.md +38 -9
  89. package/skills/camox/references/block-definitions.md +107 -286
  90. package/skills/camox/references/cli-common.md +16 -4
  91. package/skills/camox/references/cli-content.md +158 -0
  92. package/skills/camox/references/cli-environments.md +1 -1
  93. package/skills/camox/references/dom-integrations.md +93 -0
  94. package/skills/camox/references/field-styling.md +50 -0
  95. package/skills/camox/references/layout-definitions.md +33 -147
  96. package/skills/camox/references/routed-layouts.md +71 -0
  97. package/skills/camoxify/SKILL.md +66 -0
  98. package/skills/camox/references/cli-blocks.md +0 -78
  99. package/skills/camox/references/cli-pages.md +0 -53
@@ -2,13 +2,14 @@ import { useLocation } from "../../features/navigation/navigation.js";
2
2
  import { useProjectSlug } from "../../lib/auth.js";
3
3
  import { isFileMarker, isItemMarker, resolveFileMarker, useNormalizedData } from "../../lib/normalized-data.js";
4
4
  import { Type as Type$1, resolveToMarkdown } from "../lib/contentType.js";
5
+ import { IconSvg } from "../lib/icons.js";
5
6
  import { buildImageSrcSet, getDefaultImageSizes, getDefaultImageWidth, transformImageUrl } from "../lib/imageTransform.js";
6
7
  import { markdownToReactNodes } from "../lib/lexicalReact.js";
7
8
  import { blockMutations, pageQueries, projectQueries, repeatableItemMutations } from "../../lib/queries.js";
8
9
  import { previewStore, selectIsCommentMode } from "../../features/preview/previewStore.js";
9
10
  import { usePreviewSelection } from "../../features/preview/previewSelection.js";
10
- import { postOverlayMessage } from "../../features/preview/overlayMessages.js";
11
11
  import { useFrame } from "../../features/preview/components/Frame.js";
12
+ import { postOverlayMessage } from "../../features/preview/overlayMessages.js";
12
13
  import { AddBlockControlBar } from "../components/AddBlockControlBar.js";
13
14
  import { InlineLexicalEditor } from "../components/lexical/InlineLexicalEditor.js";
14
15
  import { useFieldSelection } from "../hooks/useFieldSelection.js";
@@ -185,9 +186,9 @@ function createEditableBlock(options) {
185
186
  };
186
187
  const Field = (t0) => {
187
188
  const $ = c(34);
188
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
189
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
189
190
  for (let $i = 0; $i < 34; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
190
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
191
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
191
192
  }
192
193
  const { name, textStyle, linkStyle, children } = t0;
193
194
  const selectTarget = usePreviewSelection();
@@ -387,11 +388,132 @@ function createEditableBlock(options) {
387
388
  } else t17 = $[33];
388
389
  return t17;
389
390
  };
391
+ const Icon = (t0) => {
392
+ const $ = c(34);
393
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
394
+ for (let $i = 0; $i < 34; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
395
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
396
+ }
397
+ let name;
398
+ let props;
399
+ if ($[1] !== t0) {
400
+ ({name, ...props} = t0);
401
+ $[1] = t0;
402
+ $[2] = name;
403
+ $[3] = props;
404
+ } else {
405
+ name = $[2];
406
+ props = $[3];
407
+ }
408
+ const block = React.use(Context);
409
+ const item = React.use(RepeaterItemContext);
410
+ const selectTarget = usePreviewSelection();
411
+ if (!block) throw new Error("Icon must be used within a Block Component");
412
+ const editable = useIsEditable(block.mode);
413
+ const fieldName = String(name);
414
+ const value = item ? item.itemContent[name] : block.content[name];
415
+ let t1;
416
+ if ($[4] !== block.blockId || $[5] !== fieldName || $[6] !== item) {
417
+ t1 = getOverlayFieldId(block.blockId, item, fieldName);
418
+ $[4] = block.blockId;
419
+ $[5] = fieldName;
420
+ $[6] = item;
421
+ $[7] = t1;
422
+ } else t1 = $[7];
423
+ const fieldId = t1;
424
+ const { window: iframeWindow } = useFrame();
425
+ const [isHovered, setIsHovered] = React.useState(false);
426
+ const isFocused = useFieldSelection(block.blockId, fieldName, "Icon", item?.itemId);
427
+ let t2;
428
+ if ($[8] !== fieldId) {
429
+ t2 = { fieldId };
430
+ $[8] = fieldId;
431
+ $[9] = t2;
432
+ } else t2 = $[9];
433
+ const isHoveredFromSidebar = useOverlayMessage(iframeWindow, editable, "CAMOX_HOVER_FIELD", "CAMOX_HOVER_FIELD_END", t2);
434
+ const overlayState = useOverlayState(isHovered || isHoveredFromSidebar, isFocused);
435
+ const t3 = editable ? fieldId : void 0;
436
+ const t4 = editable ? "icon" : void 0;
437
+ let t5;
438
+ if ($[10] !== editable || $[11] !== overlayState) {
439
+ t5 = editable ? overlayState : {};
440
+ $[10] = editable;
441
+ $[11] = overlayState;
442
+ $[12] = t5;
443
+ } else t5 = $[12];
444
+ const t6 = editable && options.synced ? "synced" : void 0;
445
+ let t7;
446
+ let t8;
447
+ if ($[13] !== editable || $[14] !== props) {
448
+ t7 = (event) => {
449
+ if (editable) setIsHovered(true);
450
+ props.onMouseEnter?.(event);
451
+ };
452
+ t8 = (event_0) => {
453
+ if (editable) setIsHovered(false);
454
+ props.onMouseLeave?.(event_0);
455
+ };
456
+ $[13] = editable;
457
+ $[14] = props;
458
+ $[15] = t7;
459
+ $[16] = t8;
460
+ } else {
461
+ t7 = $[15];
462
+ t8 = $[16];
463
+ }
464
+ let t9;
465
+ if ($[17] !== block.blockId || $[18] !== editable || $[19] !== fieldName || $[20] !== item || $[21] !== props.onClickCapture || $[22] !== selectTarget) {
466
+ t9 = editable ? (event_1) => selectTarget(item?.itemId != null ? {
467
+ type: "item-field",
468
+ blockId: block.blockId,
469
+ itemId: item.itemId,
470
+ fieldName,
471
+ fieldType: "Icon"
472
+ } : {
473
+ type: "block-field",
474
+ blockId: block.blockId,
475
+ fieldName,
476
+ fieldType: "Icon"
477
+ }, event_1) : props.onClickCapture;
478
+ $[17] = block.blockId;
479
+ $[18] = editable;
480
+ $[19] = fieldName;
481
+ $[20] = item;
482
+ $[21] = props.onClickCapture;
483
+ $[22] = selectTarget;
484
+ $[23] = t9;
485
+ } else t9 = $[23];
486
+ let t10;
487
+ if ($[24] !== props || $[25] !== t3 || $[26] !== t4 || $[27] !== t5 || $[28] !== t6 || $[29] !== t7 || $[30] !== t8 || $[31] !== t9 || $[32] !== value) {
488
+ t10 = /* @__PURE__ */ jsx(IconSvg, {
489
+ ...props,
490
+ iconId: value,
491
+ "data-camox-field-id": t3,
492
+ "data-camox-field-type": t4,
493
+ ...t5,
494
+ "data-camox-overlay-mode": t6,
495
+ onMouseEnter: t7,
496
+ onMouseLeave: t8,
497
+ onClickCapture: t9
498
+ });
499
+ $[24] = props;
500
+ $[25] = t3;
501
+ $[26] = t4;
502
+ $[27] = t5;
503
+ $[28] = t6;
504
+ $[29] = t7;
505
+ $[30] = t8;
506
+ $[31] = t9;
507
+ $[32] = value;
508
+ $[33] = t10;
509
+ } else t10 = $[33];
510
+ return t10;
511
+ };
390
512
  const Embed = (t0) => {
391
513
  const $ = c(39);
392
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
514
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
393
515
  for (let $i = 0; $i < 39; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
394
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
516
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
395
517
  }
396
518
  const { name, children } = t0;
397
519
  const selectTarget = usePreviewSelection();
@@ -557,9 +679,9 @@ function createEditableBlock(options) {
557
679
  };
558
680
  const Link = (t0) => {
559
681
  const $ = c(32);
560
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
682
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
561
683
  for (let $i = 0; $i < 32; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
562
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
684
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
563
685
  }
564
686
  const { name, children } = t0;
565
687
  const selectTarget = usePreviewSelection();
@@ -766,9 +888,9 @@ function createEditableBlock(options) {
766
888
  };
767
889
  const Image = (t0) => {
768
890
  const $ = c(22);
769
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
891
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
770
892
  for (let $i = 0; $i < 22; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
771
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
893
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
772
894
  }
773
895
  const { name, children } = t0;
774
896
  const selectTarget = usePreviewSelection();
@@ -886,9 +1008,9 @@ function createEditableBlock(options) {
886
1008
  };
887
1009
  const File = (t0) => {
888
1010
  const $ = c(9);
889
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
1011
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
890
1012
  for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
891
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1013
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
892
1014
  }
893
1015
  const { name, children } = t0;
894
1016
  const blockContext = React.use(Context);
@@ -962,10 +1084,10 @@ function createEditableBlock(options) {
962
1084
  const ImageList = _AssetList;
963
1085
  const FileList = _AssetList;
964
1086
  const RepeaterItemWrapper = (t0) => {
965
- const $ = c(20);
966
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
967
- for (let $i = 0; $i < 20; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
968
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1087
+ const $ = c(23);
1088
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
1089
+ for (let $i = 0; $i < 23; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1090
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
969
1091
  }
970
1092
  const { itemId, blockId, mode, children } = t0;
971
1093
  const isContentEditable = useIsEditable(mode);
@@ -974,22 +1096,33 @@ function createEditableBlock(options) {
974
1096
  const isCommentMode = useSelector(previewStore, selectIsCommentMode);
975
1097
  const [isLocallyHovered, setIsLocallyHovered] = React.useState(false);
976
1098
  const isRepeaterHovered = React.useContext(RepeaterHoverContext);
977
- const t1 = String(blockId);
978
- const t2 = String(itemId);
979
- let t3;
980
- if ($[1] !== t1 || $[2] !== t2) {
981
- t3 = {
982
- blockId: t1,
983
- itemId: t2
1099
+ let t1;
1100
+ if ($[1] !== blockId || $[2] !== itemId) {
1101
+ t1 = (state) => {
1102
+ const selection = state.context.selection;
1103
+ return selection?.type === "item" && selection.blockId === blockId && selection.itemId === itemId;
984
1104
  };
985
- $[1] = t1;
986
- $[2] = t2;
987
- $[3] = t3;
988
- } else t3 = $[3];
989
- const overlayState = useOverlayState(useOverlayMessage(iframeWindow, isContentEditable, "CAMOX_HOVER_REPEATER_ITEM", "CAMOX_HOVER_REPEATER_ITEM_END", t3) || isRepeaterHovered || isCommentMode && isLocallyHovered);
1105
+ $[1] = blockId;
1106
+ $[2] = itemId;
1107
+ $[3] = t1;
1108
+ } else t1 = $[3];
1109
+ const isSelected = useSelector(previewStore, t1);
1110
+ const t2 = String(blockId);
1111
+ const t3 = String(itemId);
990
1112
  let t4;
991
- if ($[4] !== blockId || $[5] !== isCommentMode || $[6] !== isContentEditable || $[7] !== itemId || $[8] !== selectTarget) {
992
- t4 = (event) => {
1113
+ if ($[4] !== t2 || $[5] !== t3) {
1114
+ t4 = {
1115
+ blockId: t2,
1116
+ itemId: t3
1117
+ };
1118
+ $[4] = t2;
1119
+ $[5] = t3;
1120
+ $[6] = t4;
1121
+ } else t4 = $[6];
1122
+ const overlayState = useOverlayState(useOverlayMessage(iframeWindow, isContentEditable, "CAMOX_HOVER_REPEATER_ITEM", "CAMOX_HOVER_REPEATER_ITEM_END", t4) || isRepeaterHovered || isCommentMode && isLocallyHovered, isSelected);
1123
+ let t5;
1124
+ if ($[7] !== blockId || $[8] !== isCommentMode || $[9] !== isContentEditable || $[10] !== itemId || $[11] !== selectTarget) {
1125
+ t5 = (event) => {
993
1126
  if (!isContentEditable || !isCommentMode || itemId == null) return;
994
1127
  const target = event.target;
995
1128
  if (target.closest("[data-camox-field-id]")) return;
@@ -1000,57 +1133,57 @@ function createEditableBlock(options) {
1000
1133
  itemId
1001
1134
  }, event);
1002
1135
  };
1003
- $[4] = blockId;
1004
- $[5] = isCommentMode;
1005
- $[6] = isContentEditable;
1006
- $[7] = itemId;
1007
- $[8] = selectTarget;
1008
- $[9] = t4;
1009
- } else t4 = $[9];
1010
- const handleClick = t4;
1011
- const t5 = isContentEditable ? itemId : void 0;
1012
- let t6;
1136
+ $[7] = blockId;
1137
+ $[8] = isCommentMode;
1138
+ $[9] = isContentEditable;
1139
+ $[10] = itemId;
1140
+ $[11] = selectTarget;
1141
+ $[12] = t5;
1142
+ } else t5 = $[12];
1143
+ const handleClick = t5;
1144
+ const t6 = isContentEditable ? itemId : void 0;
1013
1145
  let t7;
1014
- if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
1015
- t6 = () => setIsLocallyHovered(true);
1016
- t7 = () => setIsLocallyHovered(false);
1017
- $[10] = t6;
1018
- $[11] = t7;
1019
- } else {
1020
- t6 = $[10];
1021
- t7 = $[11];
1022
- }
1023
1146
  let t8;
1024
- if ($[12] !== isContentEditable || $[13] !== overlayState) {
1025
- t8 = isContentEditable ? overlayState : {};
1026
- $[12] = isContentEditable;
1027
- $[13] = overlayState;
1147
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
1148
+ t7 = () => setIsLocallyHovered(true);
1149
+ t8 = () => setIsLocallyHovered(false);
1150
+ $[13] = t7;
1028
1151
  $[14] = t8;
1029
- } else t8 = $[14];
1152
+ } else {
1153
+ t7 = $[13];
1154
+ t8 = $[14];
1155
+ }
1030
1156
  let t9;
1031
- if ($[15] !== children || $[16] !== handleClick || $[17] !== t5 || $[18] !== t8) {
1032
- t9 = /* @__PURE__ */ jsx("div", {
1033
- "data-camox-repeater-item-id": t5,
1157
+ if ($[15] !== isContentEditable || $[16] !== overlayState) {
1158
+ t9 = isContentEditable ? overlayState : {};
1159
+ $[15] = isContentEditable;
1160
+ $[16] = overlayState;
1161
+ $[17] = t9;
1162
+ } else t9 = $[17];
1163
+ let t10;
1164
+ if ($[18] !== children || $[19] !== handleClick || $[20] !== t6 || $[21] !== t9) {
1165
+ t10 = /* @__PURE__ */ jsx("div", {
1166
+ "data-camox-repeater-item-id": t6,
1034
1167
  onClickCapture: handleClick,
1035
- onMouseEnter: t6,
1036
- onMouseLeave: t7,
1037
- ...t8,
1168
+ onMouseEnter: t7,
1169
+ onMouseLeave: t8,
1170
+ ...t9,
1038
1171
  "data-camox-overlay-mode": options.synced ? "synced" : void 0,
1039
1172
  children
1040
1173
  });
1041
- $[15] = children;
1042
- $[16] = handleClick;
1043
- $[17] = t5;
1044
- $[18] = t8;
1045
- $[19] = t9;
1046
- } else t9 = $[19];
1047
- return t9;
1174
+ $[18] = children;
1175
+ $[19] = handleClick;
1176
+ $[20] = t6;
1177
+ $[21] = t9;
1178
+ $[22] = t10;
1179
+ } else t10 = $[22];
1180
+ return t10;
1048
1181
  };
1049
1182
  const RepeaterHoverProvider = (t0) => {
1050
1183
  const $ = c(7);
1051
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
1184
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
1052
1185
  for (let $i = 0; $i < 7; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1053
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1186
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
1054
1187
  }
1055
1188
  const { blockId, fieldName, children } = t0;
1056
1189
  const isContentEditable = useIsEditable("site");
@@ -1081,9 +1214,9 @@ function createEditableBlock(options) {
1081
1214
  };
1082
1215
  const Repeater = (t0) => {
1083
1216
  const $ = c(27);
1084
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
1217
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
1085
1218
  for (let $i = 0; $i < 27; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1086
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1219
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
1087
1220
  }
1088
1221
  const { name, children } = t0;
1089
1222
  const blockContext = React.use(Context);
@@ -1139,6 +1272,7 @@ function createEditableBlock(options) {
1139
1272
  const itemApi = {
1140
1273
  Field: ItemField,
1141
1274
  Link: ItemLink,
1275
+ Icon,
1142
1276
  Embed: ItemEmbed,
1143
1277
  Image: ItemImage,
1144
1278
  File: ItemFile,
@@ -1208,9 +1342,9 @@ function createEditableBlock(options) {
1208
1342
  };
1209
1343
  const BlockComponent = (t0) => {
1210
1344
  const $ = c(74);
1211
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
1345
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
1212
1346
  for (let $i = 0; $i < 74; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1213
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1347
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
1214
1348
  }
1215
1349
  const { blockData, mode, isFirstBlock, showAddBlockTop, showAddBlockBottom, addBlockAfterPosition } = t0;
1216
1350
  const isContentEditable = useIsEditable(mode);
@@ -1520,9 +1654,9 @@ function createEditableBlock(options) {
1520
1654
  */
1521
1655
  const Detached = (t0) => {
1522
1656
  const $ = c(32);
1523
- if ($[0] !== "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883") {
1657
+ if ($[0] !== "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6") {
1524
1658
  for (let $i = 0; $i < 32; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1525
- $[0] = "9390cfa3e0bf4e772e82a3d1352a7d56d8ae67f112d215b6d45231db17c01883";
1659
+ $[0] = "8c4e70844cd2f2752041099dd31f8aabc30492cc078e28f85c1026ddf30f63b6";
1526
1660
  }
1527
1661
  const { children } = t0;
1528
1662
  const ctx = React.use(Context);
@@ -1651,6 +1785,7 @@ function createEditableBlock(options) {
1651
1785
  };
1652
1786
  return {
1653
1787
  Detached,
1788
+ Icon,
1654
1789
  Field,
1655
1790
  Embed,
1656
1791
  Link,
@@ -1,3 +1,4 @@
1
+ import { IconId, IconValue } from "./iconTypes.js";
1
2
  import * as _$_sinclair_typebox0 from "@sinclair/typebox";
2
3
  import { Static, TArray, TObject, TSchema, TUnsafe } from "@sinclair/typebox";
3
4
 
@@ -79,6 +80,10 @@ type FileValue = {
79
80
  * All fields must have default values.
80
81
  */
81
82
  declare const Type$1: {
83
+ Icon: (options: {
84
+ default: IconId;
85
+ title?: string;
86
+ }) => TUnsafe<IconValue>;
82
87
  /**
83
88
  * Creates a string field with a required default value.
84
89
  *
@@ -127,6 +127,17 @@ function _fileList(options) {
127
127
  * All fields must have default values.
128
128
  */
129
129
  const Type$1 = {
130
+ Icon: (options) => {
131
+ const ids = typeof __CAMOX_ICON_IDS__ === "undefined" ? [] : __CAMOX_ICON_IDS__;
132
+ if (!ids.includes(options.default)) throw new Error(`Invalid icon default "${String(options.default)}". Configure icons in camox() first.`);
133
+ return Type.Unsafe({
134
+ type: "string",
135
+ fieldType: "Icon",
136
+ enum: [...ids],
137
+ default: options.default,
138
+ title: options.title
139
+ });
140
+ },
130
141
  /**
131
142
  * Creates a string field with a required default value.
132
143
  *
@@ -1,5 +1,6 @@
1
1
  //#region src/core/lib/fieldTypes.tsx
2
2
  const fieldTypesDictionary = {
3
+ Icon: { hasOwnView: true },
3
4
  String: { hasOwnView: true },
4
5
  Repeater: { hasOwnView: true },
5
6
  Enum: { hasOwnView: true },
@@ -0,0 +1,13 @@
1
+ //#region src/core/lib/iconTypes.d.ts
2
+ declare global {
3
+ interface CamoxIconRegistry {}
4
+ }
5
+ type IconId = CamoxIconRegistry extends {
6
+ ids: infer IDs extends string;
7
+ } ? IDs : never;
8
+ declare const iconBrand: unique symbol;
9
+ type IconValue = string & {
10
+ readonly [iconBrand]: true;
11
+ };
12
+ //#endregion
13
+ export { IconId, IconValue };
@@ -0,0 +1,6 @@
1
+ import { QueryClient } from "@tanstack/react-query";
2
+ import { SVGProps } from "react";
3
+ //#region src/core/lib/icons.d.ts
4
+ type IconProps = Omit<SVGProps<SVGSVGElement>, "children" | "dangerouslySetInnerHTML" | "name">;
5
+ //#endregion
6
+ export { IconProps };
@@ -0,0 +1,130 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { useId } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { ICON_CATALOG_VERSION, scopeIconSvgIds } from "@camox/api-contract";
6
+
7
+ //#region src/core/lib/icons.tsx
8
+ const iconQuery = (id) => ({
9
+ queryKey: [
10
+ "camox-icon",
11
+ ICON_CATALOG_VERSION,
12
+ id
13
+ ],
14
+ staleTime: Infinity,
15
+ queryFn: async () => {
16
+ if (!/^[a-z0-9-]+:[a-z0-9-]+$/.test(id)) throw new Error("Invalid icon ID");
17
+ const response = await fetch(`${__CAMOX_API_URL__}/icons/${ICON_CATALOG_VERSION}/${id.replace(":", "/")}`);
18
+ if (!response.ok) throw new Error(`Unable to resolve icon ${id}`);
19
+ return response.json();
20
+ }
21
+ });
22
+ function IconSvg(t0) {
23
+ const $ = c(22);
24
+ if ($[0] !== "8eab78246422a9042abf36067eb940cbd070510c54a187ee11896f87a1f6085b") {
25
+ for (let $i = 0; $i < 22; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
26
+ $[0] = "8eab78246422a9042abf36067eb940cbd070510c54a187ee11896f87a1f6085b";
27
+ }
28
+ let iconId;
29
+ let props;
30
+ if ($[1] !== t0) {
31
+ ({iconId, ...props} = t0);
32
+ $[1] = t0;
33
+ $[2] = iconId;
34
+ $[3] = props;
35
+ } else {
36
+ iconId = $[2];
37
+ props = $[3];
38
+ }
39
+ let t1;
40
+ if ($[4] !== iconId) {
41
+ t1 = iconQuery(iconId);
42
+ $[4] = iconId;
43
+ $[5] = t1;
44
+ } else t1 = $[5];
45
+ const { data } = useQuery(t1);
46
+ const t2 = useId();
47
+ let t3;
48
+ if ($[6] !== t2) {
49
+ let t4;
50
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
51
+ t4 = /[^a-zA-Z0-9_-]/g;
52
+ $[8] = t4;
53
+ } else t4 = $[8];
54
+ t3 = t2.replace(t4, "");
55
+ $[6] = t2;
56
+ $[7] = t3;
57
+ } else t3 = $[7];
58
+ const instance = t3;
59
+ const t4 = data?.body ?? "";
60
+ const t5 = `camox-${instance}-`;
61
+ let t6;
62
+ if ($[9] !== t4 || $[10] !== t5) {
63
+ t6 = scopeIconSvgIds(t4, t5);
64
+ $[9] = t4;
65
+ $[10] = t5;
66
+ $[11] = t6;
67
+ } else t6 = $[11];
68
+ const body = t6;
69
+ const labeled = !!(props["aria-label"] || props["aria-labelledby"]);
70
+ const t7 = data?.attributes;
71
+ const t8 = labeled ? void 0 : true;
72
+ const t9 = labeled ? "img" : void 0;
73
+ const t10 = data?.attribution?.url ?? data?.attribution?.license;
74
+ const t11 = data?.attribution?.author;
75
+ let t12;
76
+ if ($[12] !== body) {
77
+ t12 = { __html: body };
78
+ $[12] = body;
79
+ $[13] = t12;
80
+ } else t12 = $[13];
81
+ let t13;
82
+ if ($[14] !== props || $[15] !== t10 || $[16] !== t11 || $[17] !== t12 || $[18] !== t7 || $[19] !== t8 || $[20] !== t9) {
83
+ t13 = /* @__PURE__ */ jsx("svg", {
84
+ ...t7,
85
+ "aria-hidden": t8,
86
+ role: t9,
87
+ focusable: "false",
88
+ "data-icon-license": t10,
89
+ "data-icon-author": t11,
90
+ ...props,
91
+ dangerouslySetInnerHTML: t12
92
+ });
93
+ $[14] = props;
94
+ $[15] = t10;
95
+ $[16] = t11;
96
+ $[17] = t12;
97
+ $[18] = t7;
98
+ $[19] = t8;
99
+ $[20] = t9;
100
+ $[21] = t13;
101
+ } else t13 = $[21];
102
+ return t13;
103
+ }
104
+ async function prefetchIcons(client, ...sources) {
105
+ const ids = /* @__PURE__ */ new Set();
106
+ const allowed = /* @__PURE__ */ new Set();
107
+ const collectSchemas = (value) => {
108
+ if (!value || typeof value !== "object") return;
109
+ const schema = value;
110
+ if (schema.fieldType === "Icon" && Array.isArray(schema.enum)) {
111
+ for (const id of schema.enum) if (typeof id === "string") allowed.add(id);
112
+ return;
113
+ }
114
+ Object.values(value).forEach(collectSchemas);
115
+ };
116
+ sources.forEach(collectSchemas);
117
+ const walk = (value) => {
118
+ if (!value || typeof value !== "object") return;
119
+ for (const [key, child] of Object.entries(value)) {
120
+ if (key === "enum") continue;
121
+ if (typeof child === "string" && allowed.has(child)) ids.add(child);
122
+ else walk(child);
123
+ }
124
+ };
125
+ sources.forEach(walk);
126
+ await Promise.all([...ids].map((id) => client.fetchQuery(iconQuery(id))));
127
+ }
128
+
129
+ //#endregion
130
+ export { IconSvg, prefetchIcons };
@@ -1,7 +1,7 @@
1
1
  import { FORMAT_FLAGS } from "./modifierFormats.js";
2
2
  import { getPageIdFromTextLinkTarget, isHttpTextLinkTarget, resolveTextLinkHref, shouldOpenTextLinkInNewTab } from "./textLinks.js";
3
3
  import { isLexicalState, markdownToLexicalState } from "./lexicalState.js";
4
- import { getGradientStyle, getTextAppearance } from "./textStyles.js";
4
+ import { getHighlightStyle, getTextAppearance } from "./textStyles.js";
5
5
  import * as React from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
 
@@ -23,9 +23,9 @@ function markdownToReactNodes(value, options = {}) {
23
23
  }
24
24
  if (node.type === "linebreak") return /* @__PURE__ */ jsx("br", {}, key);
25
25
  const children = (node.children ?? []).map(render);
26
- if (node.type === "gradient") return /* @__PURE__ */ jsx("span", {
27
- "data-camox-gradient": "",
28
- ...getGradientStyle(options),
26
+ if (node.type === "highlight") return /* @__PURE__ */ jsx("span", {
27
+ "data-camox-highlight": "",
28
+ ...getHighlightStyle(options),
29
29
  children
30
30
  }, key);
31
31
  if (node.type === "link") {
@@ -36,7 +36,7 @@ function lexicalStateToMarkdown(serialized) {
36
36
  }
37
37
  }
38
38
  function extractMarkdownFromNode(node) {
39
- if (node.type === "gradient") return `<gradient>${serializeInlineChildren(node.children ?? [])}</gradient>`;
39
+ if (node.type === "highlight") return `<highlight>${serializeInlineChildren(node.children ?? [])}</highlight>`;
40
40
  if (node.type === "link") {
41
41
  const text = serializeInlineChildren(node.children ?? []);
42
42
  const url = typeof node.url === "string" ? node.url : "";
@@ -50,9 +50,9 @@ function extractMarkdownFromNode(node) {
50
50
  function serializeInlineChildren(nodes) {
51
51
  const marks = [
52
52
  {
53
- flag: FORMAT_FLAGS.underline,
54
- open: "<u>",
55
- close: "</u>"
53
+ flag: FORMAT_FLAGS.strikethrough,
54
+ open: "<s>",
55
+ close: "</s>"
56
56
  },
57
57
  {
58
58
  flag: FORMAT_FLAGS.bold,
@@ -90,7 +90,7 @@ function serializeInlineChildren(nodes) {
90
90
  let text = (node.text ?? "").replace(/[\\*_[\]<>]/g, "\\$&");
91
91
  if (node.format & FORMAT_FLAGS.italic) text = `<em>${text}</em>`;
92
92
  if (node.format & FORMAT_FLAGS.bold) text = `<strong>${text}</strong>`;
93
- if (node.format & FORMAT_FLAGS.underline) text = `<u>${text}</u>`;
93
+ if (node.format & FORMAT_FLAGS.strikethrough) text = `<s>${text}</s>`;
94
94
  return text;
95
95
  }).join("");
96
96
  }
@@ -204,7 +204,7 @@ function parseTokens(tokens, format) {
204
204
  else nodes.push(...children);
205
205
  continue;
206
206
  }
207
- const tag = token.type === "html" ? /^<(gradient|u|strong|em)>$/.exec(token.raw)?.[1] : void 0;
207
+ const tag = token.type === "html" ? /^<(highlight|s|strong|em)>$/.exec(token.raw)?.[1] : void 0;
208
208
  if (tag) {
209
209
  let depth = 1;
210
210
  let end = i + 1;
@@ -216,12 +216,12 @@ function parseTokens(tokens, format) {
216
216
  }
217
217
  if (!depth) {
218
218
  const tagFlags = {
219
- u: FORMAT_FLAGS.underline,
219
+ s: FORMAT_FLAGS.strikethrough,
220
220
  strong: FORMAT_FLAGS.bold,
221
221
  em: FORMAT_FLAGS.italic
222
222
  };
223
223
  const children = parseTokens(tokens.slice(i + 1, end), format | (tagFlags[tag] ?? 0));
224
- if (tag === "gradient") nodes.push(inlineElement("gradient", children));
224
+ if (tag === "highlight") nodes.push(inlineElement("highlight", children));
225
225
  else nodes.push(...children);
226
226
  i = end;
227
227
  continue;