litecms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1387 -0
  3. package/dist/admin/CmsAdminLayout.d.ts +27 -0
  4. package/dist/admin/CmsAdminLayout.d.ts.map +1 -0
  5. package/dist/admin/CmsAdminPage.d.ts +31 -0
  6. package/dist/admin/CmsAdminPage.d.ts.map +1 -0
  7. package/dist/admin/config.d.ts +83 -0
  8. package/dist/admin/config.d.ts.map +1 -0
  9. package/dist/admin/config.js +53 -0
  10. package/dist/admin/exports.d.ts +7 -0
  11. package/dist/admin/exports.d.ts.map +1 -0
  12. package/dist/admin/exports.js +452 -0
  13. package/dist/admin/index.d.ts +147 -0
  14. package/dist/admin/index.d.ts.map +1 -0
  15. package/dist/components/CmsAutoForm.d.ts +73 -0
  16. package/dist/components/CmsAutoForm.d.ts.map +1 -0
  17. package/dist/components/CmsField.d.ts +50 -0
  18. package/dist/components/CmsField.d.ts.map +1 -0
  19. package/dist/components/CmsForm.d.ts +74 -0
  20. package/dist/components/CmsForm.d.ts.map +1 -0
  21. package/dist/components/CmsImageField.d.ts +33 -0
  22. package/dist/components/CmsImageField.d.ts.map +1 -0
  23. package/dist/components/CmsNavSection.d.ts +7 -0
  24. package/dist/components/CmsNavSection.d.ts.map +1 -0
  25. package/dist/components/CmsSimpleForm.d.ts +54 -0
  26. package/dist/components/CmsSimpleForm.d.ts.map +1 -0
  27. package/dist/components/index.d.ts +43 -0
  28. package/dist/components/index.d.ts.map +1 -0
  29. package/dist/components/index.js +619 -0
  30. package/dist/domain/index.d.ts +1 -0
  31. package/dist/domain/index.d.ts.map +1 -0
  32. package/dist/index-8zcd33mx.js +39 -0
  33. package/dist/index-pmb5m3ek.js +4135 -0
  34. package/dist/index.d.ts +4 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +32 -0
  37. package/dist/schema/index.d.ts +80 -0
  38. package/dist/schema/index.d.ts.map +1 -0
  39. package/dist/schema/index.js +46 -0
  40. package/dist/server/index.d.ts +79 -0
  41. package/dist/server/index.d.ts.map +1 -0
  42. package/dist/server/index.js +117 -0
  43. package/dist/shared/utils.d.ts +23 -0
  44. package/dist/shared/utils.d.ts.map +1 -0
  45. package/dist/storage/index.d.ts +86 -0
  46. package/dist/storage/index.d.ts.map +1 -0
  47. package/dist/storage/index.js +86 -0
  48. package/dist/stores/index.d.ts +1 -0
  49. package/dist/stores/index.d.ts.map +1 -0
  50. package/package.json +90 -0
@@ -0,0 +1,27 @@
1
+ import type { NavPageInfo } from './index';
2
+ /**
3
+ * Serializable layout props
4
+ */
5
+ export type CmsAdminLayoutProps = {
6
+ /** Admin panel title */
7
+ adminTitle?: string;
8
+ /** Site name */
9
+ siteName?: string;
10
+ /** Base path for admin routes */
11
+ basePath?: string;
12
+ /** Link to public site */
13
+ publicSiteUrl?: string;
14
+ /** Navigation pages (serializable) */
15
+ pages: NavPageInfo[];
16
+ /** Current page slug. Auto-detected if not provided. */
17
+ currentSlug?: string;
18
+ /** Logout handler */
19
+ onLogout?: () => void;
20
+ /** Child content */
21
+ children?: any;
22
+ };
23
+ /**
24
+ * General admin layout with navigation and content.
25
+ */
26
+ export declare function CmsAdminLayout({ adminTitle, siteName, basePath, publicSiteUrl, pages, currentSlug: currentSlugProp, onLogout, children, }: CmsAdminLayoutProps): import("react/jsx-runtime").JSX.Element;
27
+ //# sourceMappingURL=CmsAdminLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CmsAdminLayout.d.ts","sourceRoot":"","sources":["../../src/admin/CmsAdminLayout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,SAAS,CAAC;AAMtD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,oBAAoB;IAEpB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC3B,UAAsB,EACtB,QAAQ,EACR,QAAmB,EACnB,aAAa,EACb,KAAK,EACL,WAAW,EAAE,eAAe,EAC5B,QAAQ,EACR,QAAQ,GACX,EAAE,mBAAmB,2CAyGrB"}
@@ -0,0 +1,31 @@
1
+ import type { ActionState } from '../server';
2
+ import type { FieldInfo } from '../schema';
3
+ import { type CmsAutoFormStyles, type ImageUploadConfig } from '../components/CmsSimpleForm';
4
+ /**
5
+ * Serializable page props for client component
6
+ */
7
+ export type CmsAdminPageProps = {
8
+ /** Page title */
9
+ title: string;
10
+ /** Page description - helps guide the user */
11
+ description?: string;
12
+ /** Pre-extracted editable fields (serializable) */
13
+ fields: FieldInfo[];
14
+ /** Default/current values (serializable) */
15
+ values: Record<string, unknown>;
16
+ /** Server action to save data */
17
+ action: (prevState: ActionState<any>, formData: FormData) => Promise<ActionState<any>>;
18
+ /** Custom form styles */
19
+ styles?: CmsAutoFormStyles;
20
+ /** Custom success message */
21
+ successMessage?: string;
22
+ /** Custom submit button text */
23
+ submitText?: string;
24
+ /** Configuration for image upload endpoints */
25
+ storage?: ImageUploadConfig;
26
+ };
27
+ /**
28
+ * Admin page with auto-generated form.
29
+ */
30
+ export declare function CmsAdminPage({ title, description, fields, values, action, styles, successMessage, submitText, storage, }: CmsAdminPageProps): import("react/jsx-runtime").JSX.Element;
31
+ //# sourceMappingURL=CmsAdminPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CmsAdminPage.d.ts","sourceRoot":"","sources":["../../src/admin/CmsAdminPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACzB,MAAM,6BAA6B,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,iCAAiC;IAEjC,MAAM,EAAE,CACJ,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,QAAQ,KACjB,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,yBAAyB;IACzB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,6BAA6B;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,WAAW,EACX,MAAM,EACN,MAAM,EACN,MAAM,EACN,MAAM,EACN,cAAc,EACd,UAAU,EACV,OAAO,GACV,EAAE,iBAAiB,2CAgCnB"}
@@ -0,0 +1,83 @@
1
+ import { type FieldInfo } from '../schema';
2
+ import type { ActionState } from '../server';
3
+ import type { CmsAdminLayoutProps } from './CmsAdminLayout';
4
+ import type { ImageUploadConfig } from '../index';
5
+ export { createCmsConfig, getCmsConfig, getPageBySlug, getNavPages, definePage, type CmsConfig, type CmsPageConfig, type TypedCmsPageConfig, } from './index';
6
+ export type { FieldInfo } from '../schema';
7
+ export type { NavPageInfo } from './index';
8
+ /**
9
+ * Serializable props for CmsAdminPage
10
+ */
11
+ export type AdminPageProps = {
12
+ title: string;
13
+ /** Optional description to guide the user */
14
+ description?: string;
15
+ fields: FieldInfo[];
16
+ values: Record<string, unknown>;
17
+ action: (prevState: ActionState<any>, formData: FormData) => Promise<ActionState<any>>;
18
+ storage?: ImageUploadConfig;
19
+ };
20
+ /**
21
+ * Extract serializable props from a page config.
22
+ * Use this in server components to prepare data for CmsAdminPage.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import { extractPageProps } from 'litecms/admin/config';
27
+ * import { CmsAdminPage } from 'litecms/admin';
28
+ * import { cmsConfig } from '@/cms/config';
29
+ *
30
+ * export default async function AdminPage({ params }) {
31
+ * const props = await extractPageProps(cmsConfig, params.slug);
32
+ * if (!props) return notFound();
33
+ * return <CmsAdminPage {...props} />;
34
+ * }
35
+ * ```
36
+ */
37
+ export declare function extractPageProps(config: {
38
+ pages: Array<{
39
+ slug: string;
40
+ title: string;
41
+ description?: string;
42
+ definition: unknown;
43
+ action: unknown;
44
+ getData: () => Promise<unknown>;
45
+ }>;
46
+ storage?: ImageUploadConfig;
47
+ }, slug: string): Promise<AdminPageProps | null>;
48
+ /**
49
+ * Serializable props for CmsAdminLayout
50
+ */
51
+ export type LayoutProps = Omit<CmsAdminLayoutProps, 'children' | 'currentSlug'>;
52
+ /**
53
+ * Extract serializable layout props from CMS config.
54
+ * Use this to pass only serializable data to CmsAdminLayout.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * import { extractLayoutProps } from 'litecms/admin/config';
59
+ * import { CmsAdminLayout } from 'litecms/admin';
60
+ * import { cmsConfig } from '@/cms/config';
61
+ *
62
+ * export default function AdminLayout({ children }) {
63
+ * return (
64
+ * <CmsAdminLayout {...extractLayoutProps(cmsConfig)}>
65
+ * {children}
66
+ * </CmsAdminLayout>
67
+ * );
68
+ * }
69
+ * ```
70
+ */
71
+ export declare function extractLayoutProps(config: {
72
+ adminTitle?: string;
73
+ siteName?: string;
74
+ basePath?: string;
75
+ publicSiteUrl?: string;
76
+ pages: Array<{
77
+ slug: string;
78
+ title: string;
79
+ group?: string;
80
+ icon?: string;
81
+ }>;
82
+ }): LayoutProps;
83
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/admin/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,EACH,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,kBAAkB,GAC1B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACvF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAClC,MAAM,EAAE;IACJ,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3I,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,EACD,IAAI,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAuBhC;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,aAAa,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChF,GACF,WAAW,CAab"}
@@ -0,0 +1,53 @@
1
+ import {
2
+ getEditableFields
3
+ } from "../schema/index.js";
4
+ import {
5
+ createCmsConfig,
6
+ definePage,
7
+ getCmsConfig,
8
+ getNavPages,
9
+ getPageBySlug
10
+ } from "../index-8zcd33mx.js";
11
+
12
+ // src/admin/config.ts
13
+ async function extractPageProps(config, slug) {
14
+ const page = config.pages.find((p) => p.slug === slug);
15
+ if (!page)
16
+ return null;
17
+ const fields = getEditableFields(page.definition);
18
+ const data = await page.getData();
19
+ const definition = page.definition;
20
+ const dataObj = data ?? {};
21
+ const values = { ...definition.defaults, ...dataObj };
22
+ return {
23
+ title: page.title,
24
+ description: page.description,
25
+ fields,
26
+ values,
27
+ action: page.action,
28
+ storage: config.storage
29
+ };
30
+ }
31
+ function extractLayoutProps(config) {
32
+ return {
33
+ adminTitle: config.adminTitle,
34
+ siteName: config.siteName,
35
+ basePath: config.basePath,
36
+ publicSiteUrl: config.publicSiteUrl,
37
+ pages: config.pages.map((p) => ({
38
+ slug: p.slug,
39
+ title: p.title,
40
+ group: p.group,
41
+ icon: p.icon
42
+ }))
43
+ };
44
+ }
45
+ export {
46
+ getPageBySlug,
47
+ getNavPages,
48
+ getCmsConfig,
49
+ extractPageProps,
50
+ extractLayoutProps,
51
+ definePage,
52
+ createCmsConfig
53
+ };
@@ -0,0 +1,7 @@
1
+ export { CmsAdminLayout, type CmsAdminLayoutProps, } from './CmsAdminLayout';
2
+ export { CmsAdminPage, type CmsAdminPageProps, } from './CmsAdminPage';
3
+ export { createCmsConfig, getCmsConfig, getPageBySlug, getNavPages, definePage, type CmsConfig, type CmsPageConfig, type TypedCmsPageConfig, } from './index';
4
+ export { NavPageInfo, PageGroup } from './index';
5
+ export { type StorageConfig, } from '../storage';
6
+ export { type ImageUploadConfig, } from '../components';
7
+ //# sourceMappingURL=exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../src/admin/exports.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,cAAc,EACd,KAAK,mBAAmB,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACH,YAAY,EACZ,KAAK,iBAAiB,GACzB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACH,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,kBAAkB,GAC1B,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,WAAW,EACX,SAAS,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,KAAK,aAAa,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,KAAK,iBAAiB,GACzB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,452 @@
1
+ "use client";
2
+ import {
3
+ isDevelopmentEnv
4
+ } from "../server/index.js";
5
+ import {
6
+ CmsCheckbox,
7
+ CmsField,
8
+ CmsImageField,
9
+ FormProvider,
10
+ flattenValue,
11
+ groupFields,
12
+ groupPages,
13
+ useForm
14
+ } from "../index-pmb5m3ek.js";
15
+ import {
16
+ createCmsConfig,
17
+ definePage,
18
+ getCmsConfig,
19
+ getNavPages,
20
+ getPageBySlug
21
+ } from "../index-8zcd33mx.js";
22
+
23
+ // src/admin/CmsAdminLayout.tsx
24
+ import { usePathname } from "next/navigation";
25
+
26
+ // src/components/CmsNavSection.tsx
27
+ import { jsxDEV } from "react/jsx-dev-runtime";
28
+ function NavSection({
29
+ group,
30
+ basePath,
31
+ currentSlug
32
+ }) {
33
+ return /* @__PURE__ */ jsxDEV("div", {
34
+ children: [
35
+ /* @__PURE__ */ jsxDEV("div", {
36
+ className: "text-[10px] font-medium uppercase tracking-widest text-neutral-400 mb-4",
37
+ children: group.name ?? "Pages"
38
+ }, undefined, false, undefined, this),
39
+ /* @__PURE__ */ jsxDEV("div", {
40
+ className: "space-y-1",
41
+ children: group.pages.map((page) => /* @__PURE__ */ jsxDEV("a", {
42
+ href: `${basePath}/${page.slug}`,
43
+ className: `block py-1.5 text-sm transition-colors ${currentSlug === page.slug ? "text-neutral-900" : "text-neutral-400 hover:text-neutral-900"}`,
44
+ "aria-current": currentSlug === page.slug ? "page" : undefined,
45
+ children: page.title
46
+ }, page.slug, false, undefined, this))
47
+ }, undefined, false, undefined, this)
48
+ ]
49
+ }, undefined, true, undefined, this);
50
+ }
51
+
52
+ // src/admin/CmsAdminLayout.tsx
53
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
54
+ function CmsAdminLayout({
55
+ adminTitle = "Content",
56
+ siteName,
57
+ basePath = "/admin",
58
+ publicSiteUrl,
59
+ pages,
60
+ currentSlug: currentSlugProp,
61
+ onLogout,
62
+ children
63
+ }) {
64
+ const pathname = usePathname();
65
+ const detectedSlug = pathname?.replace(new RegExp(`^${basePath}/?`), "").split("/")[0];
66
+ const currentSlug = currentSlugProp ?? (detectedSlug || undefined);
67
+ const groupedPages = groupPages(pages);
68
+ return /* @__PURE__ */ jsxDEV2("div", {
69
+ className: "min-h-screen font-sans antialiased bg-neutral-50 text-neutral-900 selection:bg-neutral-200 selection:text-neutral-900 [--cms-accent:#171717] [--cms-accent-subtle:rgba(23,23,23,0.08)]",
70
+ children: [
71
+ /* @__PURE__ */ jsxDEV2("aside", {
72
+ className: "fixed inset-y-0 left-0 z-50 hidden w-56 flex-col border-r border-neutral-200 bg-white md:flex",
73
+ children: [
74
+ /* @__PURE__ */ jsxDEV2("div", {
75
+ className: "flex h-16 items-center px-6 border-b border-neutral-200",
76
+ children: /* @__PURE__ */ jsxDEV2("a", {
77
+ href: basePath,
78
+ className: "text-xs tracking-widest uppercase text-neutral-900 hover:text-neutral-600 transition-colors",
79
+ children: adminTitle
80
+ }, undefined, false, undefined, this)
81
+ }, undefined, false, undefined, this),
82
+ /* @__PURE__ */ jsxDEV2("nav", {
83
+ className: "flex-1 overflow-y-auto px-6 py-8 space-y-8",
84
+ children: groupedPages.map((group) => /* @__PURE__ */ jsxDEV2(NavSection, {
85
+ group,
86
+ basePath,
87
+ currentSlug
88
+ }, group.name ?? "__default", false, undefined, this))
89
+ }, undefined, false, undefined, this),
90
+ /* @__PURE__ */ jsxDEV2("div", {
91
+ className: "border-t border-neutral-200 px-6 py-4 space-y-3",
92
+ children: [
93
+ publicSiteUrl && /* @__PURE__ */ jsxDEV2("a", {
94
+ href: publicSiteUrl,
95
+ className: "flex items-center gap-2 text-xs tracking-wide text-neutral-400 hover:text-neutral-900 transition-colors",
96
+ target: "_blank",
97
+ rel: "noopener noreferrer",
98
+ children: [
99
+ /* @__PURE__ */ jsxDEV2("span", {
100
+ children: "View site"
101
+ }, undefined, false, undefined, this),
102
+ /* @__PURE__ */ jsxDEV2("svg", {
103
+ className: "h-3 w-3",
104
+ viewBox: "0 0 24 24",
105
+ fill: "none",
106
+ stroke: "currentColor",
107
+ strokeWidth: "1.5",
108
+ children: /* @__PURE__ */ jsxDEV2("path", {
109
+ strokeLinecap: "round",
110
+ strokeLinejoin: "round",
111
+ d: "M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25"
112
+ }, undefined, false, undefined, this)
113
+ }, undefined, false, undefined, this)
114
+ ]
115
+ }, undefined, true, undefined, this),
116
+ onLogout && /* @__PURE__ */ jsxDEV2("button", {
117
+ onClick: onLogout,
118
+ className: "text-xs tracking-wide text-neutral-400 hover:text-neutral-900 transition-colors cursor-pointer",
119
+ children: "Sign out"
120
+ }, undefined, false, undefined, this)
121
+ ]
122
+ }, undefined, true, undefined, this)
123
+ ]
124
+ }, undefined, true, undefined, this),
125
+ /* @__PURE__ */ jsxDEV2("header", {
126
+ className: "sticky top-0 z-40 flex h-14 items-center gap-4 px-4 bg-white border-b border-neutral-200 md:hidden",
127
+ children: /* @__PURE__ */ jsxDEV2("a", {
128
+ href: basePath,
129
+ className: "text-xs tracking-widest uppercase text-neutral-900",
130
+ children: adminTitle
131
+ }, undefined, false, undefined, this)
132
+ }, undefined, false, undefined, this),
133
+ /* @__PURE__ */ jsxDEV2("nav", {
134
+ className: "flex gap-4 overflow-x-auto px-4 py-3 bg-white border-b border-neutral-200 md:hidden",
135
+ children: pages.map((page) => /* @__PURE__ */ jsxDEV2("a", {
136
+ href: `${basePath}/${page.slug}`,
137
+ className: `shrink-0 text-sm transition-colors ${currentSlug === page.slug ? "text-neutral-900 border-b border-neutral-900 pb-1" : "text-neutral-400 hover:text-neutral-900"}`,
138
+ children: page.title
139
+ }, page.slug, false, undefined, this))
140
+ }, undefined, false, undefined, this),
141
+ /* @__PURE__ */ jsxDEV2("main", {
142
+ className: "md:ml-56 min-h-screen bg-white",
143
+ children: /* @__PURE__ */ jsxDEV2("div", {
144
+ className: "mx-auto",
145
+ children
146
+ }, undefined, false, undefined, this)
147
+ }, undefined, false, undefined, this)
148
+ ]
149
+ }, undefined, true, undefined, this);
150
+ }
151
+ // src/components/CmsSimpleForm.tsx
152
+ import * as React from "react";
153
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
154
+ function CmsSimpleForm({
155
+ fields,
156
+ action,
157
+ values = {},
158
+ styles = {},
159
+ submitText = "Save",
160
+ submitPendingText = "Saving...",
161
+ successMessage = "Changes saved",
162
+ storage
163
+ }) {
164
+ const [state, setState] = React.useState({
165
+ success: false
166
+ });
167
+ const [isPending, setIsPending] = React.useState(false);
168
+ const [showSuccess, setShowSuccess] = React.useState(false);
169
+ const form = useForm({
170
+ defaultValues: values
171
+ });
172
+ const hiddenFieldEntries = React.useMemo(() => {
173
+ const editableFieldNames = new Set(fields.map((f) => f.name));
174
+ const entries = [];
175
+ for (const [key, value] of Object.entries(values)) {
176
+ if (!editableFieldNames.has(key)) {
177
+ flattenValue(key, value, entries);
178
+ }
179
+ }
180
+ return entries;
181
+ }, [fields, values]);
182
+ React.useEffect(() => {
183
+ if (state.success) {
184
+ setShowSuccess(true);
185
+ const timer = setTimeout(() => setShowSuccess(false), 4000);
186
+ return () => clearTimeout(timer);
187
+ } else if (state.errors?.fieldErrors) {
188
+ setShowSuccess(false);
189
+ for (const [field, messages] of Object.entries(state.errors.fieldErrors)) {
190
+ if (messages && messages[0]) {
191
+ form.setError(field, {
192
+ type: "server",
193
+ message: messages[0]
194
+ });
195
+ }
196
+ }
197
+ }
198
+ }, [state, form]);
199
+ const handleSubmit = async (event) => {
200
+ event.preventDefault();
201
+ setIsPending(true);
202
+ setShowSuccess(false);
203
+ try {
204
+ const formData = new FormData(event.currentTarget);
205
+ if (typeof window !== "undefined" && isDevelopmentEnv) {
206
+ console.log("[litecms] Submitting form data:", Object.fromEntries(formData.entries()));
207
+ }
208
+ const result = await action(state, formData);
209
+ if (typeof window !== "undefined" && isDevelopmentEnv) {
210
+ console.log("[litecms] Action result:", result);
211
+ }
212
+ if (!result) {
213
+ console.error("[litecms] Action returned null/undefined");
214
+ setState({
215
+ success: false,
216
+ errors: {
217
+ formError: "Server action returned no response."
218
+ }
219
+ });
220
+ return;
221
+ }
222
+ setState(result);
223
+ } catch (error) {
224
+ console.error("[litecms] Form submission error:", error);
225
+ setState({
226
+ success: false,
227
+ errors: {
228
+ formError: error instanceof Error ? `Error: ${error.message}` : "Something went wrong."
229
+ }
230
+ });
231
+ } finally {
232
+ setIsPending(false);
233
+ }
234
+ };
235
+ const groupedFields = groupFields(fields);
236
+ return /* @__PURE__ */ jsxDEV3(FormProvider, {
237
+ ...form,
238
+ children: /* @__PURE__ */ jsxDEV3("form", {
239
+ onSubmit: handleSubmit,
240
+ children: [
241
+ hiddenFieldEntries.map(([name, value]) => /* @__PURE__ */ jsxDEV3("input", {
242
+ type: "hidden",
243
+ name,
244
+ value
245
+ }, name, false, undefined, this)),
246
+ /* @__PURE__ */ jsxDEV3("div", {
247
+ className: styles.wrapper ?? "",
248
+ children: [
249
+ /* @__PURE__ */ jsxDEV3("div", {
250
+ className: "space-y-16",
251
+ children: groupedFields.map((group, groupIndex) => /* @__PURE__ */ jsxDEV3(FieldGroup, {
252
+ group,
253
+ styles,
254
+ storage
255
+ }, group.name ?? `__ungrouped-${groupIndex}`, false, undefined, this))
256
+ }, undefined, false, undefined, this),
257
+ state.errors?.formError && /* @__PURE__ */ jsxDEV3("div", {
258
+ className: "mt-12 py-4 px-5 border border-red-200 bg-red-50 text-red-900 text-sm",
259
+ children: state.errors.formError
260
+ }, undefined, false, undefined, this),
261
+ showSuccess && /* @__PURE__ */ jsxDEV3("div", {
262
+ className: "mt-12 py-4 px-5 border border-green-200 bg-green-50 text-green-900 text-sm",
263
+ children: successMessage
264
+ }, undefined, false, undefined, this),
265
+ /* @__PURE__ */ jsxDEV3("div", {
266
+ className: "mt-16 pt-8 border-t border-neutral-200 flex items-center justify-between",
267
+ children: [
268
+ /* @__PURE__ */ jsxDEV3("button", {
269
+ type: "button",
270
+ className: "text-sm text-neutral-400 hover:text-neutral-900 transition-colors cursor-pointer",
271
+ onClick: () => form.reset(),
272
+ disabled: isPending,
273
+ children: "Reset"
274
+ }, undefined, false, undefined, this),
275
+ /* @__PURE__ */ jsxDEV3("button", {
276
+ type: "submit",
277
+ disabled: isPending,
278
+ className: styles.submitButton ?? "inline-flex items-center gap-2 text-sm tracking-wide text-neutral-900 border-b border-neutral-900 pb-1 hover:pb-1.5 transition-all disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer",
279
+ children: [
280
+ isPending && /* @__PURE__ */ jsxDEV3("svg", {
281
+ className: "h-3.5 w-3.5 animate-spin",
282
+ viewBox: "0 0 24 24",
283
+ fill: "none",
284
+ stroke: "currentColor",
285
+ strokeWidth: "2",
286
+ children: /* @__PURE__ */ jsxDEV3("path", {
287
+ d: "M21 12a9 9 0 1 1-6.219-8.56"
288
+ }, undefined, false, undefined, this)
289
+ }, undefined, false, undefined, this),
290
+ isPending ? submitPendingText : submitText,
291
+ !isPending && /* @__PURE__ */ jsxDEV3("span", {
292
+ children: "→"
293
+ }, undefined, false, undefined, this)
294
+ ]
295
+ }, undefined, true, undefined, this)
296
+ ]
297
+ }, undefined, true, undefined, this)
298
+ ]
299
+ }, undefined, true, undefined, this)
300
+ ]
301
+ }, undefined, true, undefined, this)
302
+ }, undefined, false, undefined, this);
303
+ }
304
+ function FieldGroup({
305
+ group,
306
+ styles,
307
+ storage
308
+ }) {
309
+ if (group.name) {
310
+ return /* @__PURE__ */ jsxDEV3("fieldset", {
311
+ className: "space-y-8",
312
+ children: [
313
+ /* @__PURE__ */ jsxDEV3("legend", {
314
+ className: "text-xs font-medium uppercase tracking-widest text-neutral-400 pb-4 border-b border-neutral-200 w-full",
315
+ children: group.name
316
+ }, undefined, false, undefined, this),
317
+ /* @__PURE__ */ jsxDEV3("div", {
318
+ className: "grid grid-cols-1 md:grid-cols-2 gap-8",
319
+ children: group.fields.map((field) => /* @__PURE__ */ jsxDEV3(AutoField, {
320
+ field,
321
+ styles,
322
+ storage
323
+ }, field.name, false, undefined, this))
324
+ }, undefined, false, undefined, this)
325
+ ]
326
+ }, undefined, true, undefined, this);
327
+ }
328
+ return /* @__PURE__ */ jsxDEV3("div", {
329
+ className: "grid grid-cols-1 md:grid-cols-2 gap-8",
330
+ children: group.fields.map((field) => /* @__PURE__ */ jsxDEV3(AutoField, {
331
+ field,
332
+ styles,
333
+ storage
334
+ }, field.name, false, undefined, this))
335
+ }, undefined, false, undefined, this);
336
+ }
337
+ function AutoField({
338
+ field,
339
+ styles,
340
+ storage
341
+ }) {
342
+ const { name, meta, required } = field;
343
+ const isFullWidth = meta.type === "textarea" || meta.type === "image";
344
+ const colSpan = isFullWidth ? "col-span-1 md:col-span-2" : "col-span-1";
345
+ if (meta.type === "checkbox") {
346
+ return /* @__PURE__ */ jsxDEV3("div", {
347
+ className: colSpan,
348
+ children: /* @__PURE__ */ jsxDEV3(CmsCheckbox, {
349
+ name,
350
+ label: meta.label,
351
+ helpText: meta.helpText,
352
+ className: styles.field,
353
+ labelClassName: styles.label,
354
+ inputClassName: styles.input,
355
+ errorClassName: styles.error,
356
+ helpClassName: styles.help
357
+ }, undefined, false, undefined, this)
358
+ }, undefined, false, undefined, this);
359
+ }
360
+ if (meta.type === "image") {
361
+ return /* @__PURE__ */ jsxDEV3("div", {
362
+ className: colSpan,
363
+ children: /* @__PURE__ */ jsxDEV3(CmsImageField, {
364
+ name,
365
+ label: meta.label,
366
+ helpText: meta.helpText,
367
+ required,
368
+ accept: meta.accept,
369
+ className: styles.field,
370
+ labelClassName: styles.label,
371
+ errorClassName: styles.error,
372
+ helpClassName: styles.help,
373
+ storage
374
+ }, undefined, false, undefined, this)
375
+ }, undefined, false, undefined, this);
376
+ }
377
+ return /* @__PURE__ */ jsxDEV3("div", {
378
+ className: colSpan,
379
+ children: /* @__PURE__ */ jsxDEV3(CmsField, {
380
+ name,
381
+ label: meta.label,
382
+ type: meta.type ?? "text",
383
+ placeholder: meta.placeholder,
384
+ helpText: meta.helpText,
385
+ options: meta.options,
386
+ rows: meta.rows,
387
+ required,
388
+ className: styles.field,
389
+ labelClassName: styles.label,
390
+ inputClassName: styles.input,
391
+ errorClassName: styles.error,
392
+ helpClassName: styles.help
393
+ }, undefined, false, undefined, this)
394
+ }, undefined, false, undefined, this);
395
+ }
396
+
397
+ // src/admin/CmsAdminPage.tsx
398
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
399
+ function CmsAdminPage({
400
+ title,
401
+ description,
402
+ fields,
403
+ values,
404
+ action,
405
+ styles,
406
+ successMessage,
407
+ submitText,
408
+ storage
409
+ }) {
410
+ return /* @__PURE__ */ jsxDEV4("div", {
411
+ className: "space-y-12",
412
+ children: [
413
+ /* @__PURE__ */ jsxDEV4("header", {
414
+ className: "border-b border-neutral-200 h-16 px-8 flex items-center justify-between",
415
+ children: [
416
+ /* @__PURE__ */ jsxDEV4("h1", {
417
+ className: "text-2xl font-light tracking-tight text-neutral-900",
418
+ children: title
419
+ }, undefined, false, undefined, this),
420
+ description && /* @__PURE__ */ jsxDEV4("p", {
421
+ className: "mt-3 text-sm text-neutral-500 max-w-xl leading-relaxed",
422
+ children: description
423
+ }, undefined, false, undefined, this)
424
+ ]
425
+ }, undefined, true, undefined, this),
426
+ /* @__PURE__ */ jsxDEV4("div", {
427
+ className: "px-8 pb-12",
428
+ children: /* @__PURE__ */ jsxDEV4(CmsSimpleForm, {
429
+ fields,
430
+ action,
431
+ values,
432
+ styles,
433
+ successMessage: successMessage ?? "Changes saved successfully.",
434
+ submitText: submitText ?? "Save",
435
+ submitPendingText: "Saving...",
436
+ storage
437
+ }, undefined, false, undefined, this)
438
+ }, undefined, false, undefined, this)
439
+ ]
440
+ }, undefined, true, undefined, this);
441
+ }
442
+
443
+ // src/admin/exports.ts
444
+ export {
445
+ getPageBySlug,
446
+ getNavPages,
447
+ getCmsConfig,
448
+ definePage,
449
+ createCmsConfig,
450
+ CmsAdminPage,
451
+ CmsAdminLayout
452
+ };