tinacms 3.8.2 → 3.8.4

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/index.js CHANGED
@@ -36,6 +36,8 @@ import { useHTMLInputCursorState, useComboboxInput } from "@udecode/plate-combob
36
36
  import { useComboboxContext, Combobox as Combobox$1, useComboboxStore, ComboboxProvider, Portal, ComboboxPopover, ComboboxItem } from "@ariakit/react";
37
37
  import { filterWords } from "@udecode/plate-combobox";
38
38
  import { toggleList, unwrapList, getListItemEntry } from "@udecode/plate-list";
39
+ import { normalizeHeadingLevels, ALL_HEADING_LEVELS, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, canonicalPath, validateSchema } from "@tinacms/schema-tools";
40
+ import { NAMER, resolveField } from "@tinacms/schema-tools";
39
41
  import { useBlockSelected, BlockSelectionPlugin } from "@udecode/plate-selection/react";
40
42
  import "@udecode/plate-dnd";
41
43
  import { setCellBackground } from "@udecode/plate-table";
@@ -93,8 +95,6 @@ import { VscNewFile } from "react-icons/vsc";
93
95
  import { TbLogs } from "react-icons/tb";
94
96
  import { ImUsers, ImFilesEmpty } from "react-icons/im";
95
97
  import { PiSidebarSimpleLight } from "react-icons/pi";
96
- import { TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, canonicalPath, validateSchema } from "@tinacms/schema-tools";
97
- import { NAMER, resolveField } from "@tinacms/schema-tools";
98
98
  import gql from "graphql-tag";
99
99
  import { useLocation, NavLink, useNavigate, useParams, Link as Link$1, HashRouter, Routes, Route } from "react-router-dom";
100
100
  import { RiHome2Line } from "react-icons/ri";
@@ -1356,7 +1356,7 @@ function CodeBlockCombobox({
1356
1356
  const element = useElement();
1357
1357
  const value = element.lang || "plaintext";
1358
1358
  const [searchValue, setSearchValue] = React__default.useState("");
1359
- const items2 = React__default.useMemo(
1359
+ const items = React__default.useMemo(
1360
1360
  () => languages.filter(
1361
1361
  (language) => !searchValue || language.label.toLowerCase().includes(searchValue.toLowerCase())
1362
1362
  ),
@@ -1394,7 +1394,7 @@ function CodeBlockCombobox({
1394
1394
  onValueChange: (value2) => setSearchValue(value2),
1395
1395
  placeholder: "Search language..."
1396
1396
  }
1397
- ), /* @__PURE__ */ React__default.createElement(CommandEmpty$1, null, "No language found."), /* @__PURE__ */ React__default.createElement(CommandList$1, { className: "h-48 overflow-y-auto" }, /* @__PURE__ */ React__default.createElement(CommandGroup$1, null, items2.map((language) => /* @__PURE__ */ React__default.createElement(
1397
+ ), /* @__PURE__ */ React__default.createElement(CommandEmpty$1, null, "No language found."), /* @__PURE__ */ React__default.createElement(CommandList$1, { className: "h-48 overflow-y-auto" }, /* @__PURE__ */ React__default.createElement(CommandGroup$1, null, items.map((language) => /* @__PURE__ */ React__default.createElement(
1398
1398
  CommandItem$1,
1399
1399
  {
1400
1400
  key: language.label,
@@ -2435,16 +2435,16 @@ const InlineCombobox = ({
2435
2435
  const store = useComboboxStore({
2436
2436
  setValue: (newValue) => startTransition(() => setValue(newValue))
2437
2437
  });
2438
- const items2 = store.useState("items");
2438
+ const items = store.useState("items");
2439
2439
  useEffect(() => {
2440
2440
  if (!store.getState().activeId) {
2441
2441
  store.setActiveId(store.first());
2442
2442
  }
2443
- }, [items2, store]);
2443
+ }, [items, store]);
2444
2444
  return /* @__PURE__ */ React__default.createElement("span", { contentEditable: false }, /* @__PURE__ */ React__default.createElement(
2445
2445
  ComboboxProvider,
2446
2446
  {
2447
- open: (items2.length > 0 || hasEmpty) && (!hideWhenNoValue || value.length > 0),
2447
+ open: (items.length > 0 || hasEmpty) && (!hideWhenNoValue || value.length > 0),
2448
2448
  store
2449
2449
  },
2450
2450
  /* @__PURE__ */ React__default.createElement(InlineComboboxContext.Provider, { value: contextValue }, children)
@@ -2546,14 +2546,14 @@ const InlineComboboxEmpty = ({
2546
2546
  }) => {
2547
2547
  const { setHasEmpty } = useContext(InlineComboboxContext);
2548
2548
  const store = useComboboxContext();
2549
- const items2 = store.useState("items");
2549
+ const items = store.useState("items");
2550
2550
  useEffect(() => {
2551
2551
  setHasEmpty(true);
2552
2552
  return () => {
2553
2553
  setHasEmpty(false);
2554
2554
  };
2555
2555
  }, [setHasEmpty]);
2556
- if (items2.length > 0)
2556
+ if (items.length > 0)
2557
2557
  return null;
2558
2558
  return /* @__PURE__ */ React__default.createElement(
2559
2559
  "div",
@@ -2563,28 +2563,80 @@ const InlineComboboxEmpty = ({
2563
2563
  children
2564
2564
  );
2565
2565
  };
2566
- const rules = [
2567
- {
2568
- icon: Icons.h1,
2569
- onSelect: (editor) => {
2570
- editor.tf.toggleBlock(HEADING_KEYS.h1);
2566
+ const ToolbarContext = createContext(
2567
+ void 0
2568
+ );
2569
+ const ToolbarProvider = ({
2570
+ tinaForm,
2571
+ templates,
2572
+ overrides,
2573
+ children
2574
+ }) => {
2575
+ const configured = !Array.isArray(overrides) ? overrides == null ? void 0 : overrides.headingLevels : void 0;
2576
+ const headingLevelsConfigured = Array.isArray(configured);
2577
+ const headingLevels = useMemo(
2578
+ () => configured ? normalizeHeadingLevels(configured) : ALL_HEADING_LEVELS,
2579
+ [configured]
2580
+ );
2581
+ return /* @__PURE__ */ React__default.createElement(
2582
+ ToolbarContext.Provider,
2583
+ {
2584
+ value: {
2585
+ tinaForm,
2586
+ templates,
2587
+ overrides,
2588
+ headingLevels,
2589
+ headingLevelsConfigured
2590
+ }
2571
2591
  },
2592
+ children
2593
+ );
2594
+ };
2595
+ const useToolbarContext = () => {
2596
+ const context = useContext(ToolbarContext);
2597
+ if (!context) {
2598
+ throw new Error("useToolbarContext must be used within a ToolbarProvider");
2599
+ }
2600
+ return context;
2601
+ };
2602
+ const headingRulesByLevel = {
2603
+ h1: {
2604
+ icon: Icons.h1,
2605
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h1),
2572
2606
  value: "Heading 1"
2573
2607
  },
2574
- {
2608
+ h2: {
2575
2609
  icon: Icons.h2,
2576
- onSelect: (editor) => {
2577
- editor.tf.toggleBlock(HEADING_KEYS.h2);
2578
- },
2610
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h2),
2579
2611
  value: "Heading 2"
2580
2612
  },
2581
- {
2613
+ h3: {
2582
2614
  icon: Icons.h3,
2583
- onSelect: (editor) => {
2584
- editor.tf.toggleBlock(HEADING_KEYS.h3);
2585
- },
2615
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h3),
2586
2616
  value: "Heading 3"
2587
2617
  },
2618
+ h4: {
2619
+ icon: Icons.h4,
2620
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h4),
2621
+ value: "Heading 4"
2622
+ },
2623
+ h5: {
2624
+ icon: Icons.h5,
2625
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h5),
2626
+ value: "Heading 5"
2627
+ },
2628
+ h6: {
2629
+ icon: Icons.h6,
2630
+ onSelect: (editor) => editor.tf.toggleBlock(HEADING_KEYS.h6),
2631
+ value: "Heading 6"
2632
+ }
2633
+ };
2634
+ const DEFAULT_SLASH_HEADING_LEVELS = [
2635
+ "h1",
2636
+ "h2",
2637
+ "h3"
2638
+ ];
2639
+ const listRules = [
2588
2640
  {
2589
2641
  icon: Icons.ul,
2590
2642
  keywords: ["ul", "unordered list"],
@@ -2605,6 +2657,12 @@ const rules = [
2605
2657
  const SlashInputElement = withRef(
2606
2658
  ({ className, ...props }, ref) => {
2607
2659
  const { children, editor, element } = props;
2660
+ const { headingLevels, headingLevelsConfigured } = useToolbarContext();
2661
+ const slashHeadingLevels = headingLevelsConfigured ? headingLevels : DEFAULT_SLASH_HEADING_LEVELS;
2662
+ const rules = [
2663
+ ...slashHeadingLevels.map((level) => headingRulesByLevel[level]),
2664
+ ...listRules
2665
+ ];
2608
2666
  useEffect(() => {
2609
2667
  captureEvent(SlashCommandOpenedEvent);
2610
2668
  }, []);
@@ -3269,7 +3327,7 @@ const Heading1 = ({
3269
3327
  headerClasses,
3270
3328
  blockClasses,
3271
3329
  className,
3272
- "text-4xl mb-4 last:mb-0 mt-6 first:mt-0 font-libre-baskerville"
3330
+ "text-4xl mb-4 last:mb-0 mt-6 first:mt-0 font-inter"
3273
3331
  )
3274
3332
  },
3275
3333
  children
@@ -3286,7 +3344,7 @@ const Heading2 = ({
3286
3344
  headerClasses,
3287
3345
  blockClasses,
3288
3346
  className,
3289
- "text-3xl mb-4 last:mb-0 mt-6 first:mt-0 font-libre-baskerville"
3347
+ "text-3xl mb-4 last:mb-0 mt-6 first:mt-0 font-inter"
3290
3348
  )
3291
3349
  },
3292
3350
  children
@@ -3303,7 +3361,7 @@ const Heading3 = ({
3303
3361
  headerClasses,
3304
3362
  blockClasses,
3305
3363
  className,
3306
- "text-2xl mb-4 last:mb-0 mt-6 first:mt-0 font-libre-baskerville"
3364
+ "text-2xl mb-4 last:mb-0 mt-6 first:mt-0 font-inter"
3307
3365
  )
3308
3366
  },
3309
3367
  children
@@ -3320,13 +3378,13 @@ const Heading4 = ({
3320
3378
  headerClasses,
3321
3379
  blockClasses,
3322
3380
  className,
3323
- "text-xl mb-4 last:mb-0 mt-6 first:mt-0 font-libre-baskerville"
3381
+ "text-xl mb-4 last:mb-0 mt-6 first:mt-0 font-inter"
3324
3382
  )
3325
3383
  },
3326
3384
  children
3327
3385
  );
3328
- const headerSerifStyle = {
3329
- fontFamily: "'Libre Baskerville', serif",
3386
+ const headerFontStyle = {
3387
+ fontFamily: "'Inter', sans-serif",
3330
3388
  fontWeight: "400"
3331
3389
  };
3332
3390
  const Heading5 = ({
@@ -3343,7 +3401,7 @@ const Heading5 = ({
3343
3401
  className,
3344
3402
  "text-lg mb-4 last:mb-0 mt-6 first:mt-0"
3345
3403
  ),
3346
- style: headerSerifStyle
3404
+ style: headerFontStyle
3347
3405
  },
3348
3406
  children
3349
3407
  );
@@ -3361,7 +3419,7 @@ const Heading6 = ({
3361
3419
  className,
3362
3420
  "text-base mb-4 last:mb-0 mt-6 first:mt-0"
3363
3421
  ),
3364
- style: headerSerifStyle
3422
+ style: headerFontStyle
3365
3423
  },
3366
3424
  children
3367
3425
  );
@@ -4221,7 +4279,7 @@ const IconButton = ({
4221
4279
  function FontLoader() {
4222
4280
  React.useEffect(() => {
4223
4281
  const link = document.createElement("link");
4224
- link.href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Libre+Baskerville:wght@400;500;600;700&display=swap";
4282
+ link.href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap";
4225
4283
  link.rel = "stylesheet";
4226
4284
  document.head.appendChild(link);
4227
4285
  return () => {
@@ -4426,7 +4484,7 @@ const DropdownButton = React.forwardRef(
4426
4484
  children,
4427
4485
  className = "",
4428
4486
  onMainAction,
4429
- items: items2,
4487
+ items,
4430
4488
  showSplitButton = true,
4431
4489
  ...props
4432
4490
  }, ref) => {
@@ -4453,7 +4511,7 @@ const DropdownButton = React.forwardRef(
4453
4511
  )
4454
4512
  }
4455
4513
  )
4456
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, items2.map((item, index) => /* @__PURE__ */ React.createElement(
4514
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, items.map((item, index) => /* @__PURE__ */ React.createElement(
4457
4515
  DropdownMenuItem,
4458
4516
  {
4459
4517
  key: index,
@@ -4500,7 +4558,7 @@ const DropdownButton = React.forwardRef(
4500
4558
  style: { fill: "none" }
4501
4559
  }
4502
4560
  )
4503
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, items2.map((item, index) => {
4561
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, items.map((item, index) => {
4504
4562
  var _a;
4505
4563
  return /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, /* @__PURE__ */ React.createElement(
4506
4564
  DropdownMenuItem,
@@ -4511,7 +4569,7 @@ const DropdownButton = React.forwardRef(
4511
4569
  },
4512
4570
  item.icon && item.icon,
4513
4571
  item.label
4514
- ), item.variant === "destructive" && index < items2.length - 1 && ((_a = items2[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4572
+ ), item.variant === "destructive" && index < items.length - 1 && ((_a = items[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4515
4573
  }))));
4516
4574
  }
4517
4575
  );
@@ -4633,10 +4691,10 @@ const Draggable = ({
4633
4691
  ));
4634
4692
  };
4635
4693
  const SortableProvider = ({
4636
- items: items2,
4694
+ items,
4637
4695
  children
4638
4696
  }) => {
4639
- return /* @__PURE__ */ React__default.createElement(SortableContext, { items: items2, strategy: verticalListSortingStrategy }, children);
4697
+ return /* @__PURE__ */ React__default.createElement(SortableContext, { items, strategy: verticalListSortingStrategy }, children);
4640
4698
  };
4641
4699
  const Dismissible = ({
4642
4700
  onDismiss,
@@ -5590,6 +5648,26 @@ const absoluteImgURL = (str) => {
5590
5648
  return str;
5591
5649
  return `${window.location.origin}${str}`;
5592
5650
  };
5651
+ const MAX_BASENAME_LENGTH = 200;
5652
+ const sanitizeFilename = (filename) => {
5653
+ if (!filename)
5654
+ return "file";
5655
+ const normalized = filename.normalize("NFC");
5656
+ const justName = normalized.split(/[\\/]/).pop() || "";
5657
+ const lastDot = justName.lastIndexOf(".");
5658
+ const hasExt = lastDot > 0 && lastDot < justName.length - 1;
5659
+ const rawBase = hasExt ? justName.slice(0, lastDot) : justName;
5660
+ const rawExt = hasExt ? justName.slice(lastDot) : "";
5661
+ const clean2 = (input) => input.replace(/\s+/g, "-").replace(/[\x00-\x1F\x7F]/g, "").replace(/[<>:"|?*#%&]/g, "-").replace(/-+/g, "-");
5662
+ let base = clean2(rawBase).replace(/^[.\-]+|[.\-]+$/g, "");
5663
+ const ext = clean2(rawExt);
5664
+ if (!base)
5665
+ base = "file";
5666
+ if (base.length > MAX_BASENAME_LENGTH) {
5667
+ base = base.slice(0, MAX_BASENAME_LENGTH).replace(/[.\-]+$/, "") || "file";
5668
+ }
5669
+ return `${base}${ext}`;
5670
+ };
5593
5671
  const { useDropzone: useDropzone$1 } = dropzone;
5594
5672
  const StyledImage = ({ src }) => {
5595
5673
  const isSvg = /\.svg$/.test(src);
@@ -6198,7 +6276,7 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6198
6276
  }
6199
6277
  if (field.openFormOnCreate) {
6200
6278
  const state = tinaForm.finalForm.getState();
6201
- const newIndex = field.addItemBehavior === "prepend" ? 0 : items2.length;
6279
+ const newIndex = field.addItemBehavior === "prepend" ? 0 : items.length;
6202
6280
  if (state.invalid === true) {
6203
6281
  cms.alerts.error("Cannot navigate away from an invalid form.");
6204
6282
  return;
@@ -6216,7 +6294,7 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6216
6294
  });
6217
6295
  }
6218
6296
  }, [form, field]);
6219
- const items2 = input.value || [];
6297
+ const items = input.value || [];
6220
6298
  const itemProps = React__default.useCallback(
6221
6299
  (item) => {
6222
6300
  if (!field.itemProps)
@@ -6225,8 +6303,8 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6225
6303
  },
6226
6304
  [field.itemProps]
6227
6305
  );
6228
- const isMax = items2.length >= (field.max || Number.POSITIVE_INFINITY);
6229
- const isMin = items2.length <= (field.min || 0);
6306
+ const isMax = items.length >= (field.max || Number.POSITIVE_INFINITY);
6307
+ const isMin = items.length <= (field.min || 0);
6230
6308
  const fixedLength = field.min === field.max;
6231
6309
  return /* @__PURE__ */ React__default.createElement(
6232
6310
  ListFieldMeta,
@@ -6249,12 +6327,12 @@ const Group$1 = ({ tinaForm, form, field, input, meta, index }) => {
6249
6327
  /* @__PURE__ */ React__default.createElement(AddIcon, { className: "w-5/6 h-auto" })
6250
6328
  )
6251
6329
  },
6252
- /* @__PURE__ */ React__default.createElement(ListPanel, null, /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React__default.createElement("div", { ref: provider.innerRef }, items2.length === 0 && /* @__PURE__ */ React__default.createElement(EmptyList, null), /* @__PURE__ */ React__default.createElement(
6330
+ /* @__PURE__ */ React__default.createElement(ListPanel, null, /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React__default.createElement("div", { ref: provider.innerRef }, items.length === 0 && /* @__PURE__ */ React__default.createElement(EmptyList, null), /* @__PURE__ */ React__default.createElement(
6253
6331
  SortableProvider,
6254
6332
  {
6255
- items: items2.map((_, index2) => `${field.name}.${index2}`)
6333
+ items: items.map((_, index2) => `${field.name}.${index2}`)
6256
6334
  },
6257
- items2.map((item, index2) => /* @__PURE__ */ React__default.createElement(
6335
+ items.map((item, index2) => /* @__PURE__ */ React__default.createElement(
6258
6336
  Item$1,
6259
6337
  {
6260
6338
  key: index2,
@@ -6833,9 +6911,9 @@ const Blocks = ({
6833
6911
  },
6834
6912
  [field.name, form.mutators]
6835
6913
  );
6836
- const items2 = input.value || [];
6837
- const isMax = items2.length >= (field.max || Infinity);
6838
- const isMin = items2.length <= (field.min || 0);
6914
+ const items = input.value || [];
6915
+ const isMax = items.length >= (field.max || Infinity);
6916
+ const isMin = items.length <= (field.min || 0);
6839
6917
  const fixedLength = field.min === field.max;
6840
6918
  return /* @__PURE__ */ React.createElement(
6841
6919
  ListFieldMeta,
@@ -6857,12 +6935,12 @@ const Blocks = ({
6857
6935
  }
6858
6936
  ))
6859
6937
  },
6860
- /* @__PURE__ */ React.createElement(ListPanel, null, /* @__PURE__ */ React.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React.createElement("div", { ref: provider.innerRef, className: "edit-page--list-parent" }, items2.length === 0 && /* @__PURE__ */ React.createElement(EmptyList, null), /* @__PURE__ */ React.createElement(
6938
+ /* @__PURE__ */ React.createElement(ListPanel, null, /* @__PURE__ */ React.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React.createElement("div", { ref: provider.innerRef, className: "edit-page--list-parent" }, items.length === 0 && /* @__PURE__ */ React.createElement(EmptyList, null), /* @__PURE__ */ React.createElement(
6861
6939
  SortableProvider,
6862
6940
  {
6863
- items: items2.map((_, index2) => `${field.name}.${index2}`)
6941
+ items: items.map((_, index2) => `${field.name}.${index2}`)
6864
6942
  },
6865
- items2.map((block, index2) => {
6943
+ items.map((block, index2) => {
6866
6944
  const template = field.templates[block._template];
6867
6945
  if (!template) {
6868
6946
  return /* @__PURE__ */ React.createElement(
@@ -7014,7 +7092,7 @@ const List = ({ tinaForm, form, field, input, meta, index }) => {
7014
7092
  form.mutators.push(field.name, newItem);
7015
7093
  }
7016
7094
  }, [form, field]);
7017
- const items2 = input.value || [];
7095
+ const items = input.value || [];
7018
7096
  const itemProps = React.useCallback(
7019
7097
  (item) => {
7020
7098
  if (!field.itemProps)
@@ -7023,8 +7101,8 @@ const List = ({ tinaForm, form, field, input, meta, index }) => {
7023
7101
  },
7024
7102
  [field.itemProps]
7025
7103
  );
7026
- const isMax = items2.length >= (field == null ? void 0 : field.max);
7027
- const isMin = items2.length <= (field == null ? void 0 : field.min);
7104
+ const isMax = items.length >= (field == null ? void 0 : field.max);
7105
+ const isMin = items.length <= (field == null ? void 0 : field.min);
7028
7106
  const fixedLength = (field == null ? void 0 : field.min) === (field == null ? void 0 : field.max);
7029
7107
  return /* @__PURE__ */ React.createElement(
7030
7108
  ListFieldMeta,
@@ -7046,12 +7124,12 @@ const List = ({ tinaForm, form, field, input, meta, index }) => {
7046
7124
  /* @__PURE__ */ React.createElement(AddIcon, { className: "w-5/6 h-auto" })
7047
7125
  )
7048
7126
  },
7049
- /* @__PURE__ */ React.createElement(ListPanel, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React.createElement("div", { ref: provider.innerRef }, items2.length === 0 && /* @__PURE__ */ React.createElement(EmptyList, null), /* @__PURE__ */ React.createElement(
7127
+ /* @__PURE__ */ React.createElement(ListPanel, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Droppable, { droppableId: field.name, type: field.name }, (provider) => /* @__PURE__ */ React.createElement("div", { ref: provider.innerRef }, items.length === 0 && /* @__PURE__ */ React.createElement(EmptyList, null), /* @__PURE__ */ React.createElement(
7050
7128
  SortableProvider,
7051
7129
  {
7052
- items: items2.map((_, index2) => `${field.name}.${index2}`)
7130
+ items: items.map((_, index2) => `${field.name}.${index2}`)
7053
7131
  },
7054
- items2.map((item, index2) => /* @__PURE__ */ React.createElement(
7132
+ items.map((item, index2) => /* @__PURE__ */ React.createElement(
7055
7133
  Item,
7056
7134
  {
7057
7135
  key: index2,
@@ -7335,8 +7413,8 @@ const TextFieldPlugin = {
7335
7413
  const path = field.name.split(".");
7336
7414
  const fieldName = path[path.length - 1];
7337
7415
  const parent = path.slice(0, path.length - 2);
7338
- const items2 = get(allValues, parent);
7339
- if (((_a = items2 == null ? void 0 : items2.filter((item) => item[fieldName] === value)) == null ? void 0 : _a.length) > 1) {
7416
+ const items = get(allValues, parent);
7417
+ if (((_a = items == null ? void 0 : items.filter((item) => item[fieldName] === value)) == null ? void 0 : _a.length) > 1) {
7340
7418
  return "Item with this unique id already exists";
7341
7419
  }
7342
7420
  }
@@ -7369,7 +7447,7 @@ const TagsField = wrapFieldsWithMeta(({ input, field, form, tinaForm }) => {
7369
7447
  },
7370
7448
  [form, field.name]
7371
7449
  );
7372
- const items2 = input.value || [];
7450
+ const items = input.value || [];
7373
7451
  const ref = React.useRef(null);
7374
7452
  React.useEffect(() => {
7375
7453
  if (ref.current && field.experimental_focusIntent) {
@@ -7402,7 +7480,7 @@ const TagsField = wrapFieldsWithMeta(({ input, field, form, tinaForm }) => {
7402
7480
  className: "flex-shrink-0"
7403
7481
  },
7404
7482
  /* @__PURE__ */ React.createElement(AddIcon, { className: "w-5/6 h-auto" })
7405
- )), /* @__PURE__ */ React.createElement("span", { className: "flex gap-2 flex-wrap mt-2 mb-0" }, items2.length === 0 && /* @__PURE__ */ React.createElement("span", { className: "text-gray-300 text-sm italic" }, "No tags"), items2.map((tag, index) => /* @__PURE__ */ React.createElement(Tag, { key: tag, tinaForm, field, index }, tag))));
7483
+ )), /* @__PURE__ */ React.createElement("span", { className: "flex gap-2 flex-wrap mt-2 mb-0" }, items.length === 0 && /* @__PURE__ */ React.createElement("span", { className: "text-gray-300 text-sm italic" }, "No tags"), items.map((tag, index) => /* @__PURE__ */ React.createElement(Tag, { key: tag, tinaForm, field, index }, tag))));
7406
7484
  });
7407
7485
  const Tag = ({ tinaForm, field, index, children, ...styleProps }) => {
7408
7486
  const removeItem = React.useCallback(() => {
@@ -9975,17 +10053,20 @@ class DummyMediaStore {
9975
10053
  __publicField(this, "accept", "*");
9976
10054
  }
9977
10055
  async persist(files2) {
9978
- return files2.map(({ directory, file }) => ({
9979
- id: file.name,
9980
- type: "file",
9981
- directory,
9982
- filename: file.name
9983
- }));
10056
+ return files2.map(({ directory, file }) => {
10057
+ const filename = sanitizeFilename(file.name);
10058
+ return {
10059
+ id: filename,
10060
+ type: "file",
10061
+ directory,
10062
+ filename
10063
+ };
10064
+ });
9984
10065
  }
9985
10066
  async list() {
9986
- const items2 = [];
10067
+ const items = [];
9987
10068
  return {
9988
- items: items2,
10069
+ items,
9989
10070
  nextOffset: 0
9990
10071
  };
9991
10072
  }
@@ -10074,7 +10155,8 @@ class TinaMediaStore {
10074
10155
  if (directory == null ? void 0 : directory.endsWith("/")) {
10075
10156
  directory = directory.substr(0, directory.length - 1);
10076
10157
  }
10077
- const path = `${directory && directory !== "/" ? `${directory}/${item.file.name}` : item.file.name}`;
10158
+ const safeName = sanitizeFilename(item.file.name);
10159
+ const path = `${directory && directory !== "/" ? `${directory}/${safeName}` : safeName}`;
10078
10160
  const res = await this.api.authProvider.fetchWithToken(
10079
10161
  `${this.url}/upload_url/${path}${branchQuery}`,
10080
10162
  { method: "GET" }
@@ -10151,12 +10233,12 @@ class TinaMediaStore {
10151
10233
  { w: 1e3, h: 1e3 }
10152
10234
  ];
10153
10235
  const results = [];
10154
- for (const [directory, items2] of byDirectory) {
10236
+ for (const [directory, items] of byDirectory) {
10155
10237
  let listed;
10156
10238
  try {
10157
10239
  listed = await this.list({
10158
10240
  directory,
10159
- limit: Math.max(100, items2.length * 4),
10241
+ limit: Math.max(100, items.length * 4),
10160
10242
  thumbnailSizes
10161
10243
  });
10162
10244
  } catch (err) {
@@ -10169,8 +10251,8 @@ class TinaMediaStore {
10169
10251
  found.set(entry.filename, entry);
10170
10252
  }
10171
10253
  }
10172
- for (const item of items2) {
10173
- const entry = found.get(item.file.name);
10254
+ for (const item of items) {
10255
+ const entry = found.get(sanitizeFilename(item.file.name));
10174
10256
  if (entry)
10175
10257
  results.push(entry);
10176
10258
  }
@@ -10194,6 +10276,7 @@ class TinaMediaStore {
10194
10276
  }
10195
10277
  for (const item of media) {
10196
10278
  const { file, directory } = item;
10279
+ const safeName = sanitizeFilename(file.name);
10197
10280
  let strippedDirectory = directory;
10198
10281
  if (strippedDirectory.startsWith("/")) {
10199
10282
  strippedDirectory = strippedDirectory.substr(1) || "";
@@ -10202,14 +10285,14 @@ class TinaMediaStore {
10202
10285
  strippedDirectory = strippedDirectory.substr(0, strippedDirectory.length - 1) || "";
10203
10286
  }
10204
10287
  const formData = new FormData();
10205
- formData.append("file", file);
10288
+ formData.append("file", file, safeName);
10206
10289
  formData.append("directory", directory);
10207
- formData.append("filename", file.name);
10208
- let uploadPath = `${strippedDirectory ? `${strippedDirectory}/${file.name}` : file.name}`;
10290
+ formData.append("filename", safeName);
10291
+ let uploadPath = `${strippedDirectory ? `${strippedDirectory}/${safeName}` : safeName}`;
10209
10292
  if (uploadPath.startsWith("/")) {
10210
10293
  uploadPath = uploadPath.substr(1);
10211
10294
  }
10212
- const filePath = `${strippedDirectory ? `${folder}${strippedDirectory}/${file.name}` : folder + file.name}`;
10295
+ const filePath = `${strippedDirectory ? `${folder}${strippedDirectory}/${safeName}` : folder + safeName}`;
10213
10296
  const res = await this.fetchFunction(`${this.url}/upload/${uploadPath}`, {
10214
10297
  method: "POST",
10215
10298
  body: formData
@@ -10222,8 +10305,8 @@ class TinaMediaStore {
10222
10305
  if (fileRes == null ? void 0 : fileRes.success) {
10223
10306
  const parsedRes = {
10224
10307
  type: "file",
10225
- id: file.name,
10226
- filename: file.name,
10308
+ id: safeName,
10309
+ filename: safeName,
10227
10310
  directory,
10228
10311
  src: filePath,
10229
10312
  thumbnails: {
@@ -10311,9 +10394,9 @@ class TinaMediaStore {
10311
10394
  }
10312
10395
  }
10313
10396
  const { cursor, files: files2, directories } = await res.json();
10314
- const items2 = [];
10397
+ const items = [];
10315
10398
  for (const dir of directories) {
10316
- items2.push({
10399
+ items.push({
10317
10400
  type: "dir",
10318
10401
  id: dir,
10319
10402
  directory: options.directory || "",
@@ -10321,7 +10404,7 @@ class TinaMediaStore {
10321
10404
  });
10322
10405
  }
10323
10406
  for (const file of files2) {
10324
- items2.push({
10407
+ items.push({
10325
10408
  directory: options.directory || "",
10326
10409
  type: "file",
10327
10410
  id: file.filename,
@@ -10334,7 +10417,7 @@ class TinaMediaStore {
10334
10417
  });
10335
10418
  }
10336
10419
  return {
10337
- items: items2,
10420
+ items,
10338
10421
  nextOffset: cursor || 0
10339
10422
  };
10340
10423
  }
@@ -12011,14 +12094,16 @@ const MediaLightbox = ({
12011
12094
  item,
12012
12095
  onClose
12013
12096
  }) => {
12097
+ useEffect(() => {
12098
+ if (!item)
12099
+ return;
12100
+ captureEvent(MediaUsageDashboardPreviewOpenedEvent);
12101
+ }, [item]);
12014
12102
  if (!item)
12015
12103
  return null;
12016
12104
  const usageCount = item.usedIn.length;
12017
12105
  const mediaSrc = item.media.src;
12018
12106
  const directory = item.media.directory || "/";
12019
- useEffect(() => {
12020
- captureEvent(MediaUsageDashboardPreviewOpenedEvent);
12021
- }, []);
12022
12107
  return /* @__PURE__ */ React__default.createElement(Dialog, { open: true, onOpenChange: (isOpen) => !isOpen && onClose() }, /* @__PURE__ */ React__default.createElement(DialogContent, { className: "w-auto max-w-[95vw] border border-gray-200 bg-white px-4 pt-12 pb-4 shadow-xl sm:max-w-fit" }, /* @__PURE__ */ React__default.createElement(DialogTitle, { className: "sr-only" }, "Preview: ", item.media.filename), /* @__PURE__ */ React__default.createElement(DialogDescription, { className: "sr-only" }, usageCount > 0 ? `Used in ${usageCount} ${usageCount === 1 ? "document" : "documents"}` : "Unused media file"), /* @__PURE__ */ React__default.createElement("div", { className: "mx-auto w-fit max-w-full rounded-lg border border-gray-200 bg-gray-50 p-2 sm:p-3" }, item.type === "video" ? /* @__PURE__ */ React__default.createElement(
12023
12108
  VideoLightboxContent,
12024
12109
  {
@@ -12780,7 +12865,7 @@ const NavProvider = ({
12780
12865
  const name = "tinacms";
12781
12866
  const type = "module";
12782
12867
  const typings = "dist/index.d.ts";
12783
- const version$1 = "3.8.2";
12868
+ const version$1 = "3.8.4";
12784
12869
  const main = "dist/index.js";
12785
12870
  const module = "./dist/index.js";
12786
12871
  const exports = {
@@ -13511,7 +13596,7 @@ function BreadcrumbEllipsis({
13511
13596
  );
13512
13597
  }
13513
13598
  const getPaddingClass = (depth) => `${1.5 + depth * 1.35}rem`;
13514
- const collectAllDocumentItems = (items2, isGlobalFn) => {
13599
+ const collectAllDocumentItems = (items, isGlobalFn) => {
13515
13600
  const allItems = [];
13516
13601
  const processItem = (item, isFromSubItems = false) => {
13517
13602
  if (item.type === "document") {
@@ -13526,13 +13611,13 @@ const collectAllDocumentItems = (items2, isGlobalFn) => {
13526
13611
  }
13527
13612
  }
13528
13613
  };
13529
- items2.forEach((item) => processItem(item, false));
13614
+ items.forEach((item) => processItem(item, false));
13530
13615
  return allItems;
13531
13616
  };
13532
- const buildTreeFromPaths = (items2) => {
13617
+ const buildTreeFromPaths = (items) => {
13533
13618
  const root = [];
13534
13619
  const nodeMap = /* @__PURE__ */ new Map();
13535
- items2.forEach((item) => {
13620
+ items.forEach((item) => {
13536
13621
  const parts = item.formId.split("/").filter(Boolean);
13537
13622
  let currentLevel = root;
13538
13623
  let currentPath = "";
@@ -17004,43 +17089,15 @@ const formatList = (editor, elementType) => {
17004
17089
  })
17005
17090
  );
17006
17091
  };
17007
- const autoformatBlocks = [
17008
- {
17009
- mode: "block",
17010
- type: HEADING_KEYS.h1,
17011
- match: "# ",
17012
- preFormat
17013
- },
17014
- {
17015
- mode: "block",
17016
- type: HEADING_KEYS.h2,
17017
- match: "## ",
17018
- preFormat
17019
- },
17020
- {
17021
- mode: "block",
17022
- type: HEADING_KEYS.h3,
17023
- match: "### ",
17024
- preFormat
17025
- },
17026
- {
17027
- mode: "block",
17028
- type: HEADING_KEYS.h4,
17029
- match: "#### ",
17030
- preFormat
17031
- },
17032
- {
17033
- mode: "block",
17034
- type: HEADING_KEYS.h5,
17035
- match: "##### ",
17036
- preFormat
17037
- },
17038
- {
17039
- mode: "block",
17040
- type: HEADING_KEYS.h6,
17041
- match: "###### ",
17042
- preFormat
17043
- },
17092
+ const headingAutoformatByLevel = {
17093
+ h1: { mode: "block", type: HEADING_KEYS.h1, match: "# ", preFormat },
17094
+ h2: { mode: "block", type: HEADING_KEYS.h2, match: "## ", preFormat },
17095
+ h3: { mode: "block", type: HEADING_KEYS.h3, match: "### ", preFormat },
17096
+ h4: { mode: "block", type: HEADING_KEYS.h4, match: "#### ", preFormat },
17097
+ h5: { mode: "block", type: HEADING_KEYS.h5, match: "##### ", preFormat },
17098
+ h6: { mode: "block", type: HEADING_KEYS.h6, match: "###### ", preFormat }
17099
+ };
17100
+ const nonHeadingAutoformatBlocks = [
17044
17101
  {
17045
17102
  mode: "block",
17046
17103
  type: BlockquotePlugin.key,
@@ -17072,6 +17129,16 @@ const autoformatBlocks = [
17072
17129
  }
17073
17130
  }
17074
17131
  ];
17132
+ const getAutoformatBlocks = (headingLevels = ALL_HEADING_LEVELS) => [
17133
+ // Normalize so a pure-JS schema passing e.g. `['h7']` doesn't push an
17134
+ // `undefined` rule into the autoformat config. The toolbar context
17135
+ // applies the same filter, keeping both surfaces consistent.
17136
+ ...normalizeHeadingLevels(headingLevels).map(
17137
+ (level) => headingAutoformatByLevel[level]
17138
+ ),
17139
+ ...nonHeadingAutoformatBlocks
17140
+ ];
17141
+ const autoformatBlocks = getAutoformatBlocks();
17075
17142
  const autoformatLists = [
17076
17143
  {
17077
17144
  mode: "block",
@@ -17449,70 +17516,36 @@ const HEADING_ICON_ONLY = 58;
17449
17516
  const EMBED_ICON_WIDTH = 78;
17450
17517
  const CONTAINER_MD_BREAKPOINT = 448;
17451
17518
  const HEADING_LABEL = "Headings";
17452
- const ToolbarContext = createContext(
17453
- void 0
17454
- );
17455
- const ToolbarProvider = ({
17456
- tinaForm,
17457
- templates,
17458
- overrides,
17459
- children
17460
- }) => {
17461
- return /* @__PURE__ */ React__default.createElement(ToolbarContext.Provider, { value: { tinaForm, templates, overrides } }, children);
17462
- };
17463
- const useToolbarContext = () => {
17464
- const context = useContext(ToolbarContext);
17465
- if (!context) {
17466
- throw new Error("useToolbarContext must be used within a ToolbarProvider");
17467
- }
17468
- return context;
17519
+ const buildByLevel = (make) => ({
17520
+ h1: make("h1"),
17521
+ h2: make("h2"),
17522
+ h3: make("h3"),
17523
+ h4: make("h4"),
17524
+ h5: make("h5"),
17525
+ h6: make("h6")
17526
+ });
17527
+ const headingItemsByLevel = buildByLevel((level) => {
17528
+ const depth = level.slice(1);
17529
+ return {
17530
+ description: `Heading ${depth}`,
17531
+ icon: Icons[level],
17532
+ label: `Heading ${depth}`,
17533
+ value: level
17534
+ };
17535
+ });
17536
+ const paragraphItem = {
17537
+ description: "Paragraph",
17538
+ icon: Icons.paragraph,
17539
+ label: "Paragraph",
17540
+ value: ParagraphPlugin.key
17469
17541
  };
17470
- const items$1 = [
17471
- {
17472
- description: "Paragraph",
17473
- icon: Icons.heading,
17474
- label: "Paragraph",
17475
- value: ParagraphPlugin.key
17476
- },
17477
- {
17478
- description: "Heading 1",
17479
- icon: Icons.h1,
17480
- label: "Heading 1",
17481
- value: HEADING_KEYS.h1
17482
- },
17483
- {
17484
- description: "Heading 2",
17485
- icon: Icons.h2,
17486
- label: "Heading 2",
17487
- value: HEADING_KEYS.h2
17488
- },
17489
- {
17490
- description: "Heading 3",
17491
- icon: Icons.h3,
17492
- label: "Heading 3",
17493
- value: HEADING_KEYS.h3
17494
- },
17495
- {
17496
- description: "Heading 4",
17497
- icon: Icons.h4,
17498
- label: "Heading 4",
17499
- value: HEADING_KEYS.h4
17500
- },
17501
- {
17502
- description: "Heading 5",
17503
- icon: Icons.h5,
17504
- label: "Heading 5",
17505
- value: HEADING_KEYS.h5
17506
- },
17507
- {
17508
- description: "Heading 6",
17509
- icon: Icons.h6,
17510
- label: "Heading 6",
17511
- value: HEADING_KEYS.h6
17512
- }
17513
- ];
17514
- const defaultItem$1 = items$1.find((item) => item.value === ParagraphPlugin.key) || items$1[0];
17542
+ const getHeadingItem = (value) => isHeadingLevel(value) ? headingItemsByLevel[value] : void 0;
17515
17543
  function HeadingsMenu(props) {
17544
+ const { headingLevels } = useToolbarContext();
17545
+ const items = [
17546
+ paragraphItem,
17547
+ ...headingLevels.map((level) => headingItemsByLevel[level])
17548
+ ];
17516
17549
  const value = useEditorSelector((editor2) => {
17517
17550
  let initialNodeType = ParagraphPlugin.key;
17518
17551
  let allNodesMatchInitialNodeType = false;
@@ -17534,7 +17567,7 @@ function HeadingsMenu(props) {
17534
17567
  const editorState = useEditorState();
17535
17568
  const openState = useOpenState();
17536
17569
  const userInTable = helpers.isNodeActive(editorState, TablePlugin.key);
17537
- const selectedItem = items$1.find((item) => item.value === value) ?? defaultItem$1;
17570
+ const selectedItem = getHeadingItem(value) ?? paragraphItem;
17538
17571
  const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;
17539
17572
  return /* @__PURE__ */ React__default.createElement("div", { className: "rounded-md" }, /* @__PURE__ */ React__default.createElement(DropdownMenu$1, { modal: false, ...openState, ...props }, /* @__PURE__ */ React__default.createElement(DropdownMenuTrigger$1, { asChild: true }, /* @__PURE__ */ React__default.createElement(
17540
17573
  ToolbarButton,
@@ -17563,7 +17596,7 @@ function HeadingsMenu(props) {
17563
17596
  },
17564
17597
  value
17565
17598
  },
17566
- items$1.filter((item) => {
17599
+ items.filter((item) => {
17567
17600
  if (userInTable) {
17568
17601
  return !unsupportedItemsInTable.has(item.label);
17569
17602
  }
@@ -18239,27 +18272,27 @@ function FixedToolbarButtons() {
18239
18272
  const [itemsShown, setItemsShown] = React__default.useState(11);
18240
18273
  const { overrides, templates } = useToolbarContext();
18241
18274
  const showEmbedButton = templates.length > 0;
18242
- let items2 = [];
18275
+ let items = [];
18243
18276
  if (Array.isArray(overrides)) {
18244
- items2 = overrides === void 0 ? Object.values(toolbarItems) : overrides.map((item) => toolbarItems[item]).filter((item) => item !== void 0);
18277
+ items = overrides === void 0 ? Object.values(toolbarItems) : overrides.map((item) => toolbarItems[item]).filter((item) => item !== void 0);
18245
18278
  } else {
18246
- items2 = (overrides == null ? void 0 : overrides.toolbar) === void 0 ? Object.values(toolbarItems) : overrides.toolbar.map((item) => toolbarItems[item]).filter((item) => item !== void 0);
18279
+ items = (overrides == null ? void 0 : overrides.toolbar) === void 0 ? Object.values(toolbarItems) : overrides.toolbar.map((item) => toolbarItems[item]).filter((item) => item !== void 0);
18247
18280
  }
18248
18281
  if (!showEmbedButton) {
18249
- items2 = items2.filter((item) => item.label !== toolbarItems.embed.label);
18282
+ items = items.filter((item) => item.label !== toolbarItems.embed.label);
18250
18283
  }
18251
18284
  const editorState = useEditorState();
18252
18285
  const userInTable = helpers.isNodeActive(editorState, TablePlugin);
18253
18286
  const userInCodeBlock = helpers.isNodeActive(editorState, CodeBlockPlugin);
18254
18287
  useResize(toolbarRef, (entry) => {
18255
18288
  const width = entry.target.getBoundingClientRect().width - 8;
18256
- const headingButton = items2.find((item) => item.label === HEADING_LABEL);
18289
+ const headingButton = items.find((item) => item.label === HEADING_LABEL);
18257
18290
  const headingWidth = headingButton ? (
18258
18291
  //some discrepancy here between the md breakpoint here and in practice, but it works
18259
18292
  headingButton.width(width > CONTAINER_MD_BREAKPOINT - 9)
18260
18293
  ) : 0;
18261
18294
  const availableWidth = width - headingWidth;
18262
- const { itemFitCount } = items2.reduce(
18295
+ const { itemFitCount } = items.reduce(
18263
18296
  (acc, item) => {
18264
18297
  if (item.label !== HEADING_LABEL && acc.totalItemsWidth + item.width() <= availableWidth) {
18265
18298
  return {
@@ -18291,7 +18324,7 @@ function FixedToolbarButtons() {
18291
18324
  transform: "translateX(calc(-1px))"
18292
18325
  }
18293
18326
  },
18294
- /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, items2.slice(0, items2.length > itemsShown ? itemsShown - 1 : itemsShown).map((item) => /* @__PURE__ */ React__default.createElement(
18327
+ /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, items.slice(0, items.length > itemsShown ? itemsShown - 1 : itemsShown).map((item) => /* @__PURE__ */ React__default.createElement(
18295
18328
  "div",
18296
18329
  {
18297
18330
  className: cn(
@@ -18301,7 +18334,7 @@ function FixedToolbarButtons() {
18301
18334
  key: item.label
18302
18335
  },
18303
18336
  item.Component
18304
- )), items2.length > itemsShown && /* @__PURE__ */ React__default.createElement("div", { className: "w-fit ml-auto" }, /* @__PURE__ */ React__default.createElement(OverflowMenu, null, items2.slice(itemsShown - 1).flatMap((c2) => /* @__PURE__ */ React__default.createElement(
18337
+ )), items.length > itemsShown && /* @__PURE__ */ React__default.createElement("div", { className: "w-fit ml-auto" }, /* @__PURE__ */ React__default.createElement(OverflowMenu, null, items.slice(itemsShown - 1).flatMap((c2) => /* @__PURE__ */ React__default.createElement(
18305
18338
  "div",
18306
18339
  {
18307
18340
  className: cn(
@@ -18789,52 +18822,12 @@ const FloatingToolbar = withRef(({ children, state, ...props }, propRef) => {
18789
18822
  children
18790
18823
  ));
18791
18824
  });
18792
- const items = [
18793
- {
18794
- description: "Paragraph",
18795
- icon: Icons.paragraph,
18796
- label: "Paragraph",
18797
- value: ParagraphPlugin.key
18798
- },
18799
- {
18800
- description: "Heading 1",
18801
- icon: Icons.h1,
18802
- label: "Heading 1",
18803
- value: HEADING_KEYS.h1
18804
- },
18805
- {
18806
- description: "Heading 2",
18807
- icon: Icons.h2,
18808
- label: "Heading 2",
18809
- value: HEADING_KEYS.h2
18810
- },
18811
- {
18812
- description: "Heading 3",
18813
- icon: Icons.h3,
18814
- label: "Heading 3",
18815
- value: HEADING_KEYS.h3
18816
- },
18817
- {
18818
- description: "Heading 4",
18819
- icon: Icons.h4,
18820
- label: "Heading 4",
18821
- value: HEADING_KEYS.h4
18822
- },
18823
- {
18824
- description: "Heading 5",
18825
- icon: Icons.h5,
18826
- label: "Heading 5",
18827
- value: HEADING_KEYS.h5
18828
- },
18829
- {
18830
- description: "Heading 6",
18831
- icon: Icons.h6,
18832
- label: "Heading 6",
18833
- value: HEADING_KEYS.h6
18834
- }
18835
- ];
18836
- const defaultItem = items.find((item) => item.value === ParagraphPlugin.key);
18837
18825
  function TurnIntoDropdownMenu(props) {
18826
+ const { headingLevels } = useToolbarContext();
18827
+ const items = [
18828
+ paragraphItem,
18829
+ ...headingLevels.map((level) => headingItemsByLevel[level])
18830
+ ];
18838
18831
  const value = useEditorSelector((editor2) => {
18839
18832
  let initialNodeType = ParagraphPlugin.key;
18840
18833
  let allNodesMatchInitialNodeType = false;
@@ -18854,7 +18847,7 @@ function TurnIntoDropdownMenu(props) {
18854
18847
  }, []);
18855
18848
  const editor = useEditorRef();
18856
18849
  const openState = useOpenState();
18857
- const selectedItem = items.find((item) => item.value === value) ?? defaultItem;
18850
+ const selectedItem = getHeadingItem(value) ?? paragraphItem;
18858
18851
  const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;
18859
18852
  const editorState = useEditorState();
18860
18853
  const userInTable = helpers.isNodeActive(editorState, TablePlugin.key);
@@ -61927,9 +61920,9 @@ function php(hljs) {
61927
61920
  "static",
61928
61921
  "stdClass"
61929
61922
  ];
61930
- const dualCase = (items2) => {
61923
+ const dualCase = (items) => {
61931
61924
  const result = [];
61932
- items2.forEach((item) => {
61925
+ items.forEach((item) => {
61933
61926
  result.push(item);
61934
61927
  if (item.toLowerCase() === item) {
61935
61928
  result.push(item.toUpperCase());
@@ -61944,8 +61937,8 @@ function php(hljs) {
61944
61937
  literal: dualCase(LITERALS2),
61945
61938
  built_in: BUILT_INS2
61946
61939
  };
61947
- const normalizeKeywords = (items2) => {
61948
- return items2.map((item) => {
61940
+ const normalizeKeywords = (items) => {
61941
+ return items.map((item) => {
61949
61942
  return item.replace(/\|\d+$/, "");
61950
61943
  });
61951
61944
  };
@@ -69713,7 +69706,9 @@ const ClearHighlightOnEnterPlugin = createSlatePlugin({
69713
69706
  }
69714
69707
  }
69715
69708
  }));
69716
- const editorPlugins = [
69709
+ const createEditorPlugins = ({
69710
+ headingLevels
69711
+ } = {}) => [
69717
69712
  createMdxBlockPlugin,
69718
69713
  createMdxInlinePlugin,
69719
69714
  createImgPlugin,
@@ -69737,9 +69732,9 @@ const editorPlugins = [
69737
69732
  NodeIdPlugin,
69738
69733
  TablePlugin,
69739
69734
  SlashPlugin,
69740
- // This lets users keep typing after end of marks like headings or quotes
69735
+ // Keeps a blank paragraph at the end of the editor so users can keep
69736
+ // typing after a heading, quote, or other terminal block.
69741
69737
  TrailingBlockPlugin,
69742
- //makes sure there's always a blank paragraph at the end of the editor.
69743
69738
  createBreakPlugin,
69744
69739
  FloatingToolbarPlugin,
69745
69740
  AutoformatPlugin.configure({
@@ -69747,7 +69742,7 @@ const editorPlugins = [
69747
69742
  enableUndoOnDelete: true,
69748
69743
  rules: [
69749
69744
  ...autoformatMarks,
69750
- ...autoformatBlocks,
69745
+ ...getAutoformatBlocks(headingLevels),
69751
69746
  ...autoformatLists,
69752
69747
  ...autoformatSmartQuotes,
69753
69748
  ...autoformatPunctuation,
@@ -69757,6 +69752,7 @@ const editorPlugins = [
69757
69752
  ].map(
69758
69753
  (rule) => ({
69759
69754
  ...rule,
69755
+ // Suppress autoformat inside code blocks so e.g. `# ` stays literal.
69760
69756
  query: (editor) => !editor.api.some({
69761
69757
  match: { type: editor.getType(CodeBlockPlugin) }
69762
69758
  })
@@ -69764,29 +69760,21 @@ const editorPlugins = [
69764
69760
  )
69765
69761
  }
69766
69762
  }),
69767
- // ExitBreakPlugin lets users "break out" of a block (like a heading)
69763
+ // Lets users "break out" of a block (e.g. a heading) with Enter.
69768
69764
  ExitBreakPlugin.configure({
69769
69765
  options: {
69770
69766
  rules: [
69771
- {
69772
- hotkey: "mod+enter"
69773
- },
69774
- {
69775
- hotkey: "mod+shift+enter",
69776
- before: true
69777
- },
69767
+ { hotkey: "mod+enter" },
69768
+ { hotkey: "mod+shift+enter", before: true },
69778
69769
  {
69779
69770
  hotkey: "enter",
69780
- query: {
69781
- start: true,
69782
- end: true,
69783
- allow: HEADING_LEVELS
69784
- }
69771
+ query: { start: true, end: true, allow: HEADING_LEVELS }
69785
69772
  }
69786
69773
  ]
69787
69774
  }
69788
69775
  }),
69789
- // ResetNodePlugin lets users turn a heading back into a paragraph by pressing Enter (when empty) or Backspace (at the start).
69776
+ // Lets users turn a heading back into a paragraph by pressing Enter on
69777
+ // an empty heading or Backspace at the start of one.
69790
69778
  ResetNodePlugin.configure({
69791
69779
  options: {
69792
69780
  rules: [
@@ -69798,9 +69786,7 @@ const editorPlugins = [
69798
69786
  {
69799
69787
  ...resetBlockTypesCommonRule,
69800
69788
  hotkey: "Backspace",
69801
- predicate: (editor) => {
69802
- return editor.api.isAt({ start: true });
69803
- }
69789
+ predicate: (editor) => editor.api.isAt({ start: true })
69804
69790
  },
69805
69791
  {
69806
69792
  ...resetBlockTypesCodeBlockRule,
@@ -69812,10 +69798,11 @@ const editorPlugins = [
69812
69798
  hotkey: "Backspace",
69813
69799
  predicate: isSelectionAtCodeBlockStart
69814
69800
  },
69815
- // NOTE: Plate's ListPlugin usually handles resetting lists to paragraphs when pressing Backspace at the start of a list item.
69816
- // However, if the list is the first node in the editor, the default reset behavior may not fully unwrap the list item,
69817
- // which can leave an invalid structure (like a <li> inside a <p>).
69818
- // This rule uses `onReset: unwrapList` to ensure lists are always properly reset to paragraphs, even when they are the first node.
69801
+ // Plate's ListPlugin usually resets lists to paragraphs on Backspace
69802
+ // at the start of a list item, but when the list is the editor's
69803
+ // first node the default doesn't fully unwrap and we end up with an
69804
+ // invalid structure (e.g. <li> inside <p>). `onReset: unwrapList`
69805
+ // forces a clean reset in that case.
69819
69806
  {
69820
69807
  types: [BulletedListPlugin.key, NumberedListPlugin.key],
69821
69808
  defaultType: ParagraphPlugin.key,
@@ -69832,21 +69819,19 @@ const editorPlugins = [
69832
69819
  { hotkey: "shift+enter" },
69833
69820
  {
69834
69821
  hotkey: "enter",
69835
- query: {
69836
- allow: [CodeBlockPlugin.key, BlockquotePlugin.key]
69837
- }
69822
+ query: { allow: [CodeBlockPlugin.key, BlockquotePlugin.key] }
69838
69823
  }
69839
69824
  ]
69840
69825
  }
69841
69826
  })
69842
69827
  ];
69843
69828
  const RichEditor = ({ input, tinaForm, field }) => {
69844
- var _a;
69829
+ var _a, _b;
69845
69830
  const initialValue = React__default.useMemo(() => {
69846
- var _a2, _b, _c;
69831
+ var _a2, _b2, _c;
69847
69832
  if (((_a2 = field == null ? void 0 : field.parser) == null ? void 0 : _a2.type) === "slatejson") {
69848
69833
  return input.value.children;
69849
- } else if ((_c = (_b = input.value) == null ? void 0 : _b.children) == null ? void 0 : _c.length) {
69834
+ } else if ((_c = (_b2 = input.value) == null ? void 0 : _b2.children) == null ? void 0 : _c.length) {
69850
69835
  const normalized = input.value.children.map(helpers.normalize);
69851
69836
  return normalized;
69852
69837
  } else {
@@ -69854,7 +69839,16 @@ const RichEditor = ({ input, tinaForm, field }) => {
69854
69839
  }
69855
69840
  }, []);
69856
69841
  const showFloatingToolbar = ((_a = field == null ? void 0 : field.overrides) == null ? void 0 : _a.showFloatingToolbar) !== false;
69857
- const plugins = showFloatingToolbar ? editorPlugins : editorPlugins.filter((plugin) => plugin.key !== "floating-toolbar");
69842
+ const builtPlugins = React__default.useMemo(
69843
+ () => {
69844
+ var _a2;
69845
+ return createEditorPlugins({
69846
+ headingLevels: (_a2 = field == null ? void 0 : field.overrides) == null ? void 0 : _a2.headingLevels
69847
+ });
69848
+ },
69849
+ [(_b = field == null ? void 0 : field.overrides) == null ? void 0 : _b.headingLevels]
69850
+ );
69851
+ const plugins = showFloatingToolbar ? builtPlugins : builtPlugins.filter((plugin) => plugin.key !== "floating-toolbar");
69858
69852
  const editor = useCreateEditor({
69859
69853
  plugins: [...plugins],
69860
69854
  value: initialValue,
@@ -69896,8 +69890,9 @@ const RichEditor = ({ input, tinaForm, field }) => {
69896
69890
  templates: field.templates,
69897
69891
  overrides: (field == null ? void 0 : field.toolbarOverride) ? field.toolbarOverride : field.overrides
69898
69892
  },
69899
- /* @__PURE__ */ React__default.createElement(FixedToolbar, null, /* @__PURE__ */ React__default.createElement(FixedToolbarButtons, null))
69900
- ), /* @__PURE__ */ React__default.createElement(Editor$1, null)))
69893
+ /* @__PURE__ */ React__default.createElement(FixedToolbar, null, /* @__PURE__ */ React__default.createElement(FixedToolbarButtons, null)),
69894
+ /* @__PURE__ */ React__default.createElement(Editor$1, null)
69895
+ )))
69901
69896
  ));
69902
69897
  };
69903
69898
  const MdxFieldPlugin = {
@@ -73574,7 +73569,7 @@ const RenderForm$1 = ({
73574
73569
  };
73575
73570
  }
73576
73571
  }
73577
- const defaultItem2 = customDefaults || // @ts-ignore internal types aren't up to date
73572
+ const defaultItem = customDefaults || // @ts-ignore internal types aren't up to date
73578
73573
  ((_d = template.ui) == null ? void 0 : _d.defaultItem) || // @ts-ignore
73579
73574
  (template == null ? void 0 : template.defaultItem) || {};
73580
73575
  const fileReadOnly = (_f = (_e = schemaCollection == null ? void 0 : schemaCollection.ui) == null ? void 0 : _e.filename) == null ? void 0 : _f.readonly;
@@ -73630,7 +73625,7 @@ const RenderForm$1 = ({
73630
73625
  const folderName = folder.fullyQualifiedName ? folder.name : "";
73631
73626
  return new Form({
73632
73627
  crudType: "create",
73633
- initialValues: typeof defaultItem2 === "function" ? { ...defaultItem2(), _template: templateName } : { ...defaultItem2, _template: templateName },
73628
+ initialValues: typeof defaultItem === "function" ? { ...defaultItem(), _template: templateName } : { ...defaultItem, _template: templateName },
73634
73629
  extraSubscribeValues: { active: true, submitting: true, touched: true },
73635
73630
  onChange: (values) => {
73636
73631
  var _a3, _b3;
@@ -74639,6 +74634,7 @@ export {
74639
74634
  passwordFieldClasses,
74640
74635
  resolveField,
74641
74636
  safeAssertShape,
74637
+ sanitizeFilename,
74642
74638
  selectFieldClasses,
74643
74639
  sortBranchListFn,
74644
74640
  staticRequest,