@dalgoridim/headless-cms 0.3.1 → 0.5.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.
@@ -1,4 +1,35 @@
1
1
  "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __objRest = (source, exclude) => {
22
+ var target = {};
23
+ for (var prop in source)
24
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
+ target[prop] = source[prop];
26
+ if (source != null && __getOwnPropSymbols)
27
+ for (var prop of __getOwnPropSymbols(source)) {
28
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
+ target[prop] = source[prop];
30
+ }
31
+ return target;
32
+ };
2
33
 
3
34
  // src/auth/google/client/index.tsx
4
35
  import {
@@ -9,9 +40,13 @@ import {
9
40
  useRef,
10
41
  useState
11
42
  } from "react";
43
+ import {
44
+ GoogleOAuthProvider,
45
+ GoogleLogin,
46
+ googleLogout
47
+ } from "@react-oauth/google";
12
48
  import { CmsAuthProvider } from "@dalgoridim/headless-cms/client";
13
49
  import { jsx } from "react/jsx-runtime";
14
- var GSI_SRC = "https://accounts.google.com/gsi/client";
15
50
  var GoogleAuthContext = createContext(
16
51
  void 0
17
52
  );
@@ -32,15 +67,13 @@ function decodeJwt(token) {
32
67
  try {
33
68
  const payload = token.split(".")[1];
34
69
  const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
35
- const data = JSON.parse(json);
36
- return data;
70
+ return JSON.parse(json);
37
71
  } catch (e) {
38
72
  return null;
39
73
  }
40
74
  }
41
- function GoogleAuthProvider({
75
+ function AuthState({
42
76
  children,
43
- clientId,
44
77
  adminEmails,
45
78
  cookieName = "adminToken",
46
79
  onLogout
@@ -48,12 +81,10 @@ function GoogleAuthProvider({
48
81
  const [user, setUser] = useState(null);
49
82
  const [isAdmin, setIsAdmin] = useState(false);
50
83
  const [isEditing, setIsEditing] = useState(false);
51
- const [ready, setReady] = useState(false);
52
- const [failed, setFailed] = useState(false);
53
84
  const allowed = useRef(
54
85
  (adminEmails != null ? adminEmails : []).map((e) => e.trim().toLowerCase())
55
86
  );
56
- const applyToken = useCallback(
87
+ const applyCredential = useCallback(
57
88
  (token) => {
58
89
  var _a;
59
90
  const claims = decodeJwt(token);
@@ -77,44 +108,16 @@ function GoogleAuthProvider({
77
108
  );
78
109
  useEffect(() => {
79
110
  const existing = readCookie(cookieName);
80
- if (existing) applyToken(existing);
81
- }, [cookieName, applyToken]);
82
- useEffect(() => {
83
- let cancelled = false;
84
- function tryInit() {
85
- var _a, _b;
86
- if (cancelled || !((_b = (_a = window.google) == null ? void 0 : _a.accounts) == null ? void 0 : _b.id)) return false;
87
- window.google.accounts.id.initialize({
88
- client_id: clientId,
89
- callback: (res) => applyToken(res.credential),
90
- auto_select: false,
91
- cancel_on_tap_outside: true
92
- });
93
- setReady(true);
94
- return true;
95
- }
96
- if (tryInit()) return;
97
- if (!document.querySelector(`script[src="${GSI_SRC}"]`)) {
98
- const script = document.createElement("script");
99
- script.src = GSI_SRC;
100
- script.async = true;
101
- script.defer = true;
102
- document.head.appendChild(script);
103
- }
104
- const startedAt = Date.now();
105
- const timer = setInterval(() => {
106
- if (tryInit()) {
107
- clearInterval(timer);
108
- } else if (Date.now() - startedAt > 1e4) {
109
- clearInterval(timer);
110
- if (!cancelled) setFailed(true);
111
- }
112
- }, 120);
113
- return () => {
114
- cancelled = true;
115
- clearInterval(timer);
116
- };
117
- }, [clientId, applyToken]);
111
+ if (existing) applyCredential(existing);
112
+ }, [cookieName, applyCredential]);
113
+ const logout = useCallback(() => {
114
+ googleLogout();
115
+ deleteCookie(cookieName);
116
+ setUser(null);
117
+ setIsAdmin(false);
118
+ setIsEditing(false);
119
+ onLogout == null ? void 0 : onLogout();
120
+ }, [cookieName, onLogout]);
118
121
  useEffect(() => {
119
122
  const originalFetch = window.fetch;
120
123
  window.fetch = async (...args) => {
@@ -122,7 +125,7 @@ function GoogleAuthProvider({
122
125
  if (response.status === 401) {
123
126
  try {
124
127
  const data = await response.clone().json();
125
- if (data == null ? void 0 : data.logout) doLogout();
128
+ if (data == null ? void 0 : data.logout) logout();
126
129
  } catch (e) {
127
130
  }
128
131
  }
@@ -131,48 +134,48 @@ function GoogleAuthProvider({
131
134
  return () => {
132
135
  window.fetch = originalFetch;
133
136
  };
134
- }, []);
135
- function doLogout() {
136
- var _a;
137
- (_a = window.google) == null ? void 0 : _a.accounts.id.disableAutoSelect();
138
- deleteCookie(cookieName);
139
- setUser(null);
140
- setIsAdmin(false);
141
- setIsEditing(false);
142
- onLogout == null ? void 0 : onLogout();
143
- }
144
- const promptSignIn = useCallback(() => {
145
- var _a;
146
- if (!ready) return;
147
- (_a = window.google) == null ? void 0 : _a.accounts.id.prompt();
148
- }, [ready]);
149
- const renderButton = useCallback(
150
- (el, options) => {
151
- var _a;
152
- if (!ready) return;
153
- (_a = window.google) == null ? void 0 : _a.accounts.id.renderButton(el, options);
154
- },
155
- [ready]
156
- );
137
+ }, [logout]);
157
138
  const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);
158
139
  return /* @__PURE__ */ jsx(
159
140
  GoogleAuthContext.Provider,
160
141
  {
161
- value: {
162
- user,
163
- isAdmin,
164
- isEditing,
165
- toggleEdit,
166
- ready,
167
- failed,
168
- promptSignIn,
169
- renderButton,
170
- logout: doLogout
171
- },
142
+ value: { user, isAdmin, isEditing, toggleEdit, logout, applyCredential },
172
143
  children: /* @__PURE__ */ jsx(CmsAuthProvider, { value: { isAdmin, isEditing, toggleEdit }, children })
173
144
  }
174
145
  );
175
146
  }
147
+ function GoogleAuthProvider(_a) {
148
+ var _b = _a, {
149
+ children,
150
+ clientId
151
+ } = _b, rest = __objRest(_b, [
152
+ "children",
153
+ "clientId"
154
+ ]);
155
+ return /* @__PURE__ */ jsx(GoogleOAuthProvider, { clientId, children: /* @__PURE__ */ jsx(AuthState, __spreadProps(__spreadValues({}, rest), { children })) });
156
+ }
157
+ function GoogleSignInButton({
158
+ theme = "filled_black",
159
+ shape = "pill",
160
+ size = "large",
161
+ text = "continue_with",
162
+ onError
163
+ }) {
164
+ const { applyCredential } = useGoogleAuth();
165
+ return /* @__PURE__ */ jsx(
166
+ GoogleLogin,
167
+ {
168
+ onSuccess: (res) => {
169
+ if (res.credential) applyCredential(res.credential);
170
+ },
171
+ onError,
172
+ theme,
173
+ shape,
174
+ size,
175
+ text
176
+ }
177
+ );
178
+ }
176
179
  function useGoogleAuth() {
177
180
  const ctx = useContext(GoogleAuthContext);
178
181
  if (!ctx) {
@@ -182,6 +185,7 @@ function useGoogleAuth() {
182
185
  }
183
186
  export {
184
187
  GoogleAuthProvider,
188
+ GoogleSignInButton,
185
189
  useGoogleAuth
186
190
  };
187
191
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google Identity Services** sign-in — the Firebase-free\n * counterpart to `FirebaseAuthProvider`. It loads the GSI script, lets the user\n * sign in with Google, stashes the resulting ID token in a cookie for the\n * server `googleAuth` adapter to verify, and feeds the shared CMS auth context\n * so the edit primitives light up. Admin status is optimistic on the client\n * (via `adminEmails`); the server gate remains authoritative.\n */\n\nconst GSI_SRC = \"https://accounts.google.com/gsi/client\";\n\n/** Minimal slice of the Google Identity Services API we use. */\ninterface GsiCredentialResponse {\n credential: string;\n}\ninterface GsiButtonOptions {\n type?: \"standard\" | \"icon\";\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n width?: number;\n}\ninterface GsiClient {\n accounts: {\n id: {\n initialize: (config: {\n client_id: string;\n callback: (res: GsiCredentialResponse) => void;\n auto_select?: boolean;\n cancel_on_tap_outside?: boolean;\n }) => void;\n prompt: () => void;\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n disableAutoSelect: () => void;\n };\n };\n}\n\ndeclare global {\n interface Window {\n google?: GsiClient;\n }\n}\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n /** True once the GSI script has loaded and the client is initialized. */\n ready: boolean;\n /** True if GSI failed to load within the timeout (blocked/offline). */\n failed: boolean;\n /** Trigger Google One Tap / sign-in prompt. */\n promptSignIn: () => void;\n /** Render the official Google button into `el` (most reliable trigger). */\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n logout: () => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const data = JSON.parse(json) as {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n exp?: number;\n };\n return data;\n } catch {\n return null;\n }\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: GoogleAuthProviderProps) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const [ready, setReady] = useState(false);\n const [failed, setFailed] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyToken = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyToken(existing);\n }, [cookieName, applyToken]);\n\n // Load the GSI script and initialize the client. Polling (rather than relying\n // on a `load` event) makes this robust against React's dev double-mount, a\n // cached/already-loaded script, and missed load events — any of which could\n // otherwise leave `ready` stuck false and the UI spinning forever.\n useEffect(() => {\n let cancelled = false;\n\n function tryInit(): boolean {\n if (cancelled || !window.google?.accounts?.id) return false;\n window.google.accounts.id.initialize({\n client_id: clientId,\n callback: (res) => applyToken(res.credential),\n auto_select: false,\n cancel_on_tap_outside: true,\n });\n setReady(true);\n return true;\n }\n\n if (tryInit()) return;\n\n // Ensure the script tag exists exactly once across mounts.\n if (!document.querySelector(`script[src=\"${GSI_SRC}\"]`)) {\n const script = document.createElement(\"script\");\n script.src = GSI_SRC;\n script.async = true;\n script.defer = true;\n document.head.appendChild(script);\n }\n\n const startedAt = Date.now();\n const timer = setInterval(() => {\n if (tryInit()) {\n clearInterval(timer);\n } else if (Date.now() - startedAt > 10_000) {\n // Blocked (ad/privacy extension) or offline — surface a failure so the\n // UI can show a fallback instead of an endless spinner.\n clearInterval(timer);\n if (!cancelled) setFailed(true);\n }\n }, 120);\n\n return () => {\n cancelled = true;\n clearInterval(timer);\n };\n }, [clientId, applyToken]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) doLogout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n function doLogout() {\n window.google?.accounts.id.disableAutoSelect();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }\n\n const promptSignIn = useCallback(() => {\n if (!ready) return;\n window.google?.accounts.id.prompt();\n }, [ready]);\n\n const renderButton = useCallback(\n (el: HTMLElement, options?: GsiButtonOptions) => {\n if (!ready) return;\n window.google?.accounts.id.renderButton(el, options);\n },\n [ready],\n );\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{\n user,\n isAdmin,\n isEditing,\n toggleEdit,\n ready,\n failed,\n promptSignIn,\n renderButton,\n logout: doLogout,\n }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\n/** Google auth API (user/login/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n GoogleOAuthProvider,\n GoogleLogin,\n googleLogout,\n type CredentialResponse,\n} from \"@react-oauth/google\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google sign-in**, built on `@react-oauth/google` (the\n * official React wrapper for Google Identity Services). It handles the GSI\n * script + button rendering robustly — including remounts, so the button shows\n * reliably every time a sign-in dialog opens. The resulting ID token is stashed\n * in a cookie for the server `googleAuth` adapter to verify; admin status is\n * optimistic on the client (via `adminEmails`), with the server gate\n * authoritative.\n *\n * Render `<GoogleSignInButton />` anywhere inside the provider to show the\n * official Google button.\n */\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n logout: () => void;\n /** Feed a Google ID token credential (used by `GoogleSignInButton`). */\n applyCredential: (credential: string) => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nfunction AuthState({\n children,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: Omit<GoogleAuthProviderProps, \"clientId\">) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyCredential = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyCredential(existing);\n }, [cookieName, applyCredential]);\n\n const logout = useCallback(() => {\n googleLogout();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }, [cookieName, onLogout]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) logout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n }, [logout]);\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{ user, isAdmin, isEditing, toggleEdit, logout, applyCredential }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n ...rest\n}: GoogleAuthProviderProps) {\n return (\n <GoogleOAuthProvider clientId={clientId}>\n <AuthState {...rest}>{children}</AuthState>\n </GoogleOAuthProvider>\n );\n}\n\nexport interface GoogleSignInButtonProps {\n /** Forwarded to the underlying `<GoogleLogin>` (theme, shape, size, text…). */\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n onError?: () => void;\n}\n\n/** The official Google sign-in button. Must be rendered inside a provider. */\nexport function GoogleSignInButton({\n theme = \"filled_black\",\n shape = \"pill\",\n size = \"large\",\n text = \"continue_with\",\n onError,\n}: GoogleSignInButtonProps) {\n const { applyCredential } = useGoogleAuth();\n return (\n <GoogleLogin\n onSuccess={(res: CredentialResponse) => {\n if (res.credential) applyCredential(res.credential);\n }}\n onError={onError}\n theme={theme}\n shape={shape}\n size={size}\n text={text}\n />\n );\n}\n\n/** Google auth API (user/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
@@ -61,12 +61,14 @@ var dirtyKey = (collection, sectionKey) => `${collection}:${sectionKey}`;
61
61
  var PageProvider = ({
62
62
  children,
63
63
  initialSections = {},
64
+ initialCollections = {},
64
65
  apiBasePath = "/api/admin",
65
66
  storage,
66
67
  notify = defaultNotifier
67
68
  }) => {
68
69
  const [saving, setSaving] = (0, import_react.useState)(false);
69
70
  const [sections, setSections] = (0, import_react.useState)(initialSections);
71
+ const [collections, setCollections] = (0, import_react.useState)(initialCollections);
70
72
  const [pendingImages, setPendingImages] = (0, import_react.useState)([]);
71
73
  const [dirtySections, setDirtySections] = (0, import_react.useState)(/* @__PURE__ */ new Set());
72
74
  const hasUnsavedChanges = dirtySections.size > 0;
@@ -256,6 +258,98 @@ var PageProvider = ({
256
258
  setSaving(false);
257
259
  }
258
260
  }, [sections, pendingImages, dirtySections, saving, resolveImageUrl, persist, notify]);
261
+ const createItem = (0, import_react.useCallback)(
262
+ async (collection, data, opts) => {
263
+ var _a, _b, _c, _d;
264
+ const id = (_d = (_c = opts == null ? void 0 : opts.id) != null ? _c : (_b = (_a = globalThis.crypto) == null ? void 0 : _a.randomUUID) == null ? void 0 : _b.call(_a)) != null ? _d : `${collection}-${Date.now()}`;
265
+ const item = __spreadValues({ id }, data);
266
+ setCollections((prev) => {
267
+ var _a2;
268
+ const list = (_a2 = prev[collection]) != null ? _a2 : [];
269
+ return __spreadProps(__spreadValues({}, prev), {
270
+ [collection]: (opts == null ? void 0 : opts.atStart) ? [item, ...list] : [...list, item]
271
+ });
272
+ });
273
+ try {
274
+ const res = await fetch(`${apiBasePath}/${collection}/${id}`, {
275
+ method: "PUT",
276
+ headers: { "Content-Type": "application/json" },
277
+ body: JSON.stringify(data)
278
+ });
279
+ if (!res.ok) throw new Error("Failed to create item");
280
+ notify.success("Item added");
281
+ return id;
282
+ } catch (error) {
283
+ setCollections((prev) => {
284
+ var _a2;
285
+ return __spreadProps(__spreadValues({}, prev), {
286
+ [collection]: ((_a2 = prev[collection]) != null ? _a2 : []).filter((it) => it.id !== id)
287
+ });
288
+ });
289
+ notify.error("Failed to add item");
290
+ throw error;
291
+ }
292
+ },
293
+ [apiBasePath, notify]
294
+ );
295
+ const deleteItem = (0, import_react.useCallback)(
296
+ async (collection, id) => {
297
+ let removed = [];
298
+ setCollections((prev) => {
299
+ var _a;
300
+ removed = (_a = prev[collection]) != null ? _a : [];
301
+ return __spreadProps(__spreadValues({}, prev), {
302
+ [collection]: removed.filter((it) => it.id !== id)
303
+ });
304
+ });
305
+ try {
306
+ const res = await fetch(`${apiBasePath}/${collection}/${id}`, {
307
+ method: "DELETE"
308
+ });
309
+ if (!res.ok) throw new Error("Failed to delete item");
310
+ notify.success("Item removed");
311
+ } catch (error) {
312
+ setCollections((prev) => __spreadProps(__spreadValues({}, prev), { [collection]: removed }));
313
+ notify.error("Failed to remove item");
314
+ throw error;
315
+ }
316
+ },
317
+ [apiBasePath, notify]
318
+ );
319
+ const reorderItems = (0, import_react.useCallback)(
320
+ async (collection, orderedIds) => {
321
+ let previous = [];
322
+ let next = [];
323
+ setCollections((prev) => {
324
+ var _a;
325
+ previous = (_a = prev[collection]) != null ? _a : [];
326
+ const byId = new Map(previous.map((it) => [it.id, it]));
327
+ next = orderedIds.flatMap((id, index) => {
328
+ const item = byId.get(id);
329
+ return item ? [__spreadProps(__spreadValues({}, item), { order: index })] : [];
330
+ });
331
+ return __spreadProps(__spreadValues({}, prev), { [collection]: next });
332
+ });
333
+ try {
334
+ await Promise.all(
335
+ next.map(async (it) => {
336
+ const res = await fetch(`${apiBasePath}/${collection}/${it.id}`, {
337
+ method: "PATCH",
338
+ headers: { "Content-Type": "application/json" },
339
+ body: JSON.stringify({ order: it.order })
340
+ });
341
+ if (!res.ok) throw new Error("Failed to reorder");
342
+ })
343
+ );
344
+ notify.success("Order updated");
345
+ } catch (error) {
346
+ setCollections((prev) => __spreadProps(__spreadValues({}, prev), { [collection]: previous }));
347
+ notify.error("Failed to update order");
348
+ throw error;
349
+ }
350
+ },
351
+ [apiBasePath, notify]
352
+ );
259
353
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
260
354
  PageContext.Provider,
261
355
  {
@@ -268,7 +362,11 @@ var PageProvider = ({
268
362
  editField,
269
363
  setPendingImage,
270
364
  saveSection,
271
- saveAll
365
+ saveAll,
366
+ collections,
367
+ createItem,
368
+ deleteItem,
369
+ reorderItems
272
370
  },
273
371
  children
274
372
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/PageProvider.tsx","../../src/client/auth.tsx","../../src/client/ContentEditSpan.tsx","../../src/client/EditableImage.tsx","../../src/client/MarkdownEditor.tsx"],"sourcesContent":["\"use client\";\n\nexport {\n PageProvider,\n usePageContext,\n type PageProviderProps,\n type Notifier,\n type PendingImage,\n} from \"./PageProvider\";\nexport { CmsAuthContext, CmsAuthProvider, useCmsAuth } from \"./auth\";\nexport { default as ContentEditSpan } from \"./ContentEditSpan\";\nexport {\n default as EditableImage,\n type EditableImageRenderState,\n} from \"./EditableImage\";\nexport {\n useMarkdownEditor,\n type UseMarkdownEditorOptions,\n type MarkdownEditorApi,\n} from \"./MarkdownEditor\";\n\n// Re-export shared types for convenience.\nexport type {\n Section,\n SectionMap,\n NestedSections,\n Editable,\n EntityAddress,\n Query,\n QueryFilter,\n QueryFilterGroup,\n QueryCondition,\n QueryFilterOp,\n Ref,\n RelationConfig,\n DataAdapter,\n AuthAdapter,\n AuthIdentity,\n StorageAdapter,\n ClientStorageAdapter,\n ServerStorageAdapter,\n CmsAuthState,\n} from \"../types\";\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport type {\n ClientStorageAdapter,\n NestedSections,\n PendingImage,\n Section,\n} from \"../types\";\n\nexport type { PendingImage } from \"../types\";\n\n/**\n * Notification sink. The package ships a dependency-free console default so it\n * imposes no toast library; pass your own (e.g. a `sonner`-backed sink) via the\n * `notify` prop to surface UI toasts.\n */\nexport interface Notifier {\n success: (message: string) => void;\n error: (message: string) => void;\n}\n\nconst defaultNotifier: Notifier = {\n success: (m) => console.info(`[cms] ${m}`),\n error: (m) => console.error(`[cms] ${m}`),\n};\n\ninterface PageContextType {\n sections: NestedSections;\n hasUnsavedChanges: boolean;\n saving: boolean;\n pendingImages: PendingImage[];\n setSection: (collection: string, key: string, section: Section) => void;\n editField: (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: unknown,\n ) => void;\n setPendingImage: (image: PendingImage) => void;\n saveSection: (collection: string, sectionKey: string) => Promise<void>;\n saveAll: () => Promise<void>;\n}\n\nconst PageContext = createContext<PageContextType | undefined>(undefined);\n\nconst dirtyKey = (collection: string, sectionKey: string) =>\n `${collection}:${sectionKey}`;\n\nexport interface PageProviderProps {\n children: ReactNode;\n /** Server-rendered sections to hydrate from. */\n initialSections?: NestedSections;\n /**\n * Base path of the CMS API route mounted via `createCmsHandlers`. Saves\n * `PATCH` to `${apiBasePath}/{collection}/{id}`. Defaults to `/api/admin`.\n */\n apiBasePath?: string;\n /** Client storage adapter used to upload pending (non-external) images on save. */\n storage?: ClientStorageAdapter;\n /** Notification sink. Defaults to `sonner` toasts. */\n notify?: Notifier;\n}\n\nexport const PageProvider = ({\n children,\n initialSections = {},\n apiBasePath = \"/api/admin\",\n storage,\n notify = defaultNotifier,\n}: PageProviderProps) => {\n const [saving, setSaving] = useState(false);\n const [sections, setSections] = useState<NestedSections>(initialSections);\n const [pendingImages, setPendingImages] = useState<PendingImage[]>([]);\n const [dirtySections, setDirtySections] = useState<Set<string>>(new Set());\n\n const hasUnsavedChanges = dirtySections.size > 0;\n\n const resolveImageUrl = useCallback(\n async (img: PendingImage): Promise<string> => {\n if (img.isExternal) return img.localUrl;\n if (!storage) {\n throw new Error(\n \"PageProvider received a file upload but no `storage` adapter was provided.\",\n );\n }\n const { url } = await storage.upload(img.file!);\n return url;\n },\n [storage],\n );\n\n const setSection = useCallback(\n (collection: string, key: string, section: Section) => {\n setSections((prev) => ({\n ...prev,\n [collection]: { ...prev[collection], [key]: section },\n }));\n },\n [],\n );\n\n const editField = useCallback(\n (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: unknown,\n ) => {\n setSections((prev) => {\n const currentSection = prev[collection]?.[sectionKey];\n\n if (!currentSection) {\n console.error(`Section not found: ${collection}/${sectionKey}`);\n return prev;\n }\n\n const keys = fieldKey.split(\".\");\n const updated: Section = { ...currentSection };\n\n if (keys.length === 1) {\n updated[fieldKey] = value;\n } else {\n let current: Record<string, unknown> = updated;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current[keys[i]] = { ...(current[keys[i]] as object) };\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n }\n\n return {\n ...prev,\n [collection]: { ...prev[collection], [sectionKey]: updated },\n };\n });\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.add(dirtyKey(collection, sectionKey));\n return next;\n });\n },\n [],\n );\n\n const setPendingImage = useCallback((image: PendingImage) => {\n setPendingImages((prev) => [\n ...prev.filter(\n (img) =>\n !(\n img.collection === image.collection &&\n img.sectionKey === image.sectionKey &&\n img.fieldKey === image.fieldKey\n ),\n ),\n image,\n ]);\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.add(dirtyKey(image.collection, image.sectionKey));\n return next;\n });\n }, []);\n\n const persist = useCallback(\n async (section: Section) => {\n const response = await fetch(\n `${apiBasePath}/${section.collection}/${section.id}`,\n {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(section),\n },\n );\n if (!response.ok) throw new Error(\"Failed to save section\");\n },\n [apiBasePath],\n );\n\n const saveSection = useCallback(\n async (collection: string, sectionKey: string) => {\n if (saving) return;\n setSaving(true);\n\n try {\n const section = sections[collection]?.[sectionKey];\n if (!section?.id || !section?.collection) {\n console.error(`Invalid section: ${collection}/${sectionKey}`);\n setSaving(false);\n return;\n }\n\n const images = pendingImages.filter(\n (img) =>\n img.collection === collection && img.sectionKey === sectionKey,\n );\n\n let updatedSection: Section = { ...section };\n\n for (const img of images) {\n const url = await resolveImageUrl(img);\n const keys = img.fieldKey.split(\".\");\n if (keys.length === 1) {\n updatedSection = { ...updatedSection, [img.fieldKey]: url };\n } else {\n let current: Record<string, unknown> = updatedSection;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = url;\n }\n }\n\n await persist(updatedSection);\n\n setSections((prev) => ({\n ...prev,\n [collection]: { ...prev[collection], [sectionKey]: updatedSection },\n }));\n\n setPendingImages((prev) =>\n prev.filter(\n (img) =>\n !(img.collection === collection && img.sectionKey === sectionKey),\n ),\n );\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.delete(dirtyKey(collection, sectionKey));\n return next;\n });\n\n notify.success(\"Changes saved successfully!\");\n } catch (error) {\n console.error(\"Save failed:\", error);\n notify.error(\"Failed to save changes\");\n } finally {\n setSaving(false);\n }\n },\n [sections, pendingImages, saving, resolveImageUrl, persist, notify],\n );\n\n const saveAll = useCallback(async () => {\n if (saving || dirtySections.size === 0) return;\n setSaving(true);\n\n try {\n const updatedSections: NestedSections = { ...sections };\n\n for (const img of pendingImages) {\n const url = await resolveImageUrl(img);\n\n if (!updatedSections[img.collection])\n updatedSections[img.collection] = {};\n\n if (!updatedSections[img.collection][img.sectionKey]) {\n updatedSections[img.collection][img.sectionKey] = {\n id: img.docId,\n collection: img.collection,\n };\n }\n\n const keys = img.fieldKey.split(\".\");\n let current: Record<string, unknown> =\n updatedSections[img.collection][img.sectionKey];\n\n if (keys.length === 1) {\n updatedSections[img.collection][img.sectionKey] = {\n ...(current as Section),\n [img.fieldKey]: url,\n };\n } else {\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = url;\n }\n }\n\n for (const entry of dirtySections) {\n const [collection, key] = entry.split(\":\");\n const section = updatedSections[collection]?.[key];\n\n if (!section?.id || !section?.collection) {\n console.error(`Invalid section for save: ${entry}`);\n continue;\n }\n\n await persist(section);\n }\n\n setSections(updatedSections);\n setPendingImages([]);\n setDirtySections(new Set());\n notify.success(\"All changes saved successfully!\");\n } catch (error) {\n console.error(\"Save all failed:\", error);\n notify.error(\"Failed to save changes\");\n } finally {\n setSaving(false);\n }\n }, [sections, pendingImages, dirtySections, saving, resolveImageUrl, persist, notify]);\n\n return (\n <PageContext.Provider\n value={{\n sections,\n hasUnsavedChanges,\n pendingImages,\n saving,\n setSection,\n editField,\n setPendingImage,\n saveSection,\n saveAll,\n }}\n >\n {children}\n </PageContext.Provider>\n );\n};\n\nexport const usePageContext = () => {\n const context = useContext(PageContext);\n if (!context)\n throw new Error(\"usePageContext must be used within a PageProvider\");\n return context;\n};\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { CmsAuthState } from \"../types\";\n\n/**\n * The single source of client-side auth state for the engine. Every built-in\n * auth provider (and any custom one) feeds this context; the edit primitives\n * read it via {@link useCmsAuth}.\n *\n * Cross-entry note: provider packages (e.g. `…/auth/firebase/client`) import\n * this context through the package's public `…/client` specifier so they share\n * the *same* context instance as the consumer's primitives at runtime.\n */\nexport const CmsAuthContext = createContext<CmsAuthState | undefined>(undefined);\n\nexport function useCmsAuth(): CmsAuthState {\n const ctx = useContext(CmsAuthContext);\n if (!ctx) {\n throw new Error(\n \"useCmsAuth must be used within a CmsAuthProvider (or a built-in auth provider such as FirebaseAuthProvider).\",\n );\n }\n return ctx;\n}\n\n/**\n * Controlled provider for consumers wiring their own auth. Pass the resolved\n * {@link CmsAuthState}; the built-in providers wrap this for you.\n */\nexport function CmsAuthProvider({\n value,\n children,\n}: {\n value: CmsAuthState;\n children: ReactNode;\n}) {\n return (\n <CmsAuthContext.Provider value={value}>{children}</CmsAuthContext.Provider>\n );\n}\n","\"use client\";\n\nimport React, { useRef, useEffect, useState, useCallback } from \"react\";\nimport { usePageContext } from \"./PageProvider\";\nimport { useCmsAuth } from \"./auth\";\n\n/**\n * Inline-editable text primitive. Headless: it wires `contentEditable`, reads\n * and persists the field, and exposes edit state via `data-*` attributes —\n * styling and rich-text rendering are entirely yours.\n *\n * - Style it with `className` and the `data-cms-editing` / `data-cms-focused`\n * attributes (no built-in look, no Tailwind, no design tokens).\n * - Supply `renderValue` to turn the stored raw string into rich nodes (e.g. a\n * markdown parser). Defaults to plain text.\n */\ninterface ContentEditSpanProps {\n collection: string;\n sectionKey: string;\n fieldKey: string;\n className?: string;\n children?: React.ReactNode;\n /** Element/tag to render. Defaults to `span`. */\n as?: React.ElementType;\n /** Render the stored raw string into nodes. Defaults to plain text. */\n renderValue?: (raw: string) => React.ReactNode;\n}\n\nconst defaultRenderValue = (raw: string): React.ReactNode => raw;\n\nfunction getNestedValue(obj: unknown, path: string): unknown {\n const keys = path.split(\".\");\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current == null || (current as Record<string, unknown>)[key] === undefined)\n return undefined;\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\nexport default function ContentEditSpan({\n collection,\n sectionKey,\n fieldKey,\n className,\n children,\n as = \"span\",\n renderValue = defaultRenderValue,\n}: ContentEditSpanProps) {\n const { sections, editField } = usePageContext();\n const { isEditing } = useCmsAuth();\n\n const section = sections[collection]?.[sectionKey];\n const raw =\n (getNestedValue(section, fieldKey) as string) ??\n (typeof children === \"string\" ? children : \"\");\n\n const Component = as;\n\n if (!isEditing) {\n return <Component className={className}>{renderValue(raw)}</Component>;\n }\n\n return (\n <EditableContentSpan\n collection={collection}\n sectionKey={sectionKey}\n fieldKey={fieldKey}\n className={className}\n raw={raw}\n editField={editField}\n as={as}\n renderValue={renderValue}\n />\n );\n}\n\nfunction EditableContentSpan({\n collection,\n sectionKey,\n fieldKey,\n className,\n raw,\n editField,\n as: Component = \"span\",\n renderValue,\n}: {\n collection: string;\n sectionKey: string;\n fieldKey: string;\n className?: string;\n raw: string;\n editField: (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: string,\n ) => void;\n as?: React.ElementType;\n renderValue: (raw: string) => React.ReactNode;\n}) {\n const { isEditing } = useCmsAuth();\n const [isFocused, setIsFocused] = useState(false);\n const [editValue, setEditValue] = useState(raw);\n const contentRef = useRef<HTMLElement>(null);\n const rawRef = useRef(raw);\n\n useEffect(() => {\n if (!isFocused && rawRef.current !== raw) {\n rawRef.current = raw;\n }\n }, [raw, isFocused]);\n\n useEffect(() => {\n if (!isFocused && rawRef.current !== editValue) {\n setEditValue(rawRef.current);\n }\n }, [isFocused, editValue]);\n\n const handleInput = useCallback(() => {\n if (contentRef.current) {\n setEditValue(contentRef.current.textContent || \"\");\n }\n }, []);\n\n const handleBlur = useCallback(() => {\n setIsFocused(false);\n if (editValue !== raw) {\n editField(collection, sectionKey, fieldKey, editValue);\n }\n }, [editValue, raw, collection, sectionKey, fieldKey, editField]);\n\n const handleFocus = useCallback(() => {\n setIsFocused(true);\n if (contentRef.current) {\n contentRef.current.textContent = editValue;\n setTimeout(() => {\n if (contentRef.current) {\n const range = document.createRange();\n const sel = window.getSelection();\n range.selectNodeContents(contentRef.current);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n }, 0);\n }\n }, [editValue]);\n\n return (\n <Component\n key={isFocused ? \"editing\" : \"static\"}\n ref={contentRef}\n className={className}\n data-cms-editable=\"\"\n data-cms-editing={isEditing ? \"\" : undefined}\n data-cms-focused={isFocused ? \"\" : undefined}\n contentEditable={isEditing}\n suppressContentEditableWarning\n onInput={handleInput}\n onBlur={handleBlur}\n onFocus={handleFocus}\n >\n {!isFocused && renderValue(editValue)}\n </Component>\n );\n}\n","\"use client\";\n\nimport React, { useRef, useState } from \"react\";\nimport { useCmsAuth } from \"./auth\";\nimport { usePageContext } from \"./PageProvider\";\n\n/** State + actions handed to the {@link EditableImage} render-prop. */\nexport interface EditableImageRenderState {\n /** URL to display (pending upload preview, external URL, or the saved src). */\n src: string;\n isEditing: boolean;\n saving: boolean;\n /** True after the current `src` failed to load. */\n hasError: boolean;\n /** Open the native file picker (a hidden input is managed for you). */\n openFilePicker: () => void;\n /**\n * Queue an external image URL. Returns `false` if it isn't a valid http(s)\n * URL (nothing is changed in that case).\n */\n setExternalUrl: (url: string) => boolean;\n /** Convenience props for an `<img>`: `{ src, onError }`. */\n imgProps: { src: string; onError: () => void };\n}\n\ninterface EditableImageProps {\n sectionKey: string;\n fieldKey: string;\n src: string;\n collection: string;\n docId: string;\n className?: string;\n /**\n * Render the image and any editing chrome (overlay, buttons, URL modal). The\n * package ships no visual look — bring your own. When omitted, a bare\n * unstyled `<img>` is rendered.\n */\n children?: (state: EditableImageRenderState) => React.ReactNode;\n}\n\nexport default function EditableImage({\n sectionKey,\n fieldKey,\n src,\n collection,\n docId,\n className,\n children,\n}: EditableImageProps) {\n const { isEditing } = useCmsAuth();\n const { editField, setPendingImage, pendingImages, saving } =\n usePageContext();\n\n const [preview, setPreview] = useState(src);\n const [hasError, setHasError] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const pendingImage = pendingImages.find(\n (img) => img.sectionKey === sectionKey && img.fieldKey === fieldKey,\n );\n const imgSrc = pendingImage?.localUrl || preview;\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (saving) return;\n const file = e.target.files?.[0];\n if (!file) return;\n\n const localUrl = URL.createObjectURL(file);\n setPreview(localUrl);\n setHasError(false);\n\n editField(collection, sectionKey, fieldKey, localUrl);\n setPendingImage({\n file,\n localUrl,\n sectionKey,\n fieldKey,\n collection,\n docId,\n isExternal: false,\n });\n };\n\n const openFilePicker = () => {\n if (saving) return;\n inputRef.current?.click();\n };\n\n const setExternalUrl = (value: string): boolean => {\n let valid = false;\n try {\n const url = new URL(value);\n valid = url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n valid = false;\n }\n if (!valid) return false;\n\n setPreview(value);\n setHasError(false);\n editField(collection, sectionKey, fieldKey, value);\n setPendingImage({\n file: null,\n localUrl: value,\n sectionKey,\n fieldKey,\n collection,\n docId,\n isExternal: true,\n });\n return true;\n };\n\n const state: EditableImageRenderState = {\n src: imgSrc,\n isEditing,\n saving,\n hasError,\n openFilePicker,\n setExternalUrl,\n imgProps: { src: imgSrc, onError: () => setHasError(true) },\n };\n\n return (\n <div className={className}>\n {/* Internal hidden file input — driven via openFilePicker(). */}\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n disabled={saving}\n onChange={handleFileChange}\n style={{ display: \"none\" }}\n />\n {children ? (\n children(state)\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img {...state.imgProps} alt=\"\" />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\n\nexport interface UseMarkdownEditorOptions {\n initialValue: string;\n onSave: (content: string) => void | Promise<void>;\n}\n\nexport interface MarkdownEditorApi {\n /** Current editor text. */\n value: string;\n setValue: (next: string) => void;\n /** Attach to your `<textarea>` so `insert` can target the selection. */\n textareaRef: React.RefObject<HTMLTextAreaElement | null>;\n /**\n * Wrap the current selection (or insert a placeholder) with `before`/`after`\n * markers — e.g. `insert(\"**\", \"**\", \"bold text\")`. Restores focus and caret.\n */\n insert: (before: string, after?: string, placeholder?: string) => void;\n /** Reset back to `initialValue` (or a provided value). */\n reset: (to?: string) => void;\n /** Persist the current value via the provided `onSave`. */\n save: () => void | Promise<void>;\n charCount: number;\n}\n\n/**\n * Headless markdown-editing logic: value state, a selection-aware `insert`\n * command, and save/reset. The package ships **no** modal, toolbar, icons, or\n * preview renderer — compose those yourself (e.g. with `react-markdown`). This\n * keeps the package free of any UI library or visual opinion.\n *\n * ```tsx\n * const md = useMarkdownEditor({ initialValue, onSave });\n * <textarea ref={md.textareaRef} value={md.value}\n * onChange={(e) => md.setValue(e.target.value)} />\n * <button onClick={() => md.insert(\"**\", \"**\", \"bold\")}>Bold</button>\n * ```\n */\nexport function useMarkdownEditor({\n initialValue,\n onSave,\n}: UseMarkdownEditorOptions): MarkdownEditorApi {\n const [value, setValue] = useState(initialValue);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const insert = useCallback(\n (before: string, after = \"\", placeholder = \"text\") => {\n const textarea = textareaRef.current;\n const start = textarea?.selectionStart ?? value.length;\n const end = textarea?.selectionEnd ?? value.length;\n const selected = value.substring(start, end) || placeholder;\n const next =\n value.substring(0, start) +\n before +\n selected +\n after +\n value.substring(end);\n\n setValue(next);\n\n setTimeout(() => {\n if (!textarea) return;\n textarea.focus();\n const caret = start + before.length + selected.length;\n textarea.setSelectionRange(caret, caret);\n }, 0);\n },\n [value],\n );\n\n const reset = useCallback(\n (to: string = initialValue) => setValue(to),\n [initialValue],\n );\n\n const save = useCallback(() => onSave(value), [onSave, value]);\n\n return {\n value,\n setValue,\n textareaRef,\n insert,\n reset,\n save,\n charCount: value.length,\n };\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react"]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/PageProvider.tsx","../../src/client/auth.tsx","../../src/client/ContentEditSpan.tsx","../../src/client/EditableImage.tsx","../../src/client/MarkdownEditor.tsx"],"sourcesContent":["\"use client\";\n\nexport {\n PageProvider,\n usePageContext,\n type PageProviderProps,\n type Notifier,\n type PendingImage,\n} from \"./PageProvider\";\nexport { CmsAuthContext, CmsAuthProvider, useCmsAuth } from \"./auth\";\nexport { default as ContentEditSpan } from \"./ContentEditSpan\";\nexport {\n default as EditableImage,\n type EditableImageRenderState,\n} from \"./EditableImage\";\nexport {\n useMarkdownEditor,\n type UseMarkdownEditorOptions,\n type MarkdownEditorApi,\n} from \"./MarkdownEditor\";\n\n// Re-export shared types for convenience.\nexport type {\n Section,\n SectionMap,\n NestedSections,\n CollectionItem,\n Editable,\n EntityAddress,\n Query,\n QueryFilter,\n QueryFilterGroup,\n QueryCondition,\n QueryFilterOp,\n Ref,\n RelationConfig,\n DataAdapter,\n AuthAdapter,\n AuthIdentity,\n StorageAdapter,\n ClientStorageAdapter,\n ServerStorageAdapter,\n CmsAuthState,\n} from \"../types\";\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport type {\n ClientStorageAdapter,\n CollectionItem,\n NestedSections,\n PendingImage,\n Section,\n} from \"../types\";\n\nexport type { PendingImage } from \"../types\";\n\n/**\n * Notification sink. The package ships a dependency-free console default so it\n * imposes no toast library; pass your own (e.g. a `sonner`-backed sink) via the\n * `notify` prop to surface UI toasts.\n */\nexport interface Notifier {\n success: (message: string) => void;\n error: (message: string) => void;\n}\n\nconst defaultNotifier: Notifier = {\n success: (m) => console.info(`[cms] ${m}`),\n error: (m) => console.error(`[cms] ${m}`),\n};\n\ninterface PageContextType {\n sections: NestedSections;\n hasUnsavedChanges: boolean;\n saving: boolean;\n pendingImages: PendingImage[];\n setSection: (collection: string, key: string, section: Section) => void;\n editField: (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: unknown,\n ) => void;\n setPendingImage: (image: PendingImage) => void;\n saveSection: (collection: string, sectionKey: string) => Promise<void>;\n saveAll: () => Promise<void>;\n /** Ordered item lists per collection (for add/remove/reorder). */\n collections: Record<string, CollectionItem[]>;\n /**\n * Append (or prepend) a new item to a collection and persist it immediately\n * via `PUT`. Returns the new item's id. Optimistic with rollback on failure.\n */\n createItem: (\n collection: string,\n data: Record<string, unknown>,\n opts?: { id?: string; atStart?: boolean },\n ) => Promise<string>;\n /** Remove an item from a collection and persist via `DELETE`. Optimistic. */\n deleteItem: (collection: string, id: string) => Promise<void>;\n /**\n * Reorder a collection to match `orderedIds`, persisting each item's new\n * integer `order` via `PATCH`. Optimistic with rollback on failure.\n */\n reorderItems: (collection: string, orderedIds: string[]) => Promise<void>;\n}\n\nconst PageContext = createContext<PageContextType | undefined>(undefined);\n\nconst dirtyKey = (collection: string, sectionKey: string) =>\n `${collection}:${sectionKey}`;\n\nexport interface PageProviderProps {\n children: ReactNode;\n /** Server-rendered sections to hydrate from. */\n initialSections?: NestedSections;\n /** Server-rendered collection item lists to hydrate from (for add/remove/reorder). */\n initialCollections?: Record<string, CollectionItem[]>;\n /**\n * Base path of the CMS API route mounted via `createCmsHandlers`. Saves\n * `PATCH` to `${apiBasePath}/{collection}/{id}`. Defaults to `/api/admin`.\n */\n apiBasePath?: string;\n /** Client storage adapter used to upload pending (non-external) images on save. */\n storage?: ClientStorageAdapter;\n /** Notification sink. Defaults to `sonner` toasts. */\n notify?: Notifier;\n}\n\nexport const PageProvider = ({\n children,\n initialSections = {},\n initialCollections = {},\n apiBasePath = \"/api/admin\",\n storage,\n notify = defaultNotifier,\n}: PageProviderProps) => {\n const [saving, setSaving] = useState(false);\n const [sections, setSections] = useState<NestedSections>(initialSections);\n const [collections, setCollections] =\n useState<Record<string, CollectionItem[]>>(initialCollections);\n const [pendingImages, setPendingImages] = useState<PendingImage[]>([]);\n const [dirtySections, setDirtySections] = useState<Set<string>>(new Set());\n\n const hasUnsavedChanges = dirtySections.size > 0;\n\n const resolveImageUrl = useCallback(\n async (img: PendingImage): Promise<string> => {\n if (img.isExternal) return img.localUrl;\n if (!storage) {\n throw new Error(\n \"PageProvider received a file upload but no `storage` adapter was provided.\",\n );\n }\n const { url } = await storage.upload(img.file!);\n return url;\n },\n [storage],\n );\n\n const setSection = useCallback(\n (collection: string, key: string, section: Section) => {\n setSections((prev) => ({\n ...prev,\n [collection]: { ...prev[collection], [key]: section },\n }));\n },\n [],\n );\n\n const editField = useCallback(\n (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: unknown,\n ) => {\n setSections((prev) => {\n const currentSection = prev[collection]?.[sectionKey];\n\n if (!currentSection) {\n console.error(`Section not found: ${collection}/${sectionKey}`);\n return prev;\n }\n\n const keys = fieldKey.split(\".\");\n const updated: Section = { ...currentSection };\n\n if (keys.length === 1) {\n updated[fieldKey] = value;\n } else {\n let current: Record<string, unknown> = updated;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current[keys[i]] = { ...(current[keys[i]] as object) };\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n }\n\n return {\n ...prev,\n [collection]: { ...prev[collection], [sectionKey]: updated },\n };\n });\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.add(dirtyKey(collection, sectionKey));\n return next;\n });\n },\n [],\n );\n\n const setPendingImage = useCallback((image: PendingImage) => {\n setPendingImages((prev) => [\n ...prev.filter(\n (img) =>\n !(\n img.collection === image.collection &&\n img.sectionKey === image.sectionKey &&\n img.fieldKey === image.fieldKey\n ),\n ),\n image,\n ]);\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.add(dirtyKey(image.collection, image.sectionKey));\n return next;\n });\n }, []);\n\n const persist = useCallback(\n async (section: Section) => {\n const response = await fetch(\n `${apiBasePath}/${section.collection}/${section.id}`,\n {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(section),\n },\n );\n if (!response.ok) throw new Error(\"Failed to save section\");\n },\n [apiBasePath],\n );\n\n const saveSection = useCallback(\n async (collection: string, sectionKey: string) => {\n if (saving) return;\n setSaving(true);\n\n try {\n const section = sections[collection]?.[sectionKey];\n if (!section?.id || !section?.collection) {\n console.error(`Invalid section: ${collection}/${sectionKey}`);\n setSaving(false);\n return;\n }\n\n const images = pendingImages.filter(\n (img) =>\n img.collection === collection && img.sectionKey === sectionKey,\n );\n\n let updatedSection: Section = { ...section };\n\n for (const img of images) {\n const url = await resolveImageUrl(img);\n const keys = img.fieldKey.split(\".\");\n if (keys.length === 1) {\n updatedSection = { ...updatedSection, [img.fieldKey]: url };\n } else {\n let current: Record<string, unknown> = updatedSection;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = url;\n }\n }\n\n await persist(updatedSection);\n\n setSections((prev) => ({\n ...prev,\n [collection]: { ...prev[collection], [sectionKey]: updatedSection },\n }));\n\n setPendingImages((prev) =>\n prev.filter(\n (img) =>\n !(img.collection === collection && img.sectionKey === sectionKey),\n ),\n );\n\n setDirtySections((prev) => {\n const next = new Set(prev);\n next.delete(dirtyKey(collection, sectionKey));\n return next;\n });\n\n notify.success(\"Changes saved successfully!\");\n } catch (error) {\n console.error(\"Save failed:\", error);\n notify.error(\"Failed to save changes\");\n } finally {\n setSaving(false);\n }\n },\n [sections, pendingImages, saving, resolveImageUrl, persist, notify],\n );\n\n const saveAll = useCallback(async () => {\n if (saving || dirtySections.size === 0) return;\n setSaving(true);\n\n try {\n const updatedSections: NestedSections = { ...sections };\n\n for (const img of pendingImages) {\n const url = await resolveImageUrl(img);\n\n if (!updatedSections[img.collection])\n updatedSections[img.collection] = {};\n\n if (!updatedSections[img.collection][img.sectionKey]) {\n updatedSections[img.collection][img.sectionKey] = {\n id: img.docId,\n collection: img.collection,\n };\n }\n\n const keys = img.fieldKey.split(\".\");\n let current: Record<string, unknown> =\n updatedSections[img.collection][img.sectionKey];\n\n if (keys.length === 1) {\n updatedSections[img.collection][img.sectionKey] = {\n ...(current as Section),\n [img.fieldKey]: url,\n };\n } else {\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = url;\n }\n }\n\n for (const entry of dirtySections) {\n const [collection, key] = entry.split(\":\");\n const section = updatedSections[collection]?.[key];\n\n if (!section?.id || !section?.collection) {\n console.error(`Invalid section for save: ${entry}`);\n continue;\n }\n\n await persist(section);\n }\n\n setSections(updatedSections);\n setPendingImages([]);\n setDirtySections(new Set());\n notify.success(\"All changes saved successfully!\");\n } catch (error) {\n console.error(\"Save all failed:\", error);\n notify.error(\"Failed to save changes\");\n } finally {\n setSaving(false);\n }\n }, [sections, pendingImages, dirtySections, saving, resolveImageUrl, persist, notify]);\n\n const createItem = useCallback(\n async (\n collection: string,\n data: Record<string, unknown>,\n opts?: { id?: string; atStart?: boolean },\n ): Promise<string> => {\n const id =\n opts?.id ??\n globalThis.crypto?.randomUUID?.() ??\n `${collection}-${Date.now()}`;\n const item: CollectionItem = { id, ...data };\n\n // Optimistic insert.\n setCollections((prev) => {\n const list = prev[collection] ?? [];\n return {\n ...prev,\n [collection]: opts?.atStart ? [item, ...list] : [...list, item],\n };\n });\n\n try {\n const res = await fetch(`${apiBasePath}/${collection}/${id}`, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data),\n });\n if (!res.ok) throw new Error(\"Failed to create item\");\n notify.success(\"Item added\");\n return id;\n } catch (error) {\n // Roll back the optimistic insert.\n setCollections((prev) => ({\n ...prev,\n [collection]: (prev[collection] ?? []).filter((it) => it.id !== id),\n }));\n notify.error(\"Failed to add item\");\n throw error;\n }\n },\n [apiBasePath, notify],\n );\n\n const deleteItem = useCallback(\n async (collection: string, id: string): Promise<void> => {\n let removed: CollectionItem[] = [];\n setCollections((prev) => {\n removed = prev[collection] ?? [];\n return {\n ...prev,\n [collection]: removed.filter((it) => it.id !== id),\n };\n });\n\n try {\n const res = await fetch(`${apiBasePath}/${collection}/${id}`, {\n method: \"DELETE\",\n });\n if (!res.ok) throw new Error(\"Failed to delete item\");\n notify.success(\"Item removed\");\n } catch (error) {\n setCollections((prev) => ({ ...prev, [collection]: removed })); // rollback\n notify.error(\"Failed to remove item\");\n throw error;\n }\n },\n [apiBasePath, notify],\n );\n\n const reorderItems = useCallback(\n async (collection: string, orderedIds: string[]): Promise<void> => {\n let previous: CollectionItem[] = [];\n let next: CollectionItem[] = [];\n setCollections((prev) => {\n previous = prev[collection] ?? [];\n const byId = new Map(previous.map((it) => [it.id, it]));\n next = orderedIds.flatMap((id, index) => {\n const item = byId.get(id);\n return item ? [{ ...item, order: index }] : [];\n });\n return { ...prev, [collection]: next };\n });\n\n try {\n await Promise.all(\n next.map(async (it) => {\n const res = await fetch(`${apiBasePath}/${collection}/${it.id}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ order: it.order }),\n });\n if (!res.ok) throw new Error(\"Failed to reorder\");\n }),\n );\n notify.success(\"Order updated\");\n } catch (error) {\n setCollections((prev) => ({ ...prev, [collection]: previous })); // rollback\n notify.error(\"Failed to update order\");\n throw error;\n }\n },\n [apiBasePath, notify],\n );\n\n return (\n <PageContext.Provider\n value={{\n sections,\n hasUnsavedChanges,\n pendingImages,\n saving,\n setSection,\n editField,\n setPendingImage,\n saveSection,\n saveAll,\n collections,\n createItem,\n deleteItem,\n reorderItems,\n }}\n >\n {children}\n </PageContext.Provider>\n );\n};\n\nexport const usePageContext = () => {\n const context = useContext(PageContext);\n if (!context)\n throw new Error(\"usePageContext must be used within a PageProvider\");\n return context;\n};\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { CmsAuthState } from \"../types\";\n\n/**\n * The single source of client-side auth state for the engine. Every built-in\n * auth provider (and any custom one) feeds this context; the edit primitives\n * read it via {@link useCmsAuth}.\n *\n * Cross-entry note: provider packages (e.g. `…/auth/firebase/client`) import\n * this context through the package's public `…/client` specifier so they share\n * the *same* context instance as the consumer's primitives at runtime.\n */\nexport const CmsAuthContext = createContext<CmsAuthState | undefined>(undefined);\n\nexport function useCmsAuth(): CmsAuthState {\n const ctx = useContext(CmsAuthContext);\n if (!ctx) {\n throw new Error(\n \"useCmsAuth must be used within a CmsAuthProvider (or a built-in auth provider such as FirebaseAuthProvider).\",\n );\n }\n return ctx;\n}\n\n/**\n * Controlled provider for consumers wiring their own auth. Pass the resolved\n * {@link CmsAuthState}; the built-in providers wrap this for you.\n */\nexport function CmsAuthProvider({\n value,\n children,\n}: {\n value: CmsAuthState;\n children: ReactNode;\n}) {\n return (\n <CmsAuthContext.Provider value={value}>{children}</CmsAuthContext.Provider>\n );\n}\n","\"use client\";\n\nimport React, { useRef, useEffect, useState, useCallback } from \"react\";\nimport { usePageContext } from \"./PageProvider\";\nimport { useCmsAuth } from \"./auth\";\n\n/**\n * Inline-editable text primitive. Headless: it wires `contentEditable`, reads\n * and persists the field, and exposes edit state via `data-*` attributes —\n * styling and rich-text rendering are entirely yours.\n *\n * - Style it with `className` and the `data-cms-editing` / `data-cms-focused`\n * attributes (no built-in look, no Tailwind, no design tokens).\n * - Supply `renderValue` to turn the stored raw string into rich nodes (e.g. a\n * markdown parser). Defaults to plain text.\n */\ninterface ContentEditSpanProps {\n collection: string;\n sectionKey: string;\n fieldKey: string;\n className?: string;\n children?: React.ReactNode;\n /** Element/tag to render. Defaults to `span`. */\n as?: React.ElementType;\n /** Render the stored raw string into nodes. Defaults to plain text. */\n renderValue?: (raw: string) => React.ReactNode;\n}\n\nconst defaultRenderValue = (raw: string): React.ReactNode => raw;\n\nfunction getNestedValue(obj: unknown, path: string): unknown {\n const keys = path.split(\".\");\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current == null || (current as Record<string, unknown>)[key] === undefined)\n return undefined;\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\nexport default function ContentEditSpan({\n collection,\n sectionKey,\n fieldKey,\n className,\n children,\n as = \"span\",\n renderValue = defaultRenderValue,\n}: ContentEditSpanProps) {\n const { sections, editField } = usePageContext();\n const { isEditing } = useCmsAuth();\n\n const section = sections[collection]?.[sectionKey];\n const raw =\n (getNestedValue(section, fieldKey) as string) ??\n (typeof children === \"string\" ? children : \"\");\n\n const Component = as;\n\n if (!isEditing) {\n return <Component className={className}>{renderValue(raw)}</Component>;\n }\n\n return (\n <EditableContentSpan\n collection={collection}\n sectionKey={sectionKey}\n fieldKey={fieldKey}\n className={className}\n raw={raw}\n editField={editField}\n as={as}\n renderValue={renderValue}\n />\n );\n}\n\nfunction EditableContentSpan({\n collection,\n sectionKey,\n fieldKey,\n className,\n raw,\n editField,\n as: Component = \"span\",\n renderValue,\n}: {\n collection: string;\n sectionKey: string;\n fieldKey: string;\n className?: string;\n raw: string;\n editField: (\n collection: string,\n sectionKey: string,\n fieldKey: string,\n value: string,\n ) => void;\n as?: React.ElementType;\n renderValue: (raw: string) => React.ReactNode;\n}) {\n const { isEditing } = useCmsAuth();\n const [isFocused, setIsFocused] = useState(false);\n const [editValue, setEditValue] = useState(raw);\n const contentRef = useRef<HTMLElement>(null);\n const rawRef = useRef(raw);\n\n useEffect(() => {\n if (!isFocused && rawRef.current !== raw) {\n rawRef.current = raw;\n }\n }, [raw, isFocused]);\n\n useEffect(() => {\n if (!isFocused && rawRef.current !== editValue) {\n setEditValue(rawRef.current);\n }\n }, [isFocused, editValue]);\n\n const handleInput = useCallback(() => {\n if (contentRef.current) {\n setEditValue(contentRef.current.textContent || \"\");\n }\n }, []);\n\n const handleBlur = useCallback(() => {\n setIsFocused(false);\n if (editValue !== raw) {\n editField(collection, sectionKey, fieldKey, editValue);\n }\n }, [editValue, raw, collection, sectionKey, fieldKey, editField]);\n\n const handleFocus = useCallback(() => {\n setIsFocused(true);\n if (contentRef.current) {\n contentRef.current.textContent = editValue;\n setTimeout(() => {\n if (contentRef.current) {\n const range = document.createRange();\n const sel = window.getSelection();\n range.selectNodeContents(contentRef.current);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n }, 0);\n }\n }, [editValue]);\n\n return (\n <Component\n key={isFocused ? \"editing\" : \"static\"}\n ref={contentRef}\n className={className}\n data-cms-editable=\"\"\n data-cms-editing={isEditing ? \"\" : undefined}\n data-cms-focused={isFocused ? \"\" : undefined}\n contentEditable={isEditing}\n suppressContentEditableWarning\n onInput={handleInput}\n onBlur={handleBlur}\n onFocus={handleFocus}\n >\n {!isFocused && renderValue(editValue)}\n </Component>\n );\n}\n","\"use client\";\n\nimport React, { useRef, useState } from \"react\";\nimport { useCmsAuth } from \"./auth\";\nimport { usePageContext } from \"./PageProvider\";\n\n/** State + actions handed to the {@link EditableImage} render-prop. */\nexport interface EditableImageRenderState {\n /** URL to display (pending upload preview, external URL, or the saved src). */\n src: string;\n isEditing: boolean;\n saving: boolean;\n /** True after the current `src` failed to load. */\n hasError: boolean;\n /** Open the native file picker (a hidden input is managed for you). */\n openFilePicker: () => void;\n /**\n * Queue an external image URL. Returns `false` if it isn't a valid http(s)\n * URL (nothing is changed in that case).\n */\n setExternalUrl: (url: string) => boolean;\n /** Convenience props for an `<img>`: `{ src, onError }`. */\n imgProps: { src: string; onError: () => void };\n}\n\ninterface EditableImageProps {\n sectionKey: string;\n fieldKey: string;\n src: string;\n collection: string;\n docId: string;\n className?: string;\n /**\n * Render the image and any editing chrome (overlay, buttons, URL modal). The\n * package ships no visual look — bring your own. When omitted, a bare\n * unstyled `<img>` is rendered.\n */\n children?: (state: EditableImageRenderState) => React.ReactNode;\n}\n\nexport default function EditableImage({\n sectionKey,\n fieldKey,\n src,\n collection,\n docId,\n className,\n children,\n}: EditableImageProps) {\n const { isEditing } = useCmsAuth();\n const { editField, setPendingImage, pendingImages, saving } =\n usePageContext();\n\n const [preview, setPreview] = useState(src);\n const [hasError, setHasError] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const pendingImage = pendingImages.find(\n (img) => img.sectionKey === sectionKey && img.fieldKey === fieldKey,\n );\n const imgSrc = pendingImage?.localUrl || preview;\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (saving) return;\n const file = e.target.files?.[0];\n if (!file) return;\n\n const localUrl = URL.createObjectURL(file);\n setPreview(localUrl);\n setHasError(false);\n\n editField(collection, sectionKey, fieldKey, localUrl);\n setPendingImage({\n file,\n localUrl,\n sectionKey,\n fieldKey,\n collection,\n docId,\n isExternal: false,\n });\n };\n\n const openFilePicker = () => {\n if (saving) return;\n inputRef.current?.click();\n };\n\n const setExternalUrl = (value: string): boolean => {\n let valid = false;\n try {\n const url = new URL(value);\n valid = url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n valid = false;\n }\n if (!valid) return false;\n\n setPreview(value);\n setHasError(false);\n editField(collection, sectionKey, fieldKey, value);\n setPendingImage({\n file: null,\n localUrl: value,\n sectionKey,\n fieldKey,\n collection,\n docId,\n isExternal: true,\n });\n return true;\n };\n\n const state: EditableImageRenderState = {\n src: imgSrc,\n isEditing,\n saving,\n hasError,\n openFilePicker,\n setExternalUrl,\n imgProps: { src: imgSrc, onError: () => setHasError(true) },\n };\n\n return (\n <div className={className}>\n {/* Internal hidden file input — driven via openFilePicker(). */}\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n disabled={saving}\n onChange={handleFileChange}\n style={{ display: \"none\" }}\n />\n {children ? (\n children(state)\n ) : (\n // eslint-disable-next-line @next/next/no-img-element\n <img {...state.imgProps} alt=\"\" />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\n\nexport interface UseMarkdownEditorOptions {\n initialValue: string;\n onSave: (content: string) => void | Promise<void>;\n}\n\nexport interface MarkdownEditorApi {\n /** Current editor text. */\n value: string;\n setValue: (next: string) => void;\n /** Attach to your `<textarea>` so `insert` can target the selection. */\n textareaRef: React.RefObject<HTMLTextAreaElement | null>;\n /**\n * Wrap the current selection (or insert a placeholder) with `before`/`after`\n * markers — e.g. `insert(\"**\", \"**\", \"bold text\")`. Restores focus and caret.\n */\n insert: (before: string, after?: string, placeholder?: string) => void;\n /** Reset back to `initialValue` (or a provided value). */\n reset: (to?: string) => void;\n /** Persist the current value via the provided `onSave`. */\n save: () => void | Promise<void>;\n charCount: number;\n}\n\n/**\n * Headless markdown-editing logic: value state, a selection-aware `insert`\n * command, and save/reset. The package ships **no** modal, toolbar, icons, or\n * preview renderer — compose those yourself (e.g. with `react-markdown`). This\n * keeps the package free of any UI library or visual opinion.\n *\n * ```tsx\n * const md = useMarkdownEditor({ initialValue, onSave });\n * <textarea ref={md.textareaRef} value={md.value}\n * onChange={(e) => md.setValue(e.target.value)} />\n * <button onClick={() => md.insert(\"**\", \"**\", \"bold\")}>Bold</button>\n * ```\n */\nexport function useMarkdownEditor({\n initialValue,\n onSave,\n}: UseMarkdownEditorOptions): MarkdownEditorApi {\n const [value, setValue] = useState(initialValue);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const insert = useCallback(\n (before: string, after = \"\", placeholder = \"text\") => {\n const textarea = textareaRef.current;\n const start = textarea?.selectionStart ?? value.length;\n const end = textarea?.selectionEnd ?? value.length;\n const selected = value.substring(start, end) || placeholder;\n const next =\n value.substring(0, start) +\n before +\n selected +\n after +\n value.substring(end);\n\n setValue(next);\n\n setTimeout(() => {\n if (!textarea) return;\n textarea.focus();\n const caret = start + before.length + selected.length;\n textarea.setSelectionRange(caret, caret);\n }, 0);\n },\n [value],\n );\n\n const reset = useCallback(\n (to: string = initialValue) => setValue(to),\n [initialValue],\n );\n\n const save = useCallback(() => onSave(value), [onSave, value]);\n\n return {\n value,\n setValue,\n textareaRef,\n insert,\n reset,\n save,\n charCount: value.length,\n };\n}\n"],"mappings":"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","names":["_a","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react"]}