@dyrected/admin 2.5.56 → 2.5.58

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.
@@ -1,3 +1,4 @@
1
+ import { HeadingLevel, RichTextFeature } from '@dyrected/core';
1
2
  import * as React from "react";
2
3
  interface RichTextEditorProps {
3
4
  value: string;
@@ -5,6 +6,10 @@ interface RichTextEditorProps {
5
6
  label?: string;
6
7
  disabled?: boolean;
7
8
  collection?: string;
9
+ /** Enabled editor capabilities. When omitted, every feature is enabled. */
10
+ features?: RichTextFeature[];
11
+ /** Heading levels offered by the `heading` feature. Defaults to `[1, 2, 3]`. */
12
+ headingLevels?: HeadingLevel[];
8
13
  }
9
- export declare function RichTextEditor({ value, onChange, label, disabled, collection }: RichTextEditorProps): React.JSX.Element;
14
+ export declare function RichTextEditor({ value, onChange, label, disabled, collection, features, headingLevels }: RichTextEditorProps): React.JSX.Element;
10
15
  export {};
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { HashRouter, Link, MemoryRouter, Route, Routes, useLocation, useNavigate
5
5
  import { QueryClient, QueryClientProvider, useInfiniteQuery, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
6
6
  import { createClient } from "@dyrected/sdk";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ArrowUpDown, ArrowUpRight, Bold, Braces, Calendar, Check, CheckCircle, CheckCircle2, ChevronDown, ChevronDownIcon, ChevronLeft, ChevronLeftIcon, ChevronRight, ChevronRightIcon, ChevronUp, ChevronsUpDown, Circle, Clock, Clock3, Code, Copy, Database, Download, ExternalLink, Eye, EyeOff, FileDown, FileIcon, FileText, FileUp, Filter, Globe, GripVertical, Heading1, Heading2, Home, Image as Image$1, Info, Italic, KeyRound, Layers, LayoutDashboard, LayoutGrid, Library, Link as Link$1, List, ListOrdered, Loader2, Lock, LogOut, Mail, Menu, Monitor, Moon, MoreHorizontal, MousePointer2, PanelLeftClose, PanelLeftOpen, Pencil, Play, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, Sun, Table, Trash2, Underline, Undo2, Upload, UploadCloud, Users, Video, Volume2, Workflow, X, XCircle, icons } from "lucide-react";
8
+ import { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ArrowUpDown, ArrowUpRight, Bold, Braces, Calendar, Check, CheckCircle, CheckCircle2, ChevronDown, ChevronDownIcon, ChevronLeft, ChevronLeftIcon, ChevronRight, ChevronRightIcon, ChevronUp, ChevronsUpDown, Circle, Clock, Clock3, Code, Copy, Database, Download, ExternalLink, Eye, EyeOff, FileDown, FileIcon, FileText, FileUp, Filter, Globe, GripVertical, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Home, Image as Image$1, Info, Italic, KeyRound, Layers, LayoutDashboard, LayoutGrid, Library, Link as Link$1, List, ListOrdered, Loader2, Lock, LogOut, Mail, Menu, Monitor, Moon, MoreHorizontal, MousePointer2, PanelLeftClose, PanelLeftOpen, Pencil, Play, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, Sun, Table, Trash2, Underline, Undo2, Upload, UploadCloud, Users, Video, Volume2, Workflow, X, XCircle, icons } from "lucide-react";
9
9
  import { clsx } from "clsx";
10
10
  import { extendTailwindMerge } from "tailwind-merge";
11
11
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
@@ -30,8 +30,6 @@ import { EditorContent, useEditor } from "@tiptap/react";
30
30
  import StarterKit from "@tiptap/starter-kit";
31
31
  import TextAlign from "@tiptap/extension-text-align";
32
32
  import Image$2 from "@tiptap/extension-image";
33
- import LinkExtension from "@tiptap/extension-link";
34
- import Underline$1 from "@tiptap/extension-underline";
35
33
  import { TableKit } from "@tiptap/extension-table";
36
34
  import * as TogglePrimitive from "@radix-ui/react-toggle";
37
35
  import * as TabsPrimitive from "@radix-ui/react-tabs";
@@ -578,7 +576,7 @@ var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PU
578
576
  ref
579
577
  }));
580
578
  SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
581
- var sheetVariants = cva("dy-fixed dy-z-50 dy-gap-4 dy-bg-background dy-p-6 dy-shadow-2xl dy-transition dy-ease-in-out data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-duration-300 data-[state=open]:dy-duration-500", {
579
+ var sheetVariants = cva("dy-fixed dy-z-50 dy-gap-4 dy-bg-background dy-p-0 dy-shadow-2xl dy-transition dy-ease-in-out data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-duration-300 data-[state=open]:dy-duration-500", {
582
580
  variants: { side: {
583
581
  top: "dy-inset-x-0 dy-top-0 dy-border-b data-[state=closed]:dy-slide-out-to-top data-[state=open]:dy-slide-in-from-top",
584
582
  bottom: "dy-inset-x-0 dy-bottom-0 dy-border-t data-[state=closed]:dy-slide-out-to-bottom data-[state=open]:dy-slide-in-from-bottom",
@@ -967,7 +965,7 @@ function isNewerVersion(latest, current) {
967
965
  return false;
968
966
  }
969
967
  function useUpdateCheck() {
970
- const currentVersion = "2.5.56";
968
+ const currentVersion = "2.5.57";
971
969
  const [updateInfo, setUpdateInfo] = useState(() => {
972
970
  if (typeof window === "undefined") return null;
973
971
  const cacheKey = "dyrected_latest_release";
@@ -1114,20 +1112,24 @@ function AdminShell({ children, isEmbedded = false }) {
1114
1112
  })
1115
1113
  }), /* @__PURE__ */ jsxs("div", {
1116
1114
  className: "dy-ml-auto dy-flex dy-items-center dy-gap-1.5",
1117
- children: [/* @__PURE__ */ jsx(ThemeSelector, { mobile: true }), user && /* @__PURE__ */ jsx("div", {
1118
- className: "dy-flex dy-h-8 dy-w-8 dy-items-center dy-justify-center dy-rounded-full dy-bg-primary/10 dy-text-primary dy-font-semibold dy-text-xs dy-shrink-0",
1119
- children: (user.name || user.email || "?").charAt(0).toUpperCase()
1120
- })]
1115
+ children: [
1116
+ /* @__PURE__ */ jsx(ThemeSelector, { mobile: true }),
1117
+ user && /* @__PURE__ */ jsx("div", {
1118
+ className: "dy-flex dy-h-8 dy-w-8 dy-items-center dy-justify-center dy-rounded-full dy-bg-primary/10 dy-text-primary dy-font-semibold dy-text-xs dy-shrink-0",
1119
+ children: (user.name || user.email || "?").charAt(0).toUpperCase()
1120
+ }),
1121
+ /* @__PURE__ */ jsx("button", {
1122
+ type: "button",
1123
+ onClick: () => setMobileOpen(true),
1124
+ className: "dy-flex dy-h-9 dy-w-9 dy-items-center dy-justify-center dy-rounded-md dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground dy-transition-colors",
1125
+ "aria-label": "Open menu",
1126
+ children: /* @__PURE__ */ jsx(Menu, { className: "dy-h-5 dy-w-5" })
1127
+ })
1128
+ ]
1121
1129
  })]
1122
- }), /* @__PURE__ */ jsxs("div", {
1130
+ }), /* @__PURE__ */ jsx("div", {
1123
1131
  className: "dy-flex-1 dy-py-6 dy-px-4 lg:dy-py-10 lg:dy-px-6",
1124
- children: [children, /* @__PURE__ */ jsx("button", {
1125
- type: "button",
1126
- onClick: () => setMobileOpen(true),
1127
- className: "dy-flex dy-h-9 dy-w-9 dy-items-center dy-justify-center dy-rounded-md dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground dy-transition-colors",
1128
- "aria-label": "Open menu",
1129
- children: /* @__PURE__ */ jsx(Menu, { className: "dy-h-5 dy-w-5" })
1130
- })]
1132
+ children
1131
1133
  })]
1132
1134
  })
1133
1135
  ]
@@ -1217,7 +1219,7 @@ function getStatusLabel(doc) {
1217
1219
  }
1218
1220
  function Dashboard() {
1219
1221
  const { client, components, user } = useDyrected();
1220
- const currentVersion = "2.5.56";
1222
+ const currentVersion = "2.5.57";
1221
1223
  const [latestVersion, setLatestVersion] = useState(() => {
1222
1224
  if (typeof window === "undefined") return null;
1223
1225
  return localStorage.getItem("dyrected_latest_release");
@@ -3294,6 +3296,28 @@ function MediaGrid({ items, baseUrl, onDelete, slug }) {
3294
3296
  });
3295
3297
  }
3296
3298
  //#endregion
3299
+ //#region src/lib/preview-url.ts
3300
+ /**
3301
+ * Resolves the previewUrl from a collection schema against a document entry.
3302
+ * Handles both function-based configurations and Jexl string evaluations.
3303
+ */
3304
+ function resolvePreviewUrl(previewUrlConfig, entry, siteUrl) {
3305
+ if (!previewUrlConfig) return null;
3306
+ let previewUrl = typeof previewUrlConfig === "function" ? previewUrlConfig(entry, { locale: "en" }) : previewUrlConfig;
3307
+ if (typeof previewUrl === "string" && previewUrl.includes("{{")) previewUrl = previewUrl.replace(/{{(.*?)}}/g, (_, key) => String(entry?.[key.trim()] || ""));
3308
+ else if (typeof previewUrl === "string" && entry) try {
3309
+ const context = {
3310
+ ...entry,
3311
+ siteUrl
3312
+ };
3313
+ if (previewUrl.includes("+") || previewUrl.includes("?") || previewUrl.includes("==") || previewUrl.includes("siteUrl")) previewUrl = jexl.evalSync(previewUrl, context);
3314
+ } catch (e) {
3315
+ console.error("[PreviewDebug] Jexl Evaluation Failed:", e);
3316
+ }
3317
+ if (typeof previewUrl === "string" && previewUrl.startsWith("/")) previewUrl = `${siteUrl}${previewUrl}`;
3318
+ return typeof previewUrl === "string" ? previewUrl : null;
3319
+ }
3320
+ //#endregion
3297
3321
  //#region src/components/ui/toggle.tsx
3298
3322
  var toggleVariants = cva("dy-inline-flex dy-items-center dy-justify-center dy-rounded-md dy-text-sm dy-font-medium dy-ring-offset-background dy-transition-colors hover:dy-bg-muted hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring focus-visible:dy-ring-offset-2 disabled:dy-pointer-events-none disabled:dy-opacity-50 data-[state=on]:dy-bg-accent data-[state=on]:dy-text-accent-foreground [&_svg]:dy-pointer-events-none [&_svg]:dy-size-4 [&_svg]:dy-shrink-0 dy-gap-2", {
3299
3323
  variants: {
@@ -4705,11 +4729,35 @@ var Label = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
4705
4729
  Label.displayName = LabelPrimitive.Root.displayName;
4706
4730
  //#endregion
4707
4731
  //#region src/components/forms/fields/rich-text-editor.tsx
4708
- var MenuBar = ({ editor, collection = "media" }) => {
4732
+ var DEFAULT_HEADING_LEVELS = [
4733
+ 1,
4734
+ 2,
4735
+ 3
4736
+ ];
4737
+ var HEADING_ICONS = {
4738
+ 1: Heading1,
4739
+ 2: Heading2,
4740
+ 3: Heading3,
4741
+ 4: Heading4,
4742
+ 5: Heading5,
4743
+ 6: Heading6
4744
+ };
4745
+ /** True when `feature` is enabled. An undefined feature list means every feature is on. */
4746
+ function hasFeature(features, feature) {
4747
+ return !features || features.includes(feature);
4748
+ }
4749
+ var Separator$1 = () => /* @__PURE__ */ jsx("div", { className: "dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" });
4750
+ var MenuBar = ({ editor, collection = "media", features, headingLevels }) => {
4709
4751
  const [linkOpen, setLinkOpen] = React$1.useState(false);
4710
4752
  const [linkUrl, setLinkUrl] = React$1.useState("");
4711
4753
  const [linkNewTab, setLinkNewTab] = React$1.useState(false);
4712
4754
  if (!editor) return null;
4755
+ const has = (feature) => hasFeature(features, feature);
4756
+ const inlineGroup = has("bold") || has("italic") || has("underline") || has("strike");
4757
+ const blockGroup = has("heading") || has("bulletList") || has("orderedList") || has("blockquote");
4758
+ const alignGroup = has("align");
4759
+ const linkGroup = has("link");
4760
+ const tableGroup = has("table");
4713
4761
  const openLinkDialog = () => {
4714
4762
  const attrs = editor.getAttributes("link");
4715
4763
  setLinkUrl(attrs.href || "");
@@ -4731,82 +4779,85 @@ var MenuBar = ({ editor, collection = "media" }) => {
4731
4779
  return /* @__PURE__ */ jsxs("div", {
4732
4780
  className: "dy-border dy-border-input dy-rounded-t-md dy-p-1 dy-flex dy-flex-wrap dy-gap-1 dy-items-center dy-bg-muted/50",
4733
4781
  children: [
4734
- /* @__PURE__ */ jsx(Toggle, {
4735
- size: "sm",
4736
- pressed: editor.isActive("bold"),
4737
- onPressedChange: () => editor.chain().focus().toggleBold().run(),
4738
- children: /* @__PURE__ */ jsx(Bold, { className: "dy-h-4 dy-w-4" })
4739
- }),
4740
- /* @__PURE__ */ jsx(Toggle, {
4741
- size: "sm",
4742
- pressed: editor.isActive("italic"),
4743
- onPressedChange: () => editor.chain().focus().toggleItalic().run(),
4744
- children: /* @__PURE__ */ jsx(Italic, { className: "dy-h-4 dy-w-4" })
4745
- }),
4746
- /* @__PURE__ */ jsx(Toggle, {
4747
- size: "sm",
4748
- pressed: editor.isActive("underline"),
4749
- onPressedChange: () => editor.chain().focus().toggleUnderline().run(),
4750
- children: /* @__PURE__ */ jsx(Underline, { className: "dy-h-4 dy-w-4" })
4751
- }),
4752
- /* @__PURE__ */ jsx(Toggle, {
4753
- size: "sm",
4754
- pressed: editor.isActive("strike"),
4755
- onPressedChange: () => editor.chain().focus().toggleStrike().run(),
4756
- children: /* @__PURE__ */ jsx(Strikethrough, { className: "dy-h-4 dy-w-4" })
4757
- }),
4758
- /* @__PURE__ */ jsx("div", { className: "dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" }),
4759
- /* @__PURE__ */ jsx(Toggle, {
4760
- size: "sm",
4761
- pressed: editor.isActive("heading", { level: 1 }),
4762
- onPressedChange: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
4763
- children: /* @__PURE__ */ jsx(Heading1, { className: "dy-h-4 dy-w-4" })
4764
- }),
4765
- /* @__PURE__ */ jsx(Toggle, {
4766
- size: "sm",
4767
- pressed: editor.isActive("heading", { level: 2 }),
4768
- onPressedChange: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
4769
- children: /* @__PURE__ */ jsx(Heading2, { className: "dy-h-4 dy-w-4" })
4770
- }),
4771
- /* @__PURE__ */ jsx(Toggle, {
4772
- size: "sm",
4773
- pressed: editor.isActive("bulletList"),
4774
- onPressedChange: () => editor.chain().focus().toggleBulletList().run(),
4775
- children: /* @__PURE__ */ jsx(List, { className: "dy-h-4 dy-w-4" })
4776
- }),
4777
- /* @__PURE__ */ jsx(Toggle, {
4778
- size: "sm",
4779
- pressed: editor.isActive("orderedList"),
4780
- onPressedChange: () => editor.chain().focus().toggleOrderedList().run(),
4781
- children: /* @__PURE__ */ jsx(ListOrdered, { className: "dy-h-4 dy-w-4" })
4782
- }),
4783
- /* @__PURE__ */ jsx(Toggle, {
4784
- size: "sm",
4785
- pressed: editor.isActive("blockquote"),
4786
- onPressedChange: () => editor.chain().focus().toggleBlockquote().run(),
4787
- children: /* @__PURE__ */ jsx(Quote, { className: "dy-h-4 dy-w-4" })
4788
- }),
4789
- /* @__PURE__ */ jsx("div", { className: "dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" }),
4790
- /* @__PURE__ */ jsx(Toggle, {
4791
- size: "sm",
4792
- pressed: editor.isActive({ textAlign: "left" }),
4793
- onPressedChange: () => editor.chain().focus().setTextAlign("left").run(),
4794
- children: /* @__PURE__ */ jsx(AlignLeft, { className: "dy-h-4 dy-w-4" })
4795
- }),
4796
- /* @__PURE__ */ jsx(Toggle, {
4797
- size: "sm",
4798
- pressed: editor.isActive({ textAlign: "center" }),
4799
- onPressedChange: () => editor.chain().focus().setTextAlign("center").run(),
4800
- children: /* @__PURE__ */ jsx(AlignCenter, { className: "dy-h-4 dy-w-4" })
4801
- }),
4802
- /* @__PURE__ */ jsx(Toggle, {
4803
- size: "sm",
4804
- pressed: editor.isActive({ textAlign: "right" }),
4805
- onPressedChange: () => editor.chain().focus().setTextAlign("right").run(),
4806
- children: /* @__PURE__ */ jsx(AlignRight, { className: "dy-h-4 dy-w-4" })
4807
- }),
4808
- /* @__PURE__ */ jsx("div", { className: "dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" }),
4809
- /* @__PURE__ */ jsxs(Popover, {
4782
+ inlineGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
4783
+ has("bold") && /* @__PURE__ */ jsx(Toggle, {
4784
+ size: "sm",
4785
+ pressed: editor.isActive("bold"),
4786
+ onPressedChange: () => editor.chain().focus().toggleBold().run(),
4787
+ children: /* @__PURE__ */ jsx(Bold, { className: "dy-h-4 dy-w-4" })
4788
+ }),
4789
+ has("italic") && /* @__PURE__ */ jsx(Toggle, {
4790
+ size: "sm",
4791
+ pressed: editor.isActive("italic"),
4792
+ onPressedChange: () => editor.chain().focus().toggleItalic().run(),
4793
+ children: /* @__PURE__ */ jsx(Italic, { className: "dy-h-4 dy-w-4" })
4794
+ }),
4795
+ has("underline") && /* @__PURE__ */ jsx(Toggle, {
4796
+ size: "sm",
4797
+ pressed: editor.isActive("underline"),
4798
+ onPressedChange: () => editor.chain().focus().toggleUnderline().run(),
4799
+ children: /* @__PURE__ */ jsx(Underline, { className: "dy-h-4 dy-w-4" })
4800
+ }),
4801
+ has("strike") && /* @__PURE__ */ jsx(Toggle, {
4802
+ size: "sm",
4803
+ pressed: editor.isActive("strike"),
4804
+ onPressedChange: () => editor.chain().focus().toggleStrike().run(),
4805
+ children: /* @__PURE__ */ jsx(Strikethrough, { className: "dy-h-4 dy-w-4" })
4806
+ })
4807
+ ] }),
4808
+ inlineGroup && blockGroup && /* @__PURE__ */ jsx(Separator$1, {}),
4809
+ blockGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
4810
+ has("heading") && headingLevels.map((level) => {
4811
+ const Icon = HEADING_ICONS[level];
4812
+ return /* @__PURE__ */ jsx(Toggle, {
4813
+ size: "sm",
4814
+ pressed: editor.isActive("heading", { level }),
4815
+ onPressedChange: () => editor.chain().focus().toggleHeading({ level }).run(),
4816
+ children: /* @__PURE__ */ jsx(Icon, { className: "dy-h-4 dy-w-4" })
4817
+ }, level);
4818
+ }),
4819
+ has("bulletList") && /* @__PURE__ */ jsx(Toggle, {
4820
+ size: "sm",
4821
+ pressed: editor.isActive("bulletList"),
4822
+ onPressedChange: () => editor.chain().focus().toggleBulletList().run(),
4823
+ children: /* @__PURE__ */ jsx(List, { className: "dy-h-4 dy-w-4" })
4824
+ }),
4825
+ has("orderedList") && /* @__PURE__ */ jsx(Toggle, {
4826
+ size: "sm",
4827
+ pressed: editor.isActive("orderedList"),
4828
+ onPressedChange: () => editor.chain().focus().toggleOrderedList().run(),
4829
+ children: /* @__PURE__ */ jsx(ListOrdered, { className: "dy-h-4 dy-w-4" })
4830
+ }),
4831
+ has("blockquote") && /* @__PURE__ */ jsx(Toggle, {
4832
+ size: "sm",
4833
+ pressed: editor.isActive("blockquote"),
4834
+ onPressedChange: () => editor.chain().focus().toggleBlockquote().run(),
4835
+ children: /* @__PURE__ */ jsx(Quote, { className: "dy-h-4 dy-w-4" })
4836
+ })
4837
+ ] }),
4838
+ (inlineGroup || blockGroup) && alignGroup && /* @__PURE__ */ jsx(Separator$1, {}),
4839
+ alignGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
4840
+ /* @__PURE__ */ jsx(Toggle, {
4841
+ size: "sm",
4842
+ pressed: editor.isActive({ textAlign: "left" }),
4843
+ onPressedChange: () => editor.chain().focus().setTextAlign("left").run(),
4844
+ children: /* @__PURE__ */ jsx(AlignLeft, { className: "dy-h-4 dy-w-4" })
4845
+ }),
4846
+ /* @__PURE__ */ jsx(Toggle, {
4847
+ size: "sm",
4848
+ pressed: editor.isActive({ textAlign: "center" }),
4849
+ onPressedChange: () => editor.chain().focus().setTextAlign("center").run(),
4850
+ children: /* @__PURE__ */ jsx(AlignCenter, { className: "dy-h-4 dy-w-4" })
4851
+ }),
4852
+ /* @__PURE__ */ jsx(Toggle, {
4853
+ size: "sm",
4854
+ pressed: editor.isActive({ textAlign: "right" }),
4855
+ onPressedChange: () => editor.chain().focus().setTextAlign("right").run(),
4856
+ children: /* @__PURE__ */ jsx(AlignRight, { className: "dy-h-4 dy-w-4" })
4857
+ })
4858
+ ] }),
4859
+ (inlineGroup || blockGroup || alignGroup) && linkGroup && /* @__PURE__ */ jsx(Separator$1, {}),
4860
+ linkGroup && /* @__PURE__ */ jsxs(Popover, {
4810
4861
  open: linkOpen,
4811
4862
  onOpenChange: setLinkOpen,
4812
4863
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
@@ -4873,8 +4924,8 @@ var MenuBar = ({ editor, collection = "media" }) => {
4873
4924
  })
4874
4925
  })]
4875
4926
  }),
4876
- /* @__PURE__ */ jsx("div", { className: "dy-w-[1px] dy-h-6 dy-bg-border dy-mx-1" }),
4877
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
4927
+ (inlineGroup || blockGroup || alignGroup || linkGroup) && tableGroup && /* @__PURE__ */ jsx(Separator$1, {}),
4928
+ tableGroup && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
4878
4929
  asChild: true,
4879
4930
  children: /* @__PURE__ */ jsx(Toggle, {
4880
4931
  size: "sm",
@@ -4935,7 +4986,7 @@ var MenuBar = ({ editor, collection = "media" }) => {
4935
4986
  })
4936
4987
  ]
4937
4988
  })] }),
4938
- /* @__PURE__ */ jsx("div", {
4989
+ has("image") && /* @__PURE__ */ jsx("div", {
4939
4990
  className: "dy-ml-auto",
4940
4991
  children: /* @__PURE__ */ jsx(MediaPicker, {
4941
4992
  collection,
@@ -4956,24 +5007,34 @@ var MenuBar = ({ editor, collection = "media" }) => {
4956
5007
  ]
4957
5008
  });
4958
5009
  };
4959
- function RichTextEditor({ value, onChange, label, disabled, collection = "media" }) {
5010
+ function RichTextEditor({ value, onChange, label, disabled, collection = "media", features, headingLevels }) {
4960
5011
  const [editingImage, setEditingImage] = React$1.useState(null);
4961
5012
  const [tempAlt, setTempAlt] = React$1.useState("");
5013
+ const levels = headingLevels ?? DEFAULT_HEADING_LEVELS;
5014
+ const has = (feature) => hasFeature(features, feature);
4962
5015
  const editor = useEditor({
4963
5016
  extensions: [
4964
- StarterKit,
4965
- TextAlign.configure({ types: ["heading", "paragraph"] }),
4966
- Underline$1,
4967
- Image$2.configure({ HTMLAttributes: { class: "rounded-md max-w-full h-auto my-4 cursor-pointer hover:ring-2 hover:ring-primary/50 transition-all" } }),
4968
- LinkExtension.configure({
4969
- openOnClick: false,
4970
- HTMLAttributes: { rel: "noopener noreferrer" }
5017
+ StarterKit.configure({
5018
+ bold: has("bold") ? void 0 : false,
5019
+ italic: has("italic") ? void 0 : false,
5020
+ strike: has("strike") ? void 0 : false,
5021
+ underline: has("underline") ? void 0 : false,
5022
+ blockquote: has("blockquote") ? void 0 : false,
5023
+ bulletList: has("bulletList") ? void 0 : false,
5024
+ orderedList: has("orderedList") ? void 0 : false,
5025
+ heading: has("heading") ? { levels } : false,
5026
+ link: has("link") ? {
5027
+ openOnClick: false,
5028
+ HTMLAttributes: { rel: "noopener noreferrer" }
5029
+ } : false
4971
5030
  }),
4972
- TableKit.configure({
5031
+ ...has("align") ? [TextAlign.configure({ types: ["heading", "paragraph"] })] : [],
5032
+ ...has("image") ? [Image$2.configure({ HTMLAttributes: { class: "rounded-md max-w-full h-auto my-4 cursor-pointer hover:ring-2 hover:ring-primary/50 transition-all" } })] : [],
5033
+ ...has("table") ? [TableKit.configure({
4973
5034
  table: { HTMLAttributes: { class: "dy-border-collapse dy-w-full dy-my-4" } },
4974
5035
  tableCell: { HTMLAttributes: { class: "dy-border dy-border-border dy-p-2 dy-align-top dy-min-w-[100px]" } },
4975
5036
  tableHeader: { HTMLAttributes: { class: "dy-border dy-border-border dy-p-2 dy-bg-muted dy-font-semibold dy-text-left" } }
4976
- })
5037
+ })] : []
4977
5038
  ],
4978
5039
  content: value,
4979
5040
  editable: !disabled,
@@ -5030,7 +5091,9 @@ function RichTextEditor({ value, onChange, label, disabled, collection = "media"
5030
5091
  className: "dy-flex dy-flex-col dy-w-full",
5031
5092
  children: [!disabled && /* @__PURE__ */ jsx(MenuBar, {
5032
5093
  editor,
5033
- collection
5094
+ collection,
5095
+ features,
5096
+ headingLevels: levels
5034
5097
  }), /* @__PURE__ */ jsx(EditorContent, {
5035
5098
  editor,
5036
5099
  className: cn(disabled && "dy-opacity-80 dy-prose prose lg:prose-xl lg:dy-prose-xl")
@@ -5855,6 +5918,7 @@ function initSandbox() {
5855
5918
  `;
5856
5919
  document.body.appendChild(sandboxIframe);
5857
5920
  window.addEventListener("message", (event) => {
5921
+ if (!sandboxIframe?.contentWindow || event.source !== sandboxIframe.contentWindow) return;
5858
5922
  if (event.data && typeof event.data === "object" && "hookId" in event.data) {
5859
5923
  const { hookId, result, error } = event.data;
5860
5924
  const handler = messageHandlers.get(hookId);
@@ -7447,12 +7511,17 @@ function FieldRenderer({ schema, field, collection, context }) {
7447
7511
  disabled,
7448
7512
  multiple: relSchema.hasMany
7449
7513
  });
7450
- case "richText": return /* @__PURE__ */ jsx(RichTextEditor, {
7451
- collection: context?.schemas?.collections?.find((c) => c.upload)?.slug || "media",
7452
- value: field.value,
7453
- onChange: field.onChange,
7454
- disabled
7455
- });
7514
+ case "richText": {
7515
+ const rtSchema = schema;
7516
+ return /* @__PURE__ */ jsx(RichTextEditor, {
7517
+ collection: rtSchema.uploadCollection || context?.schemas?.collections?.find((c) => c.upload)?.slug || "media",
7518
+ value: field.value,
7519
+ onChange: field.onChange,
7520
+ disabled,
7521
+ features: rtSchema.features,
7522
+ headingLevels: rtSchema.headingLevels
7523
+ });
7524
+ }
7456
7525
  case "json": return /* @__PURE__ */ jsx(JsonEditor, {
7457
7526
  value: field.value,
7458
7527
  onChange: field.onChange,
@@ -10761,21 +10830,8 @@ function CollectionListPage({ slug }) {
10761
10830
  return true;
10762
10831
  };
10763
10832
  const getPreviewUrl = (item) => {
10764
- if (!schema?.admin?.previewUrl) return null;
10765
- let previewUrl = typeof schema.admin.previewUrl === "function" ? schema.admin.previewUrl(item, { locale: "en" }) : schema.admin.previewUrl;
10766
10833
  const siteUrl = getSiteUrl(schemas?.admin?.siteUrl);
10767
- if (typeof previewUrl === "string" && previewUrl.includes("{{")) previewUrl = previewUrl.replace(/{{(.*?)}}/g, (_, key) => String(item[key.trim()] || ""));
10768
- else if (typeof previewUrl === "string") try {
10769
- const context = {
10770
- ...item,
10771
- siteUrl
10772
- };
10773
- if (previewUrl.includes("+") || previewUrl.includes("?") || previewUrl.includes("==") || previewUrl.includes("siteUrl")) previewUrl = jexl.evalSync(previewUrl, context);
10774
- } catch (e) {
10775
- console.warn("Preview URL eval failed:", e);
10776
- }
10777
- if (typeof previewUrl === "string" && previewUrl.startsWith("/")) previewUrl = `${siteUrl}${previewUrl}`;
10778
- return typeof previewUrl === "string" ? previewUrl : null;
10834
+ return resolvePreviewUrl(schema?.admin?.previewUrl, item, siteUrl);
10779
10835
  };
10780
10836
  const renderLinkedCell = (item, cell) => {
10781
10837
  const canDelete = canDeleteRow(item);
@@ -12150,19 +12206,8 @@ function EditEntryPage() {
12150
12206
  const workflowMeta = isEdit && entry ? entry._workflow ?? null : null;
12151
12207
  const hasStatus = schema?.fields.some((f) => f.name === "status");
12152
12208
  const currentStatus = entry?.status || "draft";
12153
- let previewUrl = typeof schema.admin?.previewUrl === "function" ? schema.admin.previewUrl(previewData || entry, { locale: "en" }) : schema.admin?.previewUrl;
12154
12209
  const siteUrl = getSiteUrl(schemas?.admin?.siteUrl);
12155
- if (typeof previewUrl === "string" && previewUrl.includes("{{")) previewUrl = previewUrl.replace(/{{(.*?)}}/g, (_, key) => entry?.[key.trim()] || "");
12156
- else if (typeof previewUrl === "string" && (previewData || entry)) try {
12157
- const context = {
12158
- ...previewData || entry,
12159
- siteUrl
12160
- };
12161
- if (previewUrl.includes("+") || previewUrl.includes("?") || previewUrl.includes("==") || previewUrl.includes("siteUrl")) previewUrl = jexl.evalSync(previewUrl, context);
12162
- } catch (e) {
12163
- console.error("[PreviewDebug] Jexl Evaluation Failed:", e);
12164
- }
12165
- if (typeof previewUrl === "string" && previewUrl.startsWith("/")) previewUrl = `${siteUrl}${previewUrl}`;
12210
+ const previewUrl = resolvePreviewUrl(schema.admin?.previewUrl, previewData || entry, siteUrl);
12166
12211
  const readAccess = schema.access?.read;
12167
12212
  let canRead = true;
12168
12213
  if (readAccess === false) canRead = false;
@@ -13923,7 +13968,7 @@ function SetupPromptUI({ config }) {
13923
13968
  setCopied(true);
13924
13969
  window.setTimeout(() => setCopied(false), 1800);
13925
13970
  }
13926
- const currentVersion = "2.5.56";
13971
+ const currentVersion = "2.5.57";
13927
13972
  const [latestVersion, setLatestVersion] = useState(() => {
13928
13973
  if (typeof window === "undefined") return null;
13929
13974
  return localStorage.getItem("dyrected_latest_release");
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Resolves the previewUrl from a collection schema against a document entry.
3
+ * Handles both function-based configurations and Jexl string evaluations.
4
+ */
5
+ export declare function resolvePreviewUrl(previewUrlConfig: string | ((doc: any, opts: any) => string | null) | undefined, entry: any, siteUrl: string): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/admin",
3
- "version": "2.5.56",
3
+ "version": "2.5.58",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -64,9 +64,9 @@
64
64
  "tailwind-merge": "^3.5.0",
65
65
  "tailwindcss-animate": "^1.0.7",
66
66
  "zod": "^3.25.76",
67
- "@dyrected/core": "^2.5.56",
68
- "@dyrected/sdk": "^2.5.56",
69
- "@dyrected/knowledge": "^0.2.10"
67
+ "@dyrected/core": "^2.5.58",
68
+ "@dyrected/knowledge": "^0.2.11",
69
+ "@dyrected/sdk": "^2.5.58"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@tanstack/react-query": "^5.0.0",