@dalgoridim/headless-cms 0.5.1 → 0.6.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.
- package/README.md +6 -1
- package/dist/client/index.cjs +29 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +29 -0
- package/dist/client/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -234,11 +234,15 @@ ops. Hydrate the lists from the server via `initialCollections`:
|
|
|
234
234
|
```tsx
|
|
235
235
|
import { usePageContext } from "@dalgoridim/headless-cms/client";
|
|
236
236
|
|
|
237
|
-
const { collections, createItem, deleteItem, reorderItems } =
|
|
237
|
+
const { collections, createItem, updateItem, deleteItem, reorderItems } =
|
|
238
|
+
usePageContext();
|
|
238
239
|
|
|
239
240
|
// add — PUT (upsert) with a generated id; returns the new id
|
|
240
241
|
await createItem("projects", { title: "New project", order: collections.projects.length });
|
|
241
242
|
|
|
243
|
+
// edit fields on an existing item — PATCH
|
|
244
|
+
await updateItem("projects", id, { github: "https://github.com/me/repo" });
|
|
245
|
+
|
|
242
246
|
// remove — DELETE
|
|
243
247
|
await deleteItem("projects", id);
|
|
244
248
|
|
|
@@ -249,6 +253,7 @@ await reorderItems("projects", ["id-c", "id-a", "id-b"]);
|
|
|
249
253
|
| Op | Persists via | Notes |
|
|
250
254
|
|---|---|---|
|
|
251
255
|
| `createItem(collection, data, opts?)` | `PUT {base}/{collection}/{id}` | `opts.id` to set the id, `opts.atStart` to prepend; returns the id |
|
|
256
|
+
| `updateItem(collection, id, patch)` | `PATCH {base}/{collection}/{id}` | patches fields on an existing item |
|
|
252
257
|
| `deleteItem(collection, id)` | `DELETE {base}/{collection}/{id}` | |
|
|
253
258
|
| `reorderItems(collection, orderedIds)` | `PATCH {base}/{collection}/{id}` ×N | writes `{ order: index }` per item |
|
|
254
259
|
|
package/dist/client/index.cjs
CHANGED
|
@@ -292,6 +292,34 @@ var PageProvider = ({
|
|
|
292
292
|
},
|
|
293
293
|
[apiBasePath, notify]
|
|
294
294
|
);
|
|
295
|
+
const updateItem = (0, import_react.useCallback)(
|
|
296
|
+
async (collection, id, patch) => {
|
|
297
|
+
let previous = [];
|
|
298
|
+
setCollections((prev) => {
|
|
299
|
+
var _a;
|
|
300
|
+
previous = (_a = prev[collection]) != null ? _a : [];
|
|
301
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
302
|
+
[collection]: previous.map(
|
|
303
|
+
(it) => it.id === id ? __spreadValues(__spreadValues({}, it), patch) : it
|
|
304
|
+
)
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
try {
|
|
308
|
+
const res = await fetch(`${apiBasePath}/${collection}/${id}`, {
|
|
309
|
+
method: "PATCH",
|
|
310
|
+
headers: { "Content-Type": "application/json" },
|
|
311
|
+
body: JSON.stringify(patch)
|
|
312
|
+
});
|
|
313
|
+
if (!res.ok) throw new Error("Failed to update item");
|
|
314
|
+
notify.success("Saved");
|
|
315
|
+
} catch (error) {
|
|
316
|
+
setCollections((prev) => __spreadProps(__spreadValues({}, prev), { [collection]: previous }));
|
|
317
|
+
notify.error("Failed to save");
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
[apiBasePath, notify]
|
|
322
|
+
);
|
|
295
323
|
const deleteItem = (0, import_react.useCallback)(
|
|
296
324
|
async (collection, id) => {
|
|
297
325
|
let removed = [];
|
|
@@ -365,6 +393,7 @@ var PageProvider = ({
|
|
|
365
393
|
saveAll,
|
|
366
394
|
collections,
|
|
367
395
|
createItem,
|
|
396
|
+
updateItem,
|
|
368
397
|
deleteItem,
|
|
369
398
|
reorderItems
|
|
370
399
|
},
|
|
@@ -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 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"]}
|
|
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 /**\n * Patch fields on an existing collection item and persist via `PATCH`.\n * Optimistic with rollback on failure.\n */\n updateItem: (\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n ) => Promise<void>;\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 updateItem = useCallback(\n async (\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n ): Promise<void> => {\n let previous: CollectionItem[] = [];\n setCollections((prev) => {\n previous = prev[collection] ?? [];\n return {\n ...prev,\n [collection]: previous.map((it) =>\n it.id === id ? { ...it, ...patch } : it,\n ),\n };\n });\n\n try {\n const res = await fetch(`${apiBasePath}/${collection}/${id}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(patch),\n });\n if (!res.ok) throw new Error(\"Failed to update item\");\n notify.success(\"Saved\");\n } catch (error) {\n setCollections((prev) => ({ ...prev, [collection]: previous })); // rollback\n notify.error(\"Failed to save\");\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 updateItem,\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAMO;AAgeH;AA3cJ,IAAM,kBAA4B;AAAA,EAChC,SAAS,CAAC,MAAM,QAAQ,KAAK,SAAS,CAAC,EAAE;AAAA,EACzC,OAAO,CAAC,MAAM,QAAQ,MAAM,SAAS,CAAC,EAAE;AAC1C;AA8CA,IAAM,kBAAc,4BAA2C,MAAS;AAExE,IAAM,WAAW,CAAC,YAAoB,eACpC,GAAG,UAAU,IAAI,UAAU;AAmBtB,IAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA,kBAAkB,CAAC;AAAA,EACnB,qBAAqB,CAAC;AAAA,EACtB,cAAc;AAAA,EACd;AAAA,EACA,SAAS;AACX,MAAyB;AACvB,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAyB,eAAe;AACxE,QAAM,CAAC,aAAa,cAAc,QAChC,uBAA2C,kBAAkB;AAC/D,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAyB,CAAC,CAAC;AACrE,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAsB,oBAAI,IAAI,CAAC;AAEzE,QAAM,oBAAoB,cAAc,OAAO;AAE/C,QAAM,sBAAkB;AAAA,IACtB,OAAO,QAAuC;AAC5C,UAAI,IAAI,WAAY,QAAO,IAAI;AAC/B,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,EAAE,IAAI,IAAI,MAAM,QAAQ,OAAO,IAAI,IAAK;AAC9C,aAAO;AAAA,IACT;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,iBAAa;AAAA,IACjB,CAAC,YAAoB,KAAa,YAAqB;AACrD,kBAAY,CAAC,SAAU,iCAClB,OADkB;AAAA,QAErB,CAAC,UAAU,GAAG,iCAAK,KAAK,UAAU,IAApB,EAAuB,CAAC,GAAG,GAAG,QAAQ;AAAA,MACtD,EAAE;AAAA,IACJ;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,gBAAY;AAAA,IAChB,CACE,YACA,YACA,UACA,UACG;AACH,kBAAY,CAAC,SAAS;AApJ5B;AAqJQ,cAAM,kBAAiB,UAAK,UAAU,MAAf,mBAAmB;AAE1C,YAAI,CAAC,gBAAgB;AACnB,kBAAQ,MAAM,sBAAsB,UAAU,IAAI,UAAU,EAAE;AAC9D,iBAAO;AAAA,QACT;AAEA,cAAM,OAAO,SAAS,MAAM,GAAG;AAC/B,cAAM,UAAmB,mBAAK;AAE9B,YAAI,KAAK,WAAW,GAAG;AACrB,kBAAQ,QAAQ,IAAI;AAAA,QACtB,OAAO;AACL,cAAI,UAAmC;AACvC,mBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,gBAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAG,SAAQ,KAAK,CAAC,CAAC,IAAI,CAAC;AAC3C,oBAAQ,KAAK,CAAC,CAAC,IAAI,mBAAM,QAAQ,KAAK,CAAC,CAAC;AACxC,sBAAU,QAAQ,KAAK,CAAC,CAAC;AAAA,UAC3B;AACA,kBAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AAAA,QACnC;AAEA,eAAO,iCACF,OADE;AAAA,UAEL,CAAC,UAAU,GAAG,iCAAK,KAAK,UAAU,IAApB,EAAuB,CAAC,UAAU,GAAG,QAAQ;AAAA,QAC7D;AAAA,MACF,CAAC;AAED,uBAAiB,CAAC,SAAS;AACzB,cAAM,OAAO,IAAI,IAAI,IAAI;AACzB,aAAK,IAAI,SAAS,YAAY,UAAU,CAAC;AACzC,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,sBAAkB,0BAAY,CAAC,UAAwB;AAC3D,qBAAiB,CAAC,SAAS;AAAA,MACzB,GAAG,KAAK;AAAA,QACN,CAAC,QACC,EACE,IAAI,eAAe,MAAM,cACzB,IAAI,eAAe,MAAM,cACzB,IAAI,aAAa,MAAM;AAAA,MAE7B;AAAA,MACA;AAAA,IACF,CAAC;AAED,qBAAiB,CAAC,SAAS;AACzB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,WAAK,IAAI,SAAS,MAAM,YAAY,MAAM,UAAU,CAAC;AACrD,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,cAAU;AAAA,IACd,OAAO,YAAqB;AAC1B,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,WAAW,IAAI,QAAQ,UAAU,IAAI,QAAQ,EAAE;AAAA,QAClD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B;AAAA,MACF;AACA,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,wBAAwB;AAAA,IAC5D;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,kBAAc;AAAA,IAClB,OAAO,YAAoB,eAAuB;AA9NtD;AA+NM,UAAI,OAAQ;AACZ,gBAAU,IAAI;AAEd,UAAI;AACF,cAAM,WAAU,cAAS,UAAU,MAAnB,mBAAuB;AACvC,YAAI,EAAC,mCAAS,OAAM,EAAC,mCAAS,aAAY;AACxC,kBAAQ,MAAM,oBAAoB,UAAU,IAAI,UAAU,EAAE;AAC5D,oBAAU,KAAK;AACf;AAAA,QACF;AAEA,cAAM,SAAS,cAAc;AAAA,UAC3B,CAAC,QACC,IAAI,eAAe,cAAc,IAAI,eAAe;AAAA,QACxD;AAEA,YAAI,iBAA0B,mBAAK;AAEnC,mBAAW,OAAO,QAAQ;AACxB,gBAAM,MAAM,MAAM,gBAAgB,GAAG;AACrC,gBAAM,OAAO,IAAI,SAAS,MAAM,GAAG;AACnC,cAAI,KAAK,WAAW,GAAG;AACrB,6BAAiB,iCAAK,iBAAL,EAAqB,CAAC,IAAI,QAAQ,GAAG,IAAI;AAAA,UAC5D,OAAO;AACL,gBAAI,UAAmC;AACvC,qBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,kBAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAG,SAAQ,KAAK,CAAC,CAAC,IAAI,CAAC;AAC3C,wBAAU,QAAQ,KAAK,CAAC,CAAC;AAAA,YAC3B;AACA,oBAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AAAA,UACnC;AAAA,QACF;AAEA,cAAM,QAAQ,cAAc;AAE5B,oBAAY,CAAC,SAAU,iCAClB,OADkB;AAAA,UAErB,CAAC,UAAU,GAAG,iCAAK,KAAK,UAAU,IAApB,EAAuB,CAAC,UAAU,GAAG,eAAe;AAAA,QACpE,EAAE;AAEF;AAAA,UAAiB,CAAC,SAChB,KAAK;AAAA,YACH,CAAC,QACC,EAAE,IAAI,eAAe,cAAc,IAAI,eAAe;AAAA,UAC1D;AAAA,QACF;AAEA,yBAAiB,CAAC,SAAS;AACzB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,eAAK,OAAO,SAAS,YAAY,UAAU,CAAC;AAC5C,iBAAO;AAAA,QACT,CAAC;AAED,eAAO,QAAQ,6BAA6B;AAAA,MAC9C,SAAS,OAAO;AACd,gBAAQ,MAAM,gBAAgB,KAAK;AACnC,eAAO,MAAM,wBAAwB;AAAA,MACvC,UAAE;AACA,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,UAAU,eAAe,QAAQ,iBAAiB,SAAS,MAAM;AAAA,EACpE;AAEA,QAAM,cAAU,0BAAY,YAAY;AA/R1C;AAgSI,QAAI,UAAU,cAAc,SAAS,EAAG;AACxC,cAAU,IAAI;AAEd,QAAI;AACF,YAAM,kBAAkC,mBAAK;AAE7C,iBAAW,OAAO,eAAe;AAC/B,cAAM,MAAM,MAAM,gBAAgB,GAAG;AAErC,YAAI,CAAC,gBAAgB,IAAI,UAAU;AACjC,0BAAgB,IAAI,UAAU,IAAI,CAAC;AAErC,YAAI,CAAC,gBAAgB,IAAI,UAAU,EAAE,IAAI,UAAU,GAAG;AACpD,0BAAgB,IAAI,UAAU,EAAE,IAAI,UAAU,IAAI;AAAA,YAChD,IAAI,IAAI;AAAA,YACR,YAAY,IAAI;AAAA,UAClB;AAAA,QACF;AAEA,cAAM,OAAO,IAAI,SAAS,MAAM,GAAG;AACnC,YAAI,UACF,gBAAgB,IAAI,UAAU,EAAE,IAAI,UAAU;AAEhD,YAAI,KAAK,WAAW,GAAG;AACrB,0BAAgB,IAAI,UAAU,EAAE,IAAI,UAAU,IAAI,iCAC5C,UAD4C;AAAA,YAEhD,CAAC,IAAI,QAAQ,GAAG;AAAA,UAClB;AAAA,QACF,OAAO;AACL,mBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,gBAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAG,SAAQ,KAAK,CAAC,CAAC,IAAI,CAAC;AAC3C,sBAAU,QAAQ,KAAK,CAAC,CAAC;AAAA,UAC3B;AACA,kBAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AAAA,QACnC;AAAA,MACF;AAEA,iBAAW,SAAS,eAAe;AACjC,cAAM,CAAC,YAAY,GAAG,IAAI,MAAM,MAAM,GAAG;AACzC,cAAM,WAAU,qBAAgB,UAAU,MAA1B,mBAA8B;AAE9C,YAAI,EAAC,mCAAS,OAAM,EAAC,mCAAS,aAAY;AACxC,kBAAQ,MAAM,6BAA6B,KAAK,EAAE;AAClD;AAAA,QACF;AAEA,cAAM,QAAQ,OAAO;AAAA,MACvB;AAEA,kBAAY,eAAe;AAC3B,uBAAiB,CAAC,CAAC;AACnB,uBAAiB,oBAAI,IAAI,CAAC;AAC1B,aAAO,QAAQ,iCAAiC;AAAA,IAClD,SAAS,OAAO;AACd,cAAQ,MAAM,oBAAoB,KAAK;AACvC,aAAO,MAAM,wBAAwB;AAAA,IACvC,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,UAAU,eAAe,eAAe,QAAQ,iBAAiB,SAAS,MAAM,CAAC;AAErF,QAAM,iBAAa;AAAA,IACjB,OACE,YACA,MACA,SACoB;AAlW1B;AAmWM,YAAM,MACJ,wCAAM,OAAN,aACA,sBAAW,WAAX,mBAAmB,eAAnB,gCADA,YAEA,GAAG,UAAU,IAAI,KAAK,IAAI,CAAC;AAC7B,YAAM,OAAuB,iBAAE,MAAO;AAGtC,qBAAe,CAAC,SAAS;AA1W/B,YAAAA;AA2WQ,cAAM,QAAOA,MAAA,KAAK,UAAU,MAAf,OAAAA,MAAoB,CAAC;AAClC,eAAO,iCACF,OADE;AAAA,UAEL,CAAC,UAAU,IAAG,6BAAM,WAAU,CAAC,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,MAAM,IAAI;AAAA,QAChE;AAAA,MACF,CAAC;AAED,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,UAAU,IAAI,EAAE,IAAI;AAAA,UAC5D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QAC3B,CAAC;AACD,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB;AACpD,eAAO,QAAQ,YAAY;AAC3B,eAAO;AAAA,MACT,SAAS,OAAO;AAEd,uBAAe,CAAC,SAAM;AA7X9B,cAAAA;AA6XkC,kDACrB,OADqB;AAAA,YAExB,CAAC,UAAU,KAAIA,MAAA,KAAK,UAAU,MAAf,OAAAA,MAAoB,CAAC,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE;AAAA,UACpE;AAAA,SAAE;AACF,eAAO,MAAM,oBAAoB;AACjC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM;AAAA,EACtB;AAEA,QAAM,iBAAa;AAAA,IACjB,OACE,YACA,IACA,UACkB;AAClB,UAAI,WAA6B,CAAC;AAClC,qBAAe,CAAC,SAAS;AA/Y/B;AAgZQ,oBAAW,UAAK,UAAU,MAAf,YAAoB,CAAC;AAChC,eAAO,iCACF,OADE;AAAA,UAEL,CAAC,UAAU,GAAG,SAAS;AAAA,YAAI,CAAC,OAC1B,GAAG,OAAO,KAAK,kCAAK,KAAO,SAAU;AAAA,UACvC;AAAA,QACF;AAAA,MACF,CAAC;AAED,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,UAAU,IAAI,EAAE,IAAI;AAAA,UAC5D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,KAAK;AAAA,QAC5B,CAAC;AACD,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB;AACpD,eAAO,QAAQ,OAAO;AAAA,MACxB,SAAS,OAAO;AACd,uBAAe,CAAC,SAAU,iCAAK,OAAL,EAAW,CAAC,UAAU,GAAG,SAAS,EAAE;AAC9D,eAAO,MAAM,gBAAgB;AAC7B,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM;AAAA,EACtB;AAEA,QAAM,iBAAa;AAAA,IACjB,OAAO,YAAoB,OAA8B;AACvD,UAAI,UAA4B,CAAC;AACjC,qBAAe,CAAC,SAAS;AA7a/B;AA8aQ,mBAAU,UAAK,UAAU,MAAf,YAAoB,CAAC;AAC/B,eAAO,iCACF,OADE;AAAA,UAEL,CAAC,UAAU,GAAG,QAAQ,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE;AAAA,QACnD;AAAA,MACF,CAAC;AAED,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,UAAU,IAAI,EAAE,IAAI;AAAA,UAC5D,QAAQ;AAAA,QACV,CAAC;AACD,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uBAAuB;AACpD,eAAO,QAAQ,cAAc;AAAA,MAC/B,SAAS,OAAO;AACd,uBAAe,CAAC,SAAU,iCAAK,OAAL,EAAW,CAAC,UAAU,GAAG,QAAQ,EAAE;AAC7D,eAAO,MAAM,uBAAuB;AACpC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM;AAAA,EACtB;AAEA,QAAM,mBAAe;AAAA,IACnB,OAAO,YAAoB,eAAwC;AACjE,UAAI,WAA6B,CAAC;AAClC,UAAI,OAAyB,CAAC;AAC9B,qBAAe,CAAC,SAAS;AAxc/B;AAycQ,oBAAW,UAAK,UAAU,MAAf,YAAoB,CAAC;AAChC,cAAM,OAAO,IAAI,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AACtD,eAAO,WAAW,QAAQ,CAAC,IAAI,UAAU;AACvC,gBAAM,OAAO,KAAK,IAAI,EAAE;AACxB,iBAAO,OAAO,CAAC,iCAAK,OAAL,EAAW,OAAO,MAAM,EAAC,IAAI,CAAC;AAAA,QAC/C,CAAC;AACD,eAAO,iCAAK,OAAL,EAAW,CAAC,UAAU,GAAG,KAAK;AAAA,MACvC,CAAC;AAED,UAAI;AACF,cAAM,QAAQ;AAAA,UACZ,KAAK,IAAI,OAAO,OAAO;AACrB,kBAAM,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,UAAU,IAAI,GAAG,EAAE,IAAI;AAAA,cAC/D,QAAQ;AAAA,cACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,cAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;AAAA,YAC1C,CAAC;AACD,gBAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,mBAAmB;AAAA,UAClD,CAAC;AAAA,QACH;AACA,eAAO,QAAQ,eAAe;AAAA,MAChC,SAAS,OAAO;AACd,uBAAe,CAAC,SAAU,iCAAK,OAAL,EAAW,CAAC,UAAU,GAAG,SAAS,EAAE;AAC9D,eAAO,MAAM,wBAAwB;AACrC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM;AAAA,EACtB;AAEA,SACE;AAAA,IAAC,YAAY;AAAA,IAAZ;AAAA,MACC,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,IAAM,iBAAiB,MAAM;AAClC,QAAM,cAAU,yBAAW,WAAW;AACtC,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,mDAAmD;AACrE,SAAO;AACT;;;AClgBA,IAAAC,gBAA0D;AAoCtD,IAAAC,sBAAA;AAxBG,IAAM,qBAAiB,6BAAwC,MAAS;AAExE,SAAS,aAA2B;AACzC,QAAM,UAAM,0BAAW,cAAc;AACrC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGG;AACD,SACE,6CAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAErD;;;ACtCA,IAAAC,gBAAgE;AA6DrD,IAAAC,sBAAA;AAnCX,IAAM,qBAAqB,CAAC,QAAiC;AAE7D,SAAS,eAAe,KAAc,MAAuB;AAC3D,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,UAAmB;AAEvB,aAAW,OAAO,MAAM;AACtB,QAAI,WAAW,QAAS,QAAoC,GAAG,MAAM;AACnE,aAAO;AACT,cAAW,QAAoC,GAAG;AAAA,EACpD;AAEA,SAAO;AACT;AAEe,SAAR,gBAAiC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,KAAK;AAAA,EACL,cAAc;AAChB,GAAyB;AAnDzB;AAoDE,QAAM,EAAE,UAAU,UAAU,IAAI,eAAe;AAC/C,QAAM,EAAE,UAAU,IAAI,WAAW;AAEjC,QAAM,WAAU,cAAS,UAAU,MAAnB,mBAAuB;AACvC,QAAM,OACH,oBAAe,SAAS,QAAQ,MAAhC,YACA,OAAO,aAAa,WAAW,WAAW;AAE7C,QAAM,YAAY;AAElB,MAAI,CAAC,WAAW;AACd,WAAO,6CAAC,aAAU,WAAuB,sBAAY,GAAG,GAAE;AAAA,EAC5D;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,IAAI,YAAY;AAAA,EAChB;AACF,GAcG;AACD,QAAM,EAAE,UAAU,IAAI,WAAW;AACjC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,GAAG;AAC9C,QAAM,iBAAa,sBAAoB,IAAI;AAC3C,QAAM,aAAS,sBAAO,GAAG;AAEzB,+BAAU,MAAM;AACd,QAAI,CAAC,aAAa,OAAO,YAAY,KAAK;AACxC,aAAO,UAAU;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,KAAK,SAAS,CAAC;AAEnB,+BAAU,MAAM;AACd,QAAI,CAAC,aAAa,OAAO,YAAY,WAAW;AAC9C,mBAAa,OAAO,OAAO;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,WAAW,SAAS,CAAC;AAEzB,QAAM,kBAAc,2BAAY,MAAM;AACpC,QAAI,WAAW,SAAS;AACtB,mBAAa,WAAW,QAAQ,eAAe,EAAE;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAa,2BAAY,MAAM;AACnC,iBAAa,KAAK;AAClB,QAAI,cAAc,KAAK;AACrB,gBAAU,YAAY,YAAY,UAAU,SAAS;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,WAAW,KAAK,YAAY,YAAY,UAAU,SAAS,CAAC;AAEhE,QAAM,kBAAc,2BAAY,MAAM;AACpC,iBAAa,IAAI;AACjB,QAAI,WAAW,SAAS;AACtB,iBAAW,QAAQ,cAAc;AACjC,iBAAW,MAAM;AACf,YAAI,WAAW,SAAS;AACtB,gBAAM,QAAQ,SAAS,YAAY;AACnC,gBAAM,MAAM,OAAO,aAAa;AAChC,gBAAM,mBAAmB,WAAW,OAAO;AAC3C,gBAAM,SAAS,KAAK;AACpB,qCAAK;AACL,qCAAK,SAAS;AAAA,QAChB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,KAAK;AAAA,MACL;AAAA,MACA,qBAAkB;AAAA,MAClB,oBAAkB,YAAY,KAAK;AAAA,MACnC,oBAAkB,YAAY,KAAK;AAAA,MACnC,iBAAiB;AAAA,MACjB,gCAA8B;AAAA,MAC9B,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MAER,WAAC,aAAa,YAAY,SAAS;AAAA;AAAA,IAZ/B,YAAY,YAAY;AAAA,EAa/B;AAEJ;;;ACvKA,IAAAC,gBAAwC;AA0HpC,IAAAC,sBAAA;AApFW,SAAR,cAA+B;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,EAAE,UAAU,IAAI,WAAW;AACjC,QAAM,EAAE,WAAW,iBAAiB,eAAe,OAAO,IACxD,eAAe;AAEjB,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,GAAG;AAC1C,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,KAAK;AAC9C,QAAM,eAAW,sBAAyB,IAAI;AAE9C,QAAM,eAAe,cAAc;AAAA,IACjC,CAAC,QAAQ,IAAI,eAAe,cAAc,IAAI,aAAa;AAAA,EAC7D;AACA,QAAM,UAAS,6CAAc,aAAY;AAEzC,QAAM,mBAAmB,CAAC,MAA2C;AA9DvE;AA+DI,QAAI,OAAQ;AACZ,UAAM,QAAO,OAAE,OAAO,UAAT,mBAAiB;AAC9B,QAAI,CAAC,KAAM;AAEX,UAAM,WAAW,IAAI,gBAAgB,IAAI;AACzC,eAAW,QAAQ;AACnB,gBAAY,KAAK;AAEjB,cAAU,YAAY,YAAY,UAAU,QAAQ;AACpD,oBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,QAAM,iBAAiB,MAAM;AAnF/B;AAoFI,QAAI,OAAQ;AACZ,mBAAS,YAAT,mBAAkB;AAAA,EACpB;AAEA,QAAM,iBAAiB,CAAC,UAA2B;AACjD,QAAI,QAAQ;AACZ,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,KAAK;AACzB,cAAQ,IAAI,aAAa,WAAW,IAAI,aAAa;AAAA,IACvD,SAAQ;AACN,cAAQ;AAAA,IACV;AACA,QAAI,CAAC,MAAO,QAAO;AAEnB,eAAW,KAAK;AAChB,gBAAY,KAAK;AACjB,cAAU,YAAY,YAAY,UAAU,KAAK;AACjD,oBAAgB;AAAA,MACd,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACd,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,QAAkC;AAAA,IACtC,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,EAAE,KAAK,QAAQ,SAAS,MAAM,YAAY,IAAI,EAAE;AAAA,EAC5D;AAEA,SACE,8CAAC,SAAI,WAEH;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,QAAO;AAAA,QACP,UAAU;AAAA,QACV,UAAU;AAAA,QACV,OAAO,EAAE,SAAS,OAAO;AAAA;AAAA,IAC3B;AAAA,IACC,WACC,SAAS,KAAK;AAAA;AAAA,MAGd,6CAAC,wCAAQ,MAAM,WAAd,EAAwB,KAAI,KAAG;AAAA;AAAA,KAEpC;AAEJ;;;AC5IA,IAAAC,gBAA8C;AAsCvC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAAgD;AAC9C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,YAAY;AAC/C,QAAM,kBAAc,sBAA4B,IAAI;AAEpD,QAAM,aAAS;AAAA,IACb,CAAC,QAAgB,QAAQ,IAAI,cAAc,WAAW;AAhD1D;AAiDM,YAAM,WAAW,YAAY;AAC7B,YAAM,SAAQ,0CAAU,mBAAV,YAA4B,MAAM;AAChD,YAAM,OAAM,0CAAU,iBAAV,YAA0B,MAAM;AAC5C,YAAM,WAAW,MAAM,UAAU,OAAO,GAAG,KAAK;AAChD,YAAM,OACJ,MAAM,UAAU,GAAG,KAAK,IACxB,SACA,WACA,QACA,MAAM,UAAU,GAAG;AAErB,eAAS,IAAI;AAEb,iBAAW,MAAM;AACf,YAAI,CAAC,SAAU;AACf,iBAAS,MAAM;AACf,cAAM,QAAQ,QAAQ,OAAO,SAAS,SAAS;AAC/C,iBAAS,kBAAkB,OAAO,KAAK;AAAA,MACzC,GAAG,CAAC;AAAA,IACN;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,YAAQ;AAAA,IACZ,CAAC,KAAa,iBAAiB,SAAS,EAAE;AAAA,IAC1C,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,WAAO,2BAAY,MAAM,OAAO,KAAK,GAAG,CAAC,QAAQ,KAAK,CAAC;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,MAAM;AAAA,EACnB;AACF;","names":["_a","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react"]}
|
package/dist/client/index.d.cts
CHANGED
|
@@ -32,6 +32,11 @@ interface PageContextType {
|
|
|
32
32
|
id?: string;
|
|
33
33
|
atStart?: boolean;
|
|
34
34
|
}) => Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Patch fields on an existing collection item and persist via `PATCH`.
|
|
37
|
+
* Optimistic with rollback on failure.
|
|
38
|
+
*/
|
|
39
|
+
updateItem: (collection: string, id: string, patch: Record<string, unknown>) => Promise<void>;
|
|
35
40
|
/** Remove an item from a collection and persist via `DELETE`. Optimistic. */
|
|
36
41
|
deleteItem: (collection: string, id: string) => Promise<void>;
|
|
37
42
|
/**
|
package/dist/client/index.d.ts
CHANGED
|
@@ -32,6 +32,11 @@ interface PageContextType {
|
|
|
32
32
|
id?: string;
|
|
33
33
|
atStart?: boolean;
|
|
34
34
|
}) => Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Patch fields on an existing collection item and persist via `PATCH`.
|
|
37
|
+
* Optimistic with rollback on failure.
|
|
38
|
+
*/
|
|
39
|
+
updateItem: (collection: string, id: string, patch: Record<string, unknown>) => Promise<void>;
|
|
35
40
|
/** Remove an item from a collection and persist via `DELETE`. Optimistic. */
|
|
36
41
|
deleteItem: (collection: string, id: string) => Promise<void>;
|
|
37
42
|
/**
|
package/dist/client/index.js
CHANGED
|
@@ -267,6 +267,34 @@ var PageProvider = ({
|
|
|
267
267
|
},
|
|
268
268
|
[apiBasePath, notify]
|
|
269
269
|
);
|
|
270
|
+
const updateItem = useCallback(
|
|
271
|
+
async (collection, id, patch) => {
|
|
272
|
+
let previous = [];
|
|
273
|
+
setCollections((prev) => {
|
|
274
|
+
var _a;
|
|
275
|
+
previous = (_a = prev[collection]) != null ? _a : [];
|
|
276
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
277
|
+
[collection]: previous.map(
|
|
278
|
+
(it) => it.id === id ? __spreadValues(__spreadValues({}, it), patch) : it
|
|
279
|
+
)
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
try {
|
|
283
|
+
const res = await fetch(`${apiBasePath}/${collection}/${id}`, {
|
|
284
|
+
method: "PATCH",
|
|
285
|
+
headers: { "Content-Type": "application/json" },
|
|
286
|
+
body: JSON.stringify(patch)
|
|
287
|
+
});
|
|
288
|
+
if (!res.ok) throw new Error("Failed to update item");
|
|
289
|
+
notify.success("Saved");
|
|
290
|
+
} catch (error) {
|
|
291
|
+
setCollections((prev) => __spreadProps(__spreadValues({}, prev), { [collection]: previous }));
|
|
292
|
+
notify.error("Failed to save");
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
[apiBasePath, notify]
|
|
297
|
+
);
|
|
270
298
|
const deleteItem = useCallback(
|
|
271
299
|
async (collection, id) => {
|
|
272
300
|
let removed = [];
|
|
@@ -340,6 +368,7 @@ var PageProvider = ({
|
|
|
340
368
|
saveAll,
|
|
341
369
|
collections,
|
|
342
370
|
createItem,
|
|
371
|
+
updateItem,
|
|
343
372
|
deleteItem,
|
|
344
373
|
reorderItems
|
|
345
374
|
},
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/PageProvider.tsx","../../src/client/auth.tsx","../../src/client/ContentEditSpan.tsx","../../src/client/EditableImage.tsx","../../src/client/MarkdownEditor.tsx"],"sourcesContent":["\"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","createContext","useContext","jsx","useState","useCallback","jsx","useState","useCallback","useRef","useState","jsx","useState","useRef","useCallback","useRef","useState"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/PageProvider.tsx","../../src/client/auth.tsx","../../src/client/ContentEditSpan.tsx","../../src/client/EditableImage.tsx","../../src/client/MarkdownEditor.tsx"],"sourcesContent":["\"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 /**\n * Patch fields on an existing collection item and persist via `PATCH`.\n * Optimistic with rollback on failure.\n */\n updateItem: (\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n ) => Promise<void>;\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 updateItem = useCallback(\n async (\n collection: string,\n id: string,\n patch: Record<string, unknown>,\n ): Promise<void> => {\n let previous: CollectionItem[] = [];\n setCollections((prev) => {\n previous = prev[collection] ?? [];\n return {\n ...prev,\n [collection]: previous.map((it) =>\n it.id === id ? { ...it, ...patch } : it,\n ),\n };\n });\n\n try {\n const res = await fetch(`${apiBasePath}/${collection}/${id}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(patch),\n });\n if (!res.ok) throw new Error(\"Failed to update item\");\n notify.success(\"Saved\");\n } catch (error) {\n setCollections((prev) => ({ ...prev, [collection]: previous })); // rollback\n notify.error(\"Failed to save\");\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 updateItem,\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","createContext","useContext","jsx","useState","useCallback","jsx","useState","useCallback","useRef","useState","jsx","useState","useRef","useCallback","useRef","useState"]}
|