@dyrected/admin 2.5.48 → 2.5.49

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/admin.css CHANGED
@@ -2901,6 +2901,10 @@
2901
2901
  background-color: rgb(0 0 0 / 0.4);
2902
2902
  }
2903
2903
 
2904
+ .dy-bg-black\/5{
2905
+ background-color: rgb(0 0 0 / 0.05);
2906
+ }
2907
+
2904
2908
  .dy-bg-black\/60{
2905
2909
  background-color: rgb(0 0 0 / 0.6);
2906
2910
  }
@@ -2983,6 +2987,10 @@
2983
2987
  background-color: rgb(16 185 129 / 0.1);
2984
2988
  }
2985
2989
 
2990
+ .dy-bg-green-500\/10{
2991
+ background-color: rgb(34 197 94 / 0.1);
2992
+ }
2993
+
2986
2994
  .dy-bg-muted{
2987
2995
  background-color: hsl(var(--muted));
2988
2996
  }
@@ -3185,6 +3193,11 @@
3185
3193
  padding: 1px;
3186
3194
  }
3187
3195
 
3196
+ .dy-px-0{
3197
+ padding-left: 0px;
3198
+ padding-right: 0px;
3199
+ }
3200
+
3188
3201
  .dy-px-1{
3189
3202
  padding-left: 0.25rem;
3190
3203
  padding-right: 0.25rem;
@@ -3684,6 +3697,11 @@
3684
3697
  color: rgb(34 197 94 / var(--tw-text-opacity, 1));
3685
3698
  }
3686
3699
 
3700
+ .dy-text-green-600{
3701
+ --tw-text-opacity: 1;
3702
+ color: rgb(22 163 74 / var(--tw-text-opacity, 1));
3703
+ }
3704
+
3687
3705
  .dy-text-muted-foreground{
3688
3706
  color: hsl(var(--muted-foreground));
3689
3707
  }
@@ -5474,6 +5492,10 @@
5474
5492
  padding: 0px !important;
5475
5493
  }
5476
5494
 
5495
+ .dark\:dy-bg-white\/5:is(.dy-dark *){
5496
+ background-color: rgb(255 255 255 / 0.05);
5497
+ }
5498
+
5477
5499
  .dark\:dy-text-amber-400:is(.dy-dark *){
5478
5500
  --tw-text-opacity: 1;
5479
5501
  color: rgb(251 191 36 / var(--tw-text-opacity, 1));
@@ -5489,6 +5511,11 @@
5489
5511
  color: rgb(52 211 153 / var(--tw-text-opacity, 1));
5490
5512
  }
5491
5513
 
5514
+ .dark\:dy-text-green-400:is(.dy-dark *){
5515
+ --tw-text-opacity: 1;
5516
+ color: rgb(74 222 128 / var(--tw-text-opacity, 1));
5517
+ }
5518
+
5492
5519
  .dark\:dy-text-purple-400:is(.dy-dark *){
5493
5520
  --tw-text-opacity: 1;
5494
5521
  color: rgb(192 132 252 / var(--tw-text-opacity, 1));
package/dist/index.mjs CHANGED
@@ -586,7 +586,7 @@ function NavGroup({ label, children, collapsed, defaultExpanded = true }) {
586
586
  })]
587
587
  });
588
588
  }
589
- function ThemeSelector({ collapsed = false, mobile = false }) {
589
+ function ThemeSelector({ collapsed = false, mobile = false, iconOnly = false }) {
590
590
  const { resolvedTheme, setTheme, theme } = useAdminTheme();
591
591
  const Icon = resolvedTheme === "dark" ? Moon : Sun;
592
592
  const options = [
@@ -611,11 +611,11 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
611
611
  children: /* @__PURE__ */ jsxs(Button, {
612
612
  type: "button",
613
613
  variant: "ghost",
614
- size: mobile || collapsed ? "icon" : "sm",
615
- className: cn("dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground", collapsed || mobile ? "dy-h-8 dy-w-8" : "dy-h-7 dy-w-full dy-justify-start dy-px-2.5 dy-text-[11px]"),
614
+ size: mobile || collapsed || iconOnly ? "icon" : "sm",
615
+ className: cn("dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground", collapsed || mobile || iconOnly ? "dy-h-7 dy-w-7 dy-px-0 dy-justify-center" : "dy-h-7 dy-w-full dy-justify-start dy-px-2.5 dy-text-[11px]"),
616
616
  title: "Theme",
617
617
  "aria-label": "Change admin theme",
618
- children: [/* @__PURE__ */ jsx(Icon, { className: "dy-h-3.5 dy-w-3.5" }), !collapsed && !mobile && /* @__PURE__ */ jsx("span", { children: "Theme" })]
618
+ children: [/* @__PURE__ */ jsx(Icon, { className: "dy-h-3.5 dy-w-3.5" }), !collapsed && !mobile && !iconOnly && /* @__PURE__ */ jsx("span", { children: "Theme" })]
619
619
  })
620
620
  }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
621
621
  side: collapsed || mobile ? "bottom" : "top",
@@ -643,16 +643,16 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
643
643
  ]
644
644
  })] });
645
645
  }
646
- function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collapsed, onToggleCollapse, onNavigate }) {
646
+ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collapsed, onToggleCollapse, onNavigate, updateInfo }) {
647
647
  const { client, user } = useDyrected();
648
- const collections = schemas?.collections?.filter((c) => !c?.admin?.hidden && !c?.slug.startsWith("platform_")) ?? [];
649
- const globals = schemas?.globals?.filter((g) => !g?.admin?.hidden && !g?.slug.startsWith("platform_")) ?? [];
648
+ const collections = (schemas?.collections)?.filter((c) => !c?.admin?.hidden && !c?.slug.startsWith("platform_")) ?? [];
649
+ const globals = (schemas?.globals)?.filter((g) => !g?.admin?.hidden && !g?.slug.startsWith("platform_")) ?? [];
650
650
  const uploadCollections = collections.filter((c) => c.upload);
651
651
  const groupLabel = (text) => !collapsed ? /* @__PURE__ */ jsx("p", {
652
652
  className: "dy-px-3 dy-mb-1.5 dy-text-[10px] dy-font-semibold dy-uppercase dy-tracking-widest dy-text-muted-foreground/50",
653
653
  children: text
654
654
  }) : /* @__PURE__ */ jsx("div", { className: "dy-my-2 dy-mx-3 dy-h-px dy-bg-border" });
655
- const branding = schemas?.admin?.branding;
655
+ const branding = (schemas?.admin)?.branding;
656
656
  return /* @__PURE__ */ jsxs("div", {
657
657
  className: "dy-flex dy-flex-col dy-min-h-screen",
658
658
  children: [
@@ -783,12 +783,17 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
783
783
  /* @__PURE__ */ jsx(NavItem, {
784
784
  to: "/setup",
785
785
  icon: Sparkles,
786
- label: "Setup & Help",
786
+ label: /* @__PURE__ */ jsxs("div", {
787
+ className: "dy-flex dy-items-center dy-justify-between dy-w-full dy-min-w-0",
788
+ children: [/* @__PURE__ */ jsx("span", {
789
+ className: "dy-truncate",
790
+ children: "Setup & Help"
791
+ }), updateInfo?.hasUpdate && /* @__PURE__ */ jsx("span", { className: "dy-h-1.5 dy-w-1.5 dy-rounded-full dy-bg-primary dy-shrink-0 dy-ml-2" })]
792
+ }),
787
793
  active: location.pathname === "/setup",
788
794
  collapsed,
789
795
  onClick: onNavigate
790
796
  }),
791
- /* @__PURE__ */ jsx(ThemeSelector, { collapsed }),
792
797
  !isEmbedded && user && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
793
798
  asChild: true,
794
799
  children: /* @__PURE__ */ jsxs("button", {
@@ -834,26 +839,99 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
834
839
  })
835
840
  ]
836
841
  })] }),
837
- onToggleCollapse && !isEmbedded && /* @__PURE__ */ jsx("button", {
838
- onClick: onToggleCollapse,
839
- className: cn("dy-group/btn dy-mt-1 dy-flex dy-h-7 dy-w-full dy-items-center dy-gap-2 dy-rounded-md dy-px-2.5 dy-text-[11px] dy-font-medium dy-text-muted-foreground/45 dy-transition-colors hover:dy-bg-accent/40 hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring", collapsed ? "dy-justify-center dy-px-2" : ""),
840
- title: collapsed ? "Expand sidebar" : "Collapse sidebar",
841
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
842
- children: collapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "dy-h-3.5 dy-w-3.5" }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PanelLeftClose, { className: "dy-h-3.5 dy-w-3.5 dy-transition-transform dy-group-hover/btn:dy--translate-x-0.5" }), /* @__PURE__ */ jsx("span", { children: "Collapse sidebar" })] })
842
+ /* @__PURE__ */ jsxs("div", {
843
+ className: cn("dy-flex dy-items-center dy-gap-1.5 dy-w-full dy-mt-1", collapsed ? "dy-flex-col dy-items-center" : "dy-flex-row dy-justify-between"),
844
+ children: [onToggleCollapse && !isEmbedded && /* @__PURE__ */ jsx("button", {
845
+ onClick: onToggleCollapse,
846
+ className: cn("dy-group/btn dy-flex dy-h-7 dy-items-center dy-gap-2 dy-rounded-md dy-px-2.5 dy-text-[11px] dy-font-medium dy-text-muted-foreground/45 dy-transition-colors hover:dy-bg-accent/40 hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring", collapsed ? "dy-justify-center dy-px-2 dy-w-full" : "dy-flex-1"),
847
+ title: collapsed ? "Expand sidebar" : "Collapse sidebar",
848
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
849
+ children: collapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "dy-h-3.5 dy-w-3.5" }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PanelLeftClose, { className: "dy-h-3.5 dy-w-3.5 dy-transition-transform dy-group-hover/btn:dy--translate-x-0.5" }), /* @__PURE__ */ jsx("span", {
850
+ className: "dy-truncate",
851
+ children: "Collapse"
852
+ })] })
853
+ }), /* @__PURE__ */ jsx(ThemeSelector, {
854
+ collapsed,
855
+ iconOnly: !collapsed
856
+ })]
843
857
  })
844
858
  ]
845
859
  })
846
860
  ]
847
861
  });
848
862
  }
863
+ function isNewerVersion(latest, current) {
864
+ if (latest === current) return false;
865
+ const lParts = latest.split(".").map(Number);
866
+ const cParts = current.split(".").map(Number);
867
+ for (let i = 0; i < 3; i++) {
868
+ const l = lParts[i] || 0;
869
+ const c = cParts[i] || 0;
870
+ if (l > c) return true;
871
+ if (l < c) return false;
872
+ }
873
+ return false;
874
+ }
875
+ function useUpdateCheck() {
876
+ const currentVersion = "2.5.49";
877
+ const [updateInfo, setUpdateInfo] = useState(() => {
878
+ if (typeof window === "undefined") return null;
879
+ const cacheKey = "dyrected_latest_release";
880
+ const cacheTimeKey = "dyrected_latest_release_timestamp";
881
+ const cachedVersion = localStorage.getItem(cacheKey);
882
+ const cachedTimestamp = localStorage.getItem(cacheTimeKey);
883
+ if (cachedVersion && cachedTimestamp && Date.now() - Number(cachedTimestamp) < 1440 * 60 * 1e3) return {
884
+ latestVersion: cachedVersion,
885
+ hasUpdate: isNewerVersion(cachedVersion, currentVersion)
886
+ };
887
+ return null;
888
+ });
889
+ useEffect(() => {
890
+ let cancelled = false;
891
+ const cacheKey = "dyrected_latest_release";
892
+ const cacheTimeKey = "dyrected_latest_release_timestamp";
893
+ const oneDay = 1440 * 60 * 1e3;
894
+ const cachedVersion = localStorage.getItem(cacheKey);
895
+ const cachedTimestamp = localStorage.getItem(cacheTimeKey);
896
+ if (cachedVersion && cachedTimestamp && Date.now() - Number(cachedTimestamp) < oneDay) return;
897
+ async function fetchLatest() {
898
+ try {
899
+ const res = await fetch("https://registry.npmjs.org/@dyrected/core/latest");
900
+ if (!res.ok) return;
901
+ const latest = (await res.json())?.version;
902
+ if (latest) {
903
+ localStorage.setItem(cacheKey, latest);
904
+ localStorage.setItem(cacheTimeKey, String(Date.now()));
905
+ if (!cancelled) setUpdateInfo({
906
+ latestVersion: latest,
907
+ hasUpdate: isNewerVersion(latest, currentVersion)
908
+ });
909
+ }
910
+ } catch (err) {
911
+ console.error("Failed to check for updates:", err);
912
+ }
913
+ }
914
+ fetchLatest();
915
+ return () => {
916
+ cancelled = true;
917
+ };
918
+ }, [currentVersion]);
919
+ return updateInfo;
920
+ }
849
921
  function AdminShell({ children, isEmbedded = false }) {
850
922
  const { client, logout, user } = useDyrected();
851
923
  const location = useLocation();
924
+ const updateInfo = useUpdateCheck();
852
925
  const [collapsed, setCollapsed] = useState(false);
853
926
  const [mobileOpen, setMobileOpen] = useState(false);
854
927
  useEffect(() => {
855
- setMobileOpen(false);
856
- }, [location.pathname]);
928
+ if (mobileOpen) {
929
+ const timer = setTimeout(() => {
930
+ setMobileOpen(false);
931
+ }, 0);
932
+ return () => clearTimeout(timer);
933
+ }
934
+ }, [location.pathname, mobileOpen]);
857
935
  useEffect(() => {
858
936
  document.body.style.overflow = mobileOpen ? "hidden" : "";
859
937
  return () => {
@@ -868,7 +946,7 @@ function AdminShell({ children, isEmbedded = false }) {
868
946
  },
869
947
  enabled: !!client
870
948
  });
871
- const mobileBranding = schemas?.admin?.branding;
949
+ const mobileBranding = (schemas?.admin)?.branding;
872
950
  return /* @__PURE__ */ jsx(BrandingProvider, { children: /* @__PURE__ */ jsxs("div", {
873
951
  className: cn("dy-flex dy-w-full dy-relative", isEmbedded ? "dy-h-full dy-min-h-[600px]" : "dy-min-h-screen"),
874
952
  children: [
@@ -881,7 +959,8 @@ function AdminShell({ children, isEmbedded = false }) {
881
959
  logout,
882
960
  isEmbedded,
883
961
  collapsed,
884
- onToggleCollapse: () => setCollapsed((v) => !v)
962
+ onToggleCollapse: () => setCollapsed((v) => !v),
963
+ updateInfo
885
964
  })
886
965
  }),
887
966
  mobileOpen && /* @__PURE__ */ jsx("div", {
@@ -901,7 +980,8 @@ function AdminShell({ children, isEmbedded = false }) {
901
980
  logout,
902
981
  isEmbedded,
903
982
  collapsed: false,
904
- onNavigate: () => setMobileOpen(false)
983
+ onNavigate: () => setMobileOpen(false),
984
+ updateInfo
905
985
  })]
906
986
  }),
907
987
  /* @__PURE__ */ jsxs("main", {
@@ -1029,6 +1109,31 @@ function getStatusLabel(doc) {
1029
1109
  }
1030
1110
  function Dashboard() {
1031
1111
  const { client, components, user } = useDyrected();
1112
+ const currentVersion = "2.5.49";
1113
+ const [latestVersion, setLatestVersion] = useState(() => {
1114
+ if (typeof window === "undefined") return null;
1115
+ return localStorage.getItem("dyrected_latest_release");
1116
+ });
1117
+ useEffect(() => {
1118
+ if (!latestVersion) fetch("https://registry.npmjs.org/@dyrected/core/latest").then((r) => r.json()).then((data) => {
1119
+ if (data?.version) {
1120
+ setLatestVersion(data.version);
1121
+ localStorage.setItem("dyrected_latest_release", data.version);
1122
+ localStorage.setItem("dyrected_latest_release_timestamp", String(Date.now()));
1123
+ }
1124
+ }).catch(() => {});
1125
+ }, [latestVersion]);
1126
+ const hasUpdate = latestVersion && latestVersion !== currentVersion && (() => {
1127
+ const lParts = latestVersion.split(".").map(Number);
1128
+ const cParts = currentVersion.split(".").map(Number);
1129
+ for (let i = 0; i < 3; i++) {
1130
+ const l = lParts[i] || 0;
1131
+ const c = cParts[i] || 0;
1132
+ if (l > c) return true;
1133
+ if (l < c) return false;
1134
+ }
1135
+ return false;
1136
+ })();
1032
1137
  const { data: schemas, isLoading: isLoadingSchemas } = useQuery({
1033
1138
  queryKey: ["schemas"],
1034
1139
  queryFn: () => client.getSchemas(),
@@ -1065,6 +1170,12 @@ function Dashboard() {
1065
1170
  return (b.updatedAt ? new Date(b.updatedAt).getTime() : 0) - aTime;
1066
1171
  }).slice(0, 6);
1067
1172
  const attentionItems = [
1173
+ ...hasUpdate ? [{
1174
+ key: "dyrected-update",
1175
+ title: `A system update is available (v${latestVersion})`,
1176
+ description: "Please notify your developer or site administrator to apply this update.",
1177
+ to: "/setup"
1178
+ }] : [],
1068
1179
  ...collections.filter((collection) => !collection.upload && !collection.admin?.useAsTitle).map((collection) => ({
1069
1180
  key: `title-${collection.slug}`,
1070
1181
  title: `${getCollectionLabel(collection)} needs a title field`,
@@ -6690,244 +6801,228 @@ function UrlField({ schema: _schema, field, disabled, context: _context }) {
6690
6801
  const [docsLoading, setDocsLoading] = React$1.useState(false);
6691
6802
  const parseValue = (val) => {
6692
6803
  if (!val) return {
6693
- mode: "external",
6694
- data: {
6695
- type: "custom",
6696
- url: "",
6697
- label: ""
6698
- }
6804
+ type: "custom",
6805
+ url: ""
6699
6806
  };
6700
6807
  if (typeof val === "string") return {
6701
- mode: "external",
6702
- data: {
6703
- type: "custom",
6704
- url: val,
6705
- label: ""
6706
- }
6808
+ type: "custom",
6809
+ url: val
6707
6810
  };
6708
- if (typeof val === "object" && val.type) return {
6709
- mode: val.type === "internal" ? "internal" : "external",
6710
- data: val
6811
+ if (typeof val === "object") return {
6812
+ type: val.type === "internal" ? "internal" : "custom",
6813
+ url: val.url || "",
6814
+ relationTo: val.relationTo,
6815
+ value: val.value,
6816
+ label: val.label
6711
6817
  };
6712
6818
  return {
6713
- mode: "external",
6714
- data: {
6715
- type: "custom",
6716
- url: String(val),
6717
- label: ""
6718
- }
6819
+ type: "custom",
6820
+ url: String(val)
6719
6821
  };
6720
6822
  };
6721
- const { mode: currentMode, data: currentData } = parseValue(field.value);
6722
- const [mode, setMode] = React$1.useState(currentMode);
6723
- const [urlValue, setUrlValue] = React$1.useState(currentData.url || "");
6823
+ const currentData = parseValue(field.value);
6824
+ const [urlValue, setUrlValue] = React$1.useState(currentData.url);
6724
6825
  const [labelValue, setLabelValue] = React$1.useState(currentData.label || "");
6725
6826
  const [collectionValue, setCollectionValue] = React$1.useState(currentData.relationTo || "");
6726
6827
  const [docValue, setDocValue] = React$1.useState(currentData.value || "");
6727
- const [resolvedUrlValue, setResolvedUrlValue] = React$1.useState(currentMode === "internal" ? currentData.url || "" : "");
6728
- const [docSearch, setDocSearch] = React$1.useState("");
6729
- const collections = schemas?.collections || [];
6828
+ const collections = React$1.useMemo(() => schemas?.collections || [], [schemas?.collections]);
6730
6829
  React$1.useEffect(() => {
6731
- if (collectionValue && client) {
6830
+ if (!client || !schemas?.collections) return;
6831
+ const eligibleCollections = collections.filter((c) => c.admin?.previewUrl);
6832
+ if (eligibleCollections.length === 0) return;
6833
+ let active = true;
6834
+ Promise.resolve().then(() => {
6835
+ if (!active) return;
6732
6836
  setDocsLoading(true);
6733
6837
  setDocuments([]);
6734
- client.collection(collectionValue).find({ limit: 50 }).exec().then((res) => {
6735
- setDocuments(res.docs || []);
6736
- }).catch((err) => {
6737
- console.error("Failed to load documents:", err);
6738
- setDocuments([]);
6739
- }).finally(() => {
6740
- setDocsLoading(false);
6741
- });
6742
- }
6743
- }, [collectionValue, client]);
6744
- const handleUpdate = (newMode, updates) => {
6745
- let newValue;
6746
- if (newMode === "external") newValue = {
6747
- type: "custom",
6748
- url: updates.url !== void 0 ? updates.url : urlValue,
6749
- label: updates.label !== void 0 ? updates.label : labelValue
6838
+ });
6839
+ Promise.all(eligibleCollections.map((col) => client.collection(col.slug).find({ limit: 50 }).exec().then((res) => ({
6840
+ collection: col,
6841
+ docs: res.docs || []
6842
+ })).catch((err) => {
6843
+ console.error(`Failed to load documents for ${col.slug}:`, err);
6844
+ return {
6845
+ collection: col,
6846
+ docs: []
6847
+ };
6848
+ }))).then((results) => {
6849
+ if (!active) return;
6850
+ setDocuments(results.flatMap((r) => r.docs.map((doc) => ({
6851
+ ...doc,
6852
+ __collectionSlug: r.collection.slug,
6853
+ __collectionLabel: r.collection.labels?.plural || r.collection.slug,
6854
+ __previewUrl: r.collection.admin?.previewUrl
6855
+ }))));
6856
+ }).catch((err) => {
6857
+ console.error("Failed to load documents:", err);
6858
+ }).finally(() => {
6859
+ if (active) setDocsLoading(false);
6860
+ });
6861
+ return () => {
6862
+ active = false;
6750
6863
  };
6751
- else newValue = {
6752
- type: "internal",
6753
- url: (updates.url !== void 0 ? updates.url : resolvedUrlValue) || void 0,
6754
- relationTo: updates.relationTo || collectionValue,
6755
- value: updates.value || docValue,
6756
- label: updates.label !== void 0 ? updates.label : labelValue
6864
+ }, [
6865
+ client,
6866
+ schemas,
6867
+ collections
6868
+ ]);
6869
+ const handleUpdate = (url, label, relationTo, docId) => {
6870
+ const isInternal = !!(relationTo && docId);
6871
+ const newValue = {
6872
+ type: isInternal ? "internal" : "custom",
6873
+ url: url || void 0,
6874
+ relationTo: isInternal ? relationTo : void 0,
6875
+ value: isInternal ? docId : void 0,
6876
+ label: label || void 0
6757
6877
  };
6758
6878
  field.onChange(newValue);
6759
6879
  };
6760
- const handleModeChange = (newMode) => {
6761
- setMode(newMode);
6762
- if (newMode === "external") {
6763
- setUrlValue(currentData.url || "");
6764
- setLabelValue(currentData.label || "");
6765
- setResolvedUrlValue("");
6766
- } else {
6767
- setCollectionValue(currentData.relationTo || "");
6768
- setDocValue(currentData.value || "");
6769
- setResolvedUrlValue(currentData.type === "internal" ? currentData.url || "" : "");
6770
- setLabelValue(currentData.label || "");
6771
- }
6772
- };
6773
- const handleExternalUrlChange = (value) => {
6880
+ const handleUrlInputChange = (value) => {
6774
6881
  setUrlValue(value);
6775
- handleUpdate("external", { url: value });
6882
+ setCollectionValue("");
6883
+ setDocValue("");
6884
+ handleUpdate(value, labelValue);
6776
6885
  };
6777
- const handleExternalLabelChange = (value) => {
6886
+ const handleLabelInputChange = (value) => {
6778
6887
  setLabelValue(value);
6779
- handleUpdate("external", { label: value });
6780
- };
6781
- const handleCollectionChange = (value) => {
6782
- setCollectionValue(value);
6783
- setDocValue("");
6784
- setResolvedUrlValue("");
6888
+ handleUpdate(urlValue, value, collectionValue, docValue);
6785
6889
  };
6786
- const handleDocumentSelect = (docId) => {
6787
- setDocValue(docId);
6890
+ const handleDocumentSelect = (doc) => {
6891
+ setDocValue(doc.id);
6892
+ setCollectionValue(doc.__collectionSlug);
6788
6893
  setOpenPopover(false);
6789
- const urlPattern = collections.find((c) => c.slug === collectionValue)?.admin?.urlPattern;
6790
- const doc = documents.find((d) => d.id === docId);
6791
- const resolvedUrl = urlPattern && doc ? interpolateUrlPattern(urlPattern, doc) : void 0;
6792
- setResolvedUrlValue(resolvedUrl || "");
6793
- handleUpdate("internal", {
6794
- value: docId,
6795
- url: resolvedUrl
6796
- });
6797
- };
6798
- const handleInternalLabelChange = (value) => {
6799
- setLabelValue(value);
6800
- handleUpdate("internal", { label: value });
6894
+ const urlPattern = doc.__previewUrl;
6895
+ let resolvedUrl = void 0;
6896
+ if (urlPattern) {
6897
+ if (typeof urlPattern === "function") resolvedUrl = urlPattern(doc, { locale: "en" });
6898
+ else if (typeof urlPattern === "string") if (urlPattern.includes("{{")) resolvedUrl = urlPattern.replace(/{{(.*?)}}/g, (_, key) => String(doc[key.trim()] || ""));
6899
+ else try {
6900
+ const context = {
6901
+ ...doc,
6902
+ siteUrl: window.location.origin
6903
+ };
6904
+ if (urlPattern.includes("+") || urlPattern.includes("?") || urlPattern.includes("==") || urlPattern.includes("siteUrl")) resolvedUrl = jexl.evalSync(urlPattern, context);
6905
+ else resolvedUrl = interpolateUrlPattern(urlPattern, doc);
6906
+ } catch {
6907
+ resolvedUrl = interpolateUrlPattern(urlPattern, doc);
6908
+ }
6909
+ }
6910
+ if (typeof resolvedUrl === "string" && resolvedUrl.startsWith("/")) resolvedUrl = `${window.location.origin}${resolvedUrl}`;
6911
+ const finalUrl = resolvedUrl || "";
6912
+ setUrlValue(finalUrl);
6913
+ handleUpdate(finalUrl, labelValue, doc.__collectionSlug, doc.id);
6801
6914
  };
6802
- const getCollectionDisplayField = (collectionSlug) => {
6915
+ const getCollectionDisplayField = React$1.useCallback((collectionSlug) => {
6803
6916
  return collections.find((c) => c.slug === collectionSlug)?.admin?.useAsTitle || "title";
6804
- };
6805
- const getDocumentDisplay = (doc, collectionSlug) => {
6917
+ }, [collections]);
6918
+ const getDocumentDisplay = React$1.useCallback((doc, collectionSlug) => {
6806
6919
  return doc[getCollectionDisplayField(collectionSlug)] || doc.name || doc.slug || doc.id;
6807
- };
6808
- const selectedDoc = documents.find((d) => d.id === docValue);
6920
+ }, [getCollectionDisplayField]);
6921
+ const filteredDocs = React$1.useMemo(() => {
6922
+ if (!urlValue) return documents;
6923
+ const query = urlValue.toLowerCase();
6924
+ return documents.filter((doc) => {
6925
+ const display = getDocumentDisplay(doc, doc.__collectionSlug).toLowerCase();
6926
+ const slug = String(doc.slug || "").toLowerCase();
6927
+ return display.includes(query) || slug.includes(query);
6928
+ });
6929
+ }, [
6930
+ documents,
6931
+ urlValue,
6932
+ getDocumentDisplay
6933
+ ]);
6934
+ const selectedDoc = documents.find((d) => d.id === docValue && d.__collectionSlug === collectionValue);
6809
6935
  const selectedCollectionLabel = collections.find((c) => c.slug === collectionValue)?.labels?.singular || collectionValue;
6810
- return /* @__PURE__ */ jsx("div", {
6936
+ return /* @__PURE__ */ jsxs("div", {
6811
6937
  className: "dy-space-y-3",
6812
- children: /* @__PURE__ */ jsxs(Tabs, {
6813
- value: mode,
6814
- onValueChange: (val) => handleModeChange(val),
6815
- children: [
6816
- /* @__PURE__ */ jsxs(TabsList, {
6817
- className: "dy-w-full dy-grid dy-grid-cols-2 dy-bg-muted dy-p-1 dy-rounded-lg dy-h-auto",
6818
- children: [/* @__PURE__ */ jsx(TabsTrigger, {
6819
- value: "internal",
6820
- disabled,
6821
- className: "dy-rounded-md",
6822
- children: "Internal Link"
6823
- }), /* @__PURE__ */ jsx(TabsTrigger, {
6824
- value: "external",
6825
- disabled,
6826
- className: "dy-rounded-md",
6827
- children: "External Link"
6828
- })]
6829
- }),
6830
- /* @__PURE__ */ jsxs(TabsContent, {
6831
- value: "external",
6832
- className: "dy-space-y-3 dy-mt-3",
6833
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6834
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6835
- children: "URL"
6836
- }), /* @__PURE__ */ jsx(Input, {
6837
- type: "url",
6838
- value: urlValue,
6839
- onChange: (e) => handleExternalUrlChange(e.target.value),
6840
- placeholder: "https://example.com",
6841
- disabled,
6842
- className: "dy-border-border/50"
6843
- })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6844
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6845
- children: "Link Label (Optional)"
6846
- }), /* @__PURE__ */ jsx(Input, {
6938
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs(Popover, {
6939
+ open: openPopover && !disabled,
6940
+ onOpenChange: setOpenPopover,
6941
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
6942
+ asChild: true,
6943
+ children: /* @__PURE__ */ jsx("div", {
6944
+ className: "dy-w-full",
6945
+ children: /* @__PURE__ */ jsx(Input, {
6847
6946
  type: "text",
6848
- value: labelValue,
6849
- onChange: (e) => handleExternalLabelChange(e.target.value),
6850
- placeholder: "e.g., Learn More",
6947
+ value: urlValue,
6948
+ onChange: (e) => handleUrlInputChange(e.target.value),
6949
+ onFocus: () => setOpenPopover(true),
6950
+ placeholder: "Type or paste a URL, or search pages...",
6851
6951
  disabled,
6852
- className: "dy-border-border/50"
6853
- })] })]
6854
- }),
6855
- /* @__PURE__ */ jsxs(TabsContent, {
6856
- value: "internal",
6857
- className: "dy-space-y-3 dy-mt-3",
6858
- children: [
6859
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6860
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6861
- children: "Collection"
6862
- }), /* @__PURE__ */ jsxs(Select, {
6863
- value: collectionValue,
6864
- onValueChange: handleCollectionChange,
6865
- disabled,
6866
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
6867
- className: "dy-bg-background dy-border-border/50",
6868
- children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a collection..." })
6869
- }), /* @__PURE__ */ jsx(SelectContent, { children: collections.map((col) => /* @__PURE__ */ jsx(SelectItem, {
6870
- value: col.slug,
6871
- children: col.labels?.plural || col.slug
6872
- }, col.slug)) })]
6873
- })] }),
6874
- collectionValue && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6875
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6876
- children: "Document"
6877
- }), /* @__PURE__ */ jsxs(Popover, {
6878
- open: disabled ? false : openPopover,
6879
- onOpenChange: setOpenPopover,
6880
- children: [/* @__PURE__ */ jsx(PopoverTrigger, {
6881
- asChild: true,
6882
- children: /* @__PURE__ */ jsxs(Button, {
6883
- variant: "outline",
6884
- role: "combobox",
6885
- "aria-expanded": openPopover,
6886
- disabled,
6887
- className: "dy-w-full dy-justify-between dy-font-normal dy-bg-background dy-border-border/50 dy-h-11",
6888
- children: [docsLoading ? "Loading..." : selectedDoc ? /* @__PURE__ */ jsx("span", {
6889
- className: "dy-truncate",
6890
- children: getDocumentDisplay(selectedDoc, collectionValue)
6891
- }) : /* @__PURE__ */ jsx("span", {
6892
- className: "dy-text-muted-foreground",
6893
- children: "Select a document..."
6894
- }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "dy-ml-2 dy-h-4 dy-w-4 dy-shrink-0 dy-opacity-50" })]
6895
- })
6896
- }), /* @__PURE__ */ jsx(PopoverContent, {
6897
- className: "dy-w-full dy-p-0",
6898
- align: "start",
6899
- children: /* @__PURE__ */ jsxs(Command$1, { children: [/* @__PURE__ */ jsx(CommandInput, {
6900
- placeholder: `Search ${selectedCollectionLabel}...`,
6901
- value: docSearch,
6902
- onValueChange: setDocSearch
6903
- }), /* @__PURE__ */ jsxs(CommandList, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: docsLoading ? "Searching..." : "No documents found." }), /* @__PURE__ */ jsx(CommandGroup, { children: documents.filter((doc) => {
6904
- if (!docSearch) return true;
6905
- return getDocumentDisplay(doc, collectionValue).toLowerCase().includes(docSearch.toLowerCase());
6906
- }).map((doc) => /* @__PURE__ */ jsxs(CommandItem, {
6907
- value: doc.id,
6908
- onSelect: () => handleDocumentSelect(doc.id),
6909
- children: [/* @__PURE__ */ jsx(Check, { className: cn("dy-mr-2 dy-h-4 dy-w-4", docValue === doc.id ? "dy-opacity-100" : "dy-opacity-0") }), /* @__PURE__ */ jsx("span", {
6952
+ className: "dy-bg-background dy-border-border/50 dy-h-11 dy-w-full"
6953
+ })
6954
+ })
6955
+ }), /* @__PURE__ */ jsx(PopoverContent, {
6956
+ className: "dy-p-0 dy-w-[var(--radix-popover-trigger-width)] dy-max-h-60 dy-overflow-y-auto",
6957
+ align: "start",
6958
+ onOpenAutoFocus: (e) => e.preventDefault(),
6959
+ children: /* @__PURE__ */ jsx(Command$1, {
6960
+ className: "dy-border-none",
6961
+ shouldFilter: false,
6962
+ children: /* @__PURE__ */ jsxs(CommandList, { children: [
6963
+ /* @__PURE__ */ jsx(CommandEmpty, {
6964
+ className: "dy-py-2.5 dy-px-3 dy-text-xs dy-text-muted-foreground",
6965
+ children: docsLoading ? "Loading pages..." : "No matching pages found. Press Enter to use as custom link."
6966
+ }),
6967
+ urlValue && /* @__PURE__ */ jsx(CommandGroup, {
6968
+ heading: "Custom Link",
6969
+ children: /* @__PURE__ */ jsxs(CommandItem, {
6970
+ value: `custom-${urlValue}`,
6971
+ onSelect: () => {
6972
+ handleUrlInputChange(urlValue);
6973
+ setOpenPopover(false);
6974
+ },
6975
+ className: "dy-text-primary",
6976
+ children: [
6977
+ "Use link \"",
6978
+ urlValue,
6979
+ "\""
6980
+ ]
6981
+ })
6982
+ }),
6983
+ (() => {
6984
+ const grouped = {};
6985
+ filteredDocs.forEach((doc) => {
6986
+ const key = doc.__collectionLabel;
6987
+ if (!grouped[key]) grouped[key] = [];
6988
+ grouped[key].push(doc);
6989
+ });
6990
+ return Object.entries(grouped).map(([groupName, docs]) => /* @__PURE__ */ jsx(CommandGroup, {
6991
+ heading: groupName,
6992
+ children: docs.map((doc) => /* @__PURE__ */ jsxs(CommandItem, {
6993
+ value: `${doc.__collectionSlug}-${doc.id}-${getDocumentDisplay(doc, doc.__collectionSlug)}`,
6994
+ onSelect: () => handleDocumentSelect(doc),
6995
+ children: [/* @__PURE__ */ jsx(Check, { className: cn("dy-mr-2 dy-h-4 dy-w-4", docValue === doc.id && collectionValue === doc.__collectionSlug ? "dy-opacity-100" : "dy-opacity-0") }), /* @__PURE__ */ jsx("span", {
6910
6996
  className: "dy-flex-1",
6911
- children: getDocumentDisplay(doc, collectionValue)
6997
+ children: getDocumentDisplay(doc, doc.__collectionSlug)
6912
6998
  })]
6913
- }, doc.id)) })] })] })
6914
- })]
6915
- })] }),
6916
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6917
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6918
- children: "Link Label (Optional)"
6919
- }), /* @__PURE__ */ jsx(Input, {
6920
- type: "text",
6921
- value: labelValue,
6922
- onChange: (e) => handleInternalLabelChange(e.target.value),
6923
- placeholder: "e.g., Read More",
6924
- disabled,
6925
- className: "dy-bg-background dy-border-border/50"
6926
- })] })
6927
- ]
6999
+ }, `${doc.__collectionSlug}-${doc.id}`))
7000
+ }, groupName));
7001
+ })()
7002
+ ] })
6928
7003
  })
6929
- ]
6930
- })
7004
+ })]
7005
+ }), selectedDoc && /* @__PURE__ */ jsxs("p", {
7006
+ className: "dy-text-[11px] dy-text-muted-foreground dy-mt-1.5",
7007
+ children: ["Linked to internal page: ", /* @__PURE__ */ jsxs("span", {
7008
+ className: "dy-font-medium dy-text-foreground",
7009
+ children: [
7010
+ selectedCollectionLabel,
7011
+ ": ",
7012
+ getDocumentDisplay(selectedDoc, collectionValue)
7013
+ ]
7014
+ })]
7015
+ })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
7016
+ className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
7017
+ children: "Link Label (Optional)"
7018
+ }), /* @__PURE__ */ jsx(Input, {
7019
+ type: "text",
7020
+ value: labelValue,
7021
+ onChange: (e) => handleLabelInputChange(e.target.value),
7022
+ placeholder: "e.g., Learn More",
7023
+ disabled,
7024
+ className: "dy-bg-background dy-border-border/50"
7025
+ })] })]
6931
7026
  });
6932
7027
  }
6933
7028
  //#endregion
@@ -10084,8 +10179,25 @@ function CollectionListPage({ slug }) {
10084
10179
  }
10085
10180
  return true;
10086
10181
  };
10182
+ const getPreviewUrl = (item) => {
10183
+ if (!schema?.admin?.previewUrl) return null;
10184
+ let previewUrl = typeof schema.admin.previewUrl === "function" ? schema.admin.previewUrl(item, { locale: "en" }) : schema.admin.previewUrl;
10185
+ if (typeof previewUrl === "string" && previewUrl.includes("{{")) previewUrl = previewUrl.replace(/{{(.*?)}}/g, (_, key) => String(item[key.trim()] || ""));
10186
+ else if (typeof previewUrl === "string") try {
10187
+ const context = {
10188
+ ...item,
10189
+ siteUrl: window.location.origin
10190
+ };
10191
+ if (previewUrl.includes("+") || previewUrl.includes("?") || previewUrl.includes("==") || previewUrl.includes("siteUrl")) previewUrl = jexl.evalSync(previewUrl, context);
10192
+ } catch (e) {
10193
+ console.warn("Preview URL eval failed:", e);
10194
+ }
10195
+ if (typeof previewUrl === "string" && previewUrl.startsWith("/")) previewUrl = `${window.location.origin}${previewUrl}`;
10196
+ return typeof previewUrl === "string" ? previewUrl : null;
10197
+ };
10087
10198
  const renderLinkedCell = (item, cell) => {
10088
10199
  const canDelete = canDeleteRow(item);
10200
+ const previewUrl = getPreviewUrl(item);
10089
10201
  return /* @__PURE__ */ jsxs("div", {
10090
10202
  className: "dy-flex dy-flex-col dy-gap-1",
10091
10203
  children: [/* @__PURE__ */ jsx(Link, {
@@ -10095,6 +10207,16 @@ function CollectionListPage({ slug }) {
10095
10207
  }), /* @__PURE__ */ jsxs("div", {
10096
10208
  className: "dy-flex dy-items-center dy-gap-2",
10097
10209
  children: [
10210
+ previewUrl && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("a", {
10211
+ href: previewUrl,
10212
+ target: "_blank",
10213
+ rel: "noopener noreferrer",
10214
+ className: "dy-text-[11px] dy-text-muted-foreground hover:dy-text-foreground dy-underline-offset-2 hover:dy-underline dy-transition-colors dy-duration-150",
10215
+ children: "View"
10216
+ }), /* @__PURE__ */ jsx("span", {
10217
+ className: "dy-text-muted-foreground/40 dy-text-[11px]",
10218
+ children: "|"
10219
+ })] }),
10098
10220
  /* @__PURE__ */ jsx(Link, {
10099
10221
  to: `/collections/${slug}/edit/${String(item.id)}`,
10100
10222
  className: "dy-text-[11px] dy-text-muted-foreground hover:dy-text-foreground dy-underline-offset-2 hover:dy-underline dy-transition-colors dy-duration-150",
@@ -10119,6 +10241,22 @@ function CollectionListPage({ slug }) {
10119
10241
  accessorKey: field.name,
10120
10242
  header: field.label || field.name,
10121
10243
  cell: ({ row }) => {
10244
+ if (field.name === "url") {
10245
+ if (schema?.admin?.previewUrl) {
10246
+ const resolved = getPreviewUrl(row.original);
10247
+ if (resolved) return /* @__PURE__ */ jsx("a", {
10248
+ href: resolved,
10249
+ target: "_blank",
10250
+ rel: "noopener noreferrer",
10251
+ className: "dy-text-xs dy-text-primary hover:dy-underline dy-underline-offset-2 dy-font-medium",
10252
+ children: resolved
10253
+ });
10254
+ }
10255
+ return /* @__PURE__ */ jsx("span", {
10256
+ className: "dy-text-muted-foreground",
10257
+ children: "-"
10258
+ });
10259
+ }
10122
10260
  const cell = /* @__PURE__ */ jsx(RenderCell, {
10123
10261
  value: row.getValue(field.name),
10124
10262
  field,
@@ -12702,21 +12840,8 @@ function GlobalEditorPage() {
12702
12840
  });
12703
12841
  }
12704
12842
  //#endregion
12705
- //#region src/pages/setup/setup-prompt.tsx
12843
+ //#region src/pages/setup/utils.ts
12706
12844
  var GUIDE_URL = "https://www.dyrected.com/guide";
12707
- var DOCS_URL = "https://docs.dyrected.com";
12708
- var steps = [
12709
- "Paste the prompt into your AI builder — the same one that owns the website code.",
12710
- "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12711
- "Your Dyrected credentials are already in the prompt. Give them to the AI when it asks at Stage 4.",
12712
- "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12713
- ];
12714
- var stepsNoCredentials = [
12715
- "Paste the prompt into your AI builder — the same one that owns the website code.",
12716
- "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12717
- "When the AI reaches Stage 4, it will ask for your Site ID, Site API key, and Base URL.",
12718
- "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12719
- ];
12720
12845
  function normalizeTechStack(techStack) {
12721
12846
  if (!techStack) return void 0;
12722
12847
  if (techStack === "next") return "nextjs";
@@ -12749,6 +12874,21 @@ Wait for my reply.`;
12749
12874
  - Base URL: ${baseUrl}`;
12750
12875
  return GENERATE_CMS_PROMPT.replace(placeholder, replacement);
12751
12876
  }
12877
+ //#endregion
12878
+ //#region src/pages/setup/setup-prompt.tsx
12879
+ var DOCS_URL = "https://docs.dyrected.com";
12880
+ var steps = [
12881
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12882
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12883
+ "Your Dyrected credentials are already in the prompt. Give them to the AI when it asks at Stage 4.",
12884
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12885
+ ];
12886
+ var stepsNoCredentials = [
12887
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12888
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12889
+ "When the AI reaches Stage 4, it will ask for your Site ID, Site API key, and Base URL.",
12890
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12891
+ ];
12752
12892
  function SetupPromptUI({ config }) {
12753
12893
  const [copied, setCopied] = useState(false);
12754
12894
  const guideUrl = buildGuideUrl(config);
@@ -12760,9 +12900,87 @@ function SetupPromptUI({ config }) {
12760
12900
  setCopied(true);
12761
12901
  window.setTimeout(() => setCopied(false), 1800);
12762
12902
  }
12903
+ const currentVersion = "2.5.49";
12904
+ const [latestVersion, setLatestVersion] = useState(() => {
12905
+ if (typeof window === "undefined") return null;
12906
+ return localStorage.getItem("dyrected_latest_release");
12907
+ });
12908
+ useEffect(() => {
12909
+ if (!latestVersion) fetch("https://registry.npmjs.org/@dyrected/core/latest").then((r) => r.json()).then((data) => {
12910
+ if (data?.version) {
12911
+ setLatestVersion(data.version);
12912
+ localStorage.setItem("dyrected_latest_release", data.version);
12913
+ localStorage.setItem("dyrected_latest_release_timestamp", String(Date.now()));
12914
+ }
12915
+ }).catch(() => {});
12916
+ }, [latestVersion]);
12917
+ const hasUpdate = latestVersion && latestVersion !== currentVersion && (() => {
12918
+ const lParts = latestVersion.split(".").map(Number);
12919
+ const cParts = currentVersion.split(".").map(Number);
12920
+ for (let i = 0; i < 3; i++) {
12921
+ const l = lParts[i] || 0;
12922
+ const c = cParts[i] || 0;
12923
+ if (l > c) return true;
12924
+ if (l < c) return false;
12925
+ }
12926
+ return false;
12927
+ })();
12763
12928
  return /* @__PURE__ */ jsxs("div", {
12764
12929
  className: "dy-mx-auto dy-max-w-3xl dy-space-y-10 dy-px-4 dy-py-8",
12765
12930
  children: [
12931
+ /* @__PURE__ */ jsxs("div", {
12932
+ className: "dy-rounded-xl dy-border dy-border-border dy-bg-muted/30 dy-p-5 dy-space-y-3",
12933
+ children: [/* @__PURE__ */ jsx("h2", {
12934
+ className: "dy-text-sm dy-font-semibold dy-text-foreground",
12935
+ children: "System Info & Updates"
12936
+ }), /* @__PURE__ */ jsxs("div", {
12937
+ className: "dy-flex dy-flex-col sm:dy-flex-row sm:dy-items-center dy-justify-between dy-gap-4 dy-text-xs",
12938
+ children: [/* @__PURE__ */ jsxs("div", {
12939
+ className: "dy-space-y-1",
12940
+ children: [/* @__PURE__ */ jsxs("p", {
12941
+ className: "dy-text-muted-foreground",
12942
+ children: ["Current version: ", /* @__PURE__ */ jsxs("span", {
12943
+ className: "dy-font-mono dy-font-semibold dy-text-foreground",
12944
+ children: ["v", currentVersion]
12945
+ })]
12946
+ }), latestVersion && /* @__PURE__ */ jsxs("p", {
12947
+ className: "dy-text-muted-foreground",
12948
+ children: ["Latest release: ", /* @__PURE__ */ jsxs("span", {
12949
+ className: "dy-font-mono dy-font-semibold dy-text-foreground",
12950
+ children: ["v", latestVersion]
12951
+ })]
12952
+ })]
12953
+ }), hasUpdate ? /* @__PURE__ */ jsxs("div", {
12954
+ className: "dy-flex dy-flex-col dy-items-start sm:dy-items-end dy-gap-1.5",
12955
+ children: [/* @__PURE__ */ jsxs("div", {
12956
+ className: "dy-flex dy-items-center dy-gap-2",
12957
+ children: [/* @__PURE__ */ jsx("span", {
12958
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-rounded-full dy-bg-primary/10 dy-px-2.5 dy-py-0.5 dy-text-[11px] dy-font-medium dy-text-primary",
12959
+ children: "Update available!"
12960
+ }), /* @__PURE__ */ jsxs("a", {
12961
+ href: "https://github.com/Dyrected/dyrected/blob/main/CHANGELOG.md",
12962
+ target: "_blank",
12963
+ rel: "noopener noreferrer",
12964
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-text-[11px] dy-text-primary dy-underline-offset-4 hover:dy-underline",
12965
+ children: ["View changelog", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3 dy-w-3" })]
12966
+ })]
12967
+ }), /* @__PURE__ */ jsxs("p", {
12968
+ className: "dy-text-muted-foreground",
12969
+ children: [
12970
+ "Run ",
12971
+ /* @__PURE__ */ jsx("code", {
12972
+ className: "dy-bg-black/5 dark:dy-bg-white/5 dy-px-1 dy-py-0.5 dy-rounded dy-font-mono",
12973
+ children: "npx dyrected upgrade"
12974
+ }),
12975
+ " to update."
12976
+ ]
12977
+ })]
12978
+ }) : /* @__PURE__ */ jsx("span", {
12979
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-rounded-full dy-bg-green-500/10 dy-px-2.5 dy-py-0.5 dy-text-[11px] dy-font-medium dy-text-green-600 dark:dy-text-green-400",
12980
+ children: "Up to date"
12981
+ })]
12982
+ })]
12983
+ }),
12766
12984
  /* @__PURE__ */ jsxs("div", {
12767
12985
  className: "dy-space-y-2",
12768
12986
  children: [/* @__PURE__ */ jsx("h1", {
@@ -10,5 +10,4 @@ export interface SetupPromptConfig {
10
10
  export interface SetupPromptProps {
11
11
  config: SetupPromptConfig;
12
12
  }
13
- export declare function buildGuideUrl(config: SetupPromptConfig): string;
14
13
  export declare function SetupPromptUI({ config }: SetupPromptProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { SetupPromptConfig } from './setup-prompt';
2
+ export declare function normalizeTechStack(techStack?: string): string | undefined;
3
+ export declare function buildGuideUrl(config: SetupPromptConfig): string;
4
+ export declare function buildPrompt(config: SetupPromptConfig): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/admin",
3
- "version": "2.5.48",
3
+ "version": "2.5.49",
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.48",
68
- "@dyrected/knowledge": "^0.2.5",
69
- "@dyrected/sdk": "^2.5.48"
67
+ "@dyrected/knowledge": "^0.2.7",
68
+ "@dyrected/sdk": "^2.5.49",
69
+ "@dyrected/core": "^2.5.49"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@tanstack/react-query": "^5.0.0",