@drawnagency/primitives 0.1.44 → 0.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/editor/IndicatorPill.d.ts.map +1 -1
- package/dist/components/editor/SectionWrapper.d.ts +1 -1
- package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
- package/dist/components/primitives/useEditableRichText.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/hooks/useEditorPersistence.d.ts +1 -0
- package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
- package/dist/hooks/useEditorPublish.d.ts +3 -1
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/dist/hooks/useMediaPipeline.d.ts +2 -0
- package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
- package/dist/lib/dexie.d.ts +27 -0
- package/dist/lib/dexie.d.ts.map +1 -1
- package/dist/lib/dexie.js +30 -0
- package/dist/lib/registry.d.ts +0 -2
- package/dist/lib/registry.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/editor/IndicatorPill.tsx +4 -1
- package/src/components/editor/SectionWrapper.tsx +0 -30
- package/src/components/primitives/useEditableRichText.ts +12 -0
- package/src/components/shell/EditorShell.tsx +66 -19
- package/src/hooks/useEditorPersistence.ts +41 -1
- package/src/hooks/useEditorPublish.ts +32 -16
- package/src/hooks/useMediaPipeline.ts +12 -0
- package/src/lib/dexie.ts +51 -0
- package/src/lib/registry.ts +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndicatorPill.d.ts","sourceRoot":"","sources":["../../../src/components/editor/IndicatorPill.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,MAAM;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,eAAe,EACf,SAAS,GACV,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"IndicatorPill.d.ts","sourceRoot":"","sources":["../../../src/components/editor/IndicatorPill.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,MAAM;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,eAAe,EACf,SAAS,GACV,EAAE,kBAAkB,2CA6FpB"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { WrapperProps } from "../../lib/registry";
|
|
2
|
-
export declare function SectionWrapper({ sectionId, sectionType, status, dirty, index, isLast, definition, options, audiences, access, onAccessChange, onStatusChange, onSectionChange, onReorder, onRequestInsert, onDelete,
|
|
2
|
+
export declare function SectionWrapper({ sectionId, sectionType, status, dirty, index, isLast, definition, options, audiences, access, onAccessChange, onStatusChange, onSectionChange, onReorder, onRequestInsert, onDelete, mainStatus, contentDiffersFromMain, isLocalOnly, children, }: WrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=SectionWrapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionWrapper.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SectionWrapper.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"SectionWrapper.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SectionWrapper.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,WAAW,EACX,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,OAAO,EACP,SAAS,EACT,MAAM,EACN,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,EACT,eAAe,EACf,QAAQ,EACR,UAAU,EACV,sBAAsB,EACtB,WAAW,EACX,QAAQ,GACT,EAAE,YAAY,2CAiRd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditableRichText.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/useEditableRichText.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,UAAU,0BAA0B;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,yBAAyB;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,0BAA0B,GAAG,yBAAyB,
|
|
1
|
+
{"version":3,"file":"useEditableRichText.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/useEditableRichText.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,UAAU,0BAA0B;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,yBAAyB;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,0BAA0B,GAAG,yBAAyB,CAmExD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAuDjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAQxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAuDjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAQxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,2CA0sBP"}
|
|
@@ -3,6 +3,7 @@ import type { SiteIndex, SiteConfig } from "../schemas/site-config";
|
|
|
3
3
|
export declare function useEditorPersistence(siteIndexRef: React.RefObject<SiteIndex>): {
|
|
4
4
|
markSectionDirty: (sectionId: string, content: SectionContent) => void;
|
|
5
5
|
markIndexDirty: () => void;
|
|
6
|
+
removeSection: (sectionId: string) => void;
|
|
6
7
|
persistConfig: (config: SiteConfig) => Promise<void>;
|
|
7
8
|
flushNow: () => Promise<void>;
|
|
8
9
|
cancelPendingFlush: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorPersistence.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPersistence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AASpE,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"useEditorPersistence.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPersistence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AASpE,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC;kCA8E7D,MAAM,WAAW,cAAc;;+BAgBC,MAAM;4BASH,UAAU;;;;;EAyB5D"}
|
|
@@ -12,6 +12,8 @@ interface PublishDeps {
|
|
|
12
12
|
deletedSectionIds?: string[];
|
|
13
13
|
onSuccess: () => void;
|
|
14
14
|
mediaManifest: MediaManifest;
|
|
15
|
+
manifestDirty: boolean;
|
|
16
|
+
clearManifestDirty: () => void;
|
|
15
17
|
pendingMediaItems: MediaItem[];
|
|
16
18
|
pendingMediaDeletions: string[];
|
|
17
19
|
onMediaPublished: (publishedItems: MediaItem[], publishedDeletions: string[]) => void;
|
|
@@ -19,7 +21,7 @@ interface PublishDeps {
|
|
|
19
21
|
onPublishComplete?: () => void;
|
|
20
22
|
}
|
|
21
23
|
export type PublishAction = "idle" | "saving" | "publishing";
|
|
22
|
-
export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, }: PublishDeps): {
|
|
24
|
+
export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, manifestDirty, clearManifestDirty, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, }: PublishDeps): {
|
|
23
25
|
publishAction: PublishAction;
|
|
24
26
|
publishFeedback: string | null;
|
|
25
27
|
handleSave: () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AAQD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7D,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,WAAW;;;;;;
|
|
1
|
+
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AAQD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7D,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,WAAW;;;;;;EAwRb"}
|
|
@@ -54,6 +54,8 @@ export declare function useMediaPipeline({ siteConfig, mediaManifest, setMediaMa
|
|
|
54
54
|
setPendingLocalUrls: import("react").Dispatch<import("react").SetStateAction<Record<string, string>>>;
|
|
55
55
|
pendingDeletions: string[];
|
|
56
56
|
setPendingDeletions: import("react").Dispatch<import("react").SetStateAction<string[]>>;
|
|
57
|
+
manifestDirty: boolean;
|
|
58
|
+
setManifestDirty: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
57
59
|
handleMediaUpload: (files: File[]) => Promise<void>;
|
|
58
60
|
handleMediaDelete: (ids: string[]) => Promise<void>;
|
|
59
61
|
handleMediaAltChange: (id: string, alt: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AA0BxF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CACxE;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,EAAE,qBAAqB
|
|
1
|
+
{"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AA0BxF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CACxE;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAoI8B,IAAI,EAAE;6BAyBR,MAAM,EAAE;+BA+CZ,MAAM,OAAO,MAAM;;;wBAkC1B,IAAI,eAAe,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;EA6EpF"}
|
package/dist/lib/dexie.d.ts
CHANGED
|
@@ -20,6 +20,33 @@ export declare function getDirtySections(): Promise<{
|
|
|
20
20
|
sectionId: string;
|
|
21
21
|
content: SectionContent;
|
|
22
22
|
}[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Like getDirtySections but includes each row's updatedAt, so callers can later
|
|
25
|
+
* clear only the rows they actually saved without clobbering edits made during
|
|
26
|
+
* an in-flight save (compare-and-clear on updatedAt).
|
|
27
|
+
*/
|
|
28
|
+
export declare function getDirtySectionRows(): Promise<{
|
|
29
|
+
sectionId: string;
|
|
30
|
+
content: SectionContent;
|
|
31
|
+
updatedAt: string;
|
|
32
|
+
}[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Remove section content rows (used when a section is deleted, so a dirty row
|
|
35
|
+
* for a section no longer in the index can't be sent to /api/save and rejected).
|
|
36
|
+
*/
|
|
37
|
+
export declare function deleteSectionRows(ids: string[]): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Post-save cleanup that preserves edits made WHILE the save was in flight.
|
|
40
|
+
* Section rows are cleared only when their updatedAt still matches what was sent
|
|
41
|
+
* (i.e. they weren't re-edited during the request). The index/meta/config rows
|
|
42
|
+
* are cleared wholesale (the save persisted them upstream), and contentCache is
|
|
43
|
+
* cleared so a subsequent reload re-fetches the saved-branch content instead of
|
|
44
|
+
* serving the stale pre-save snapshot keyed at the production SHA.
|
|
45
|
+
*/
|
|
46
|
+
export declare function discardSavedChanges(savedSections: {
|
|
47
|
+
sectionId: string;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
}[]): Promise<void>;
|
|
23
50
|
export declare function persistAll(sections: {
|
|
24
51
|
sectionId: string;
|
|
25
52
|
content: SectionContent;
|
package/dist/lib/dexie.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/dexie.js
CHANGED
|
@@ -157,6 +157,33 @@ async function getDirtySections() {
|
|
|
157
157
|
const rows = await getDb().sections.filter((row) => row.dirty).toArray();
|
|
158
158
|
return rows.map((row) => ({ sectionId: row.sectionId, content: row.content }));
|
|
159
159
|
}
|
|
160
|
+
async function getDirtySectionRows() {
|
|
161
|
+
const rows = await getDb().sections.filter((row) => row.dirty).toArray();
|
|
162
|
+
return rows.map((row) => ({ sectionId: row.sectionId, content: row.content, updatedAt: row.updatedAt }));
|
|
163
|
+
}
|
|
164
|
+
async function deleteSectionRows(ids) {
|
|
165
|
+
if (ids.length === 0) return;
|
|
166
|
+
await getDb().sections.bulkDelete(ids);
|
|
167
|
+
}
|
|
168
|
+
async function discardSavedChanges(savedSections) {
|
|
169
|
+
const database = getDb();
|
|
170
|
+
await database.transaction(
|
|
171
|
+
"rw",
|
|
172
|
+
[database.sections, database.siteIndex, database.meta, database.siteConfig, database.contentCache],
|
|
173
|
+
async () => {
|
|
174
|
+
for (const { sectionId, updatedAt } of savedSections) {
|
|
175
|
+
const row = await database.sections.get(sectionId);
|
|
176
|
+
if (row && row.updatedAt === updatedAt) {
|
|
177
|
+
await database.sections.delete(sectionId);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
await database.siteIndex.clear();
|
|
181
|
+
await database.meta.clear();
|
|
182
|
+
await database.siteConfig.clear();
|
|
183
|
+
await database.contentCache.clear();
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
}
|
|
160
187
|
async function persistAll(sections, siteIndex, deletedSectionIds, siteConfig) {
|
|
161
188
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
162
189
|
const database = getDb();
|
|
@@ -294,10 +321,13 @@ export {
|
|
|
294
321
|
cacheContent,
|
|
295
322
|
checkForLocalChanges,
|
|
296
323
|
clearPendingMedia,
|
|
324
|
+
deleteSectionRows,
|
|
297
325
|
discardLocalChanges,
|
|
326
|
+
discardSavedChanges,
|
|
298
327
|
getBranchShas,
|
|
299
328
|
getCachedContent,
|
|
300
329
|
getDeletedSections,
|
|
330
|
+
getDirtySectionRows,
|
|
301
331
|
getDirtySections,
|
|
302
332
|
getMediaManifest,
|
|
303
333
|
getPendingMediaBlobs,
|
package/dist/lib/registry.d.ts
CHANGED
|
@@ -72,8 +72,6 @@ export interface WrapperProps {
|
|
|
72
72
|
onReorder?: (fromIndex: number, toIndex: number) => void;
|
|
73
73
|
onRequestInsert?: (index: number) => void;
|
|
74
74
|
onDelete?: () => void;
|
|
75
|
-
isDeleted?: boolean;
|
|
76
|
-
onUndoDelete?: () => void;
|
|
77
75
|
mainStatus?: string | null;
|
|
78
76
|
contentDiffersFromMain?: boolean;
|
|
79
77
|
isLocalOnly?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -76,7 +76,10 @@ export function IndicatorPill({
|
|
|
76
76
|
aria-expanded={clickContent ? clickOpen : undefined}
|
|
77
77
|
aria-label={ariaLabel}
|
|
78
78
|
className={cn(
|
|
79
|
-
|
|
79
|
+
// align-middle keeps the inline-flex pill centred in its line box regardless of
|
|
80
|
+
// whether it leads with a status/audience dot — without it, the dot-less variant's
|
|
81
|
+
// text baseline sits ~2px lower than the dotted variant, misaligning the two pills.
|
|
82
|
+
"cursor-pointer inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium align-middle",
|
|
80
83
|
"border border-base-200 hover:bg-base-accent",
|
|
81
84
|
buttonClassName,
|
|
82
85
|
)}
|
|
@@ -11,7 +11,6 @@ import { useEditorContext } from "../shell/EditorContext";
|
|
|
11
11
|
import { useEditorModal } from "../shell/EditorModalContext";
|
|
12
12
|
import type { WrapperProps } from "../../lib/registry";
|
|
13
13
|
import { cn } from "../../lib/cn";
|
|
14
|
-
import { Button } from "../shared/Button";
|
|
15
14
|
|
|
16
15
|
export function SectionWrapper({
|
|
17
16
|
sectionId,
|
|
@@ -30,8 +29,6 @@ export function SectionWrapper({
|
|
|
30
29
|
onReorder,
|
|
31
30
|
onRequestInsert,
|
|
32
31
|
onDelete,
|
|
33
|
-
isDeleted,
|
|
34
|
-
onUndoDelete,
|
|
35
32
|
mainStatus,
|
|
36
33
|
contentDiffersFromMain,
|
|
37
34
|
isLocalOnly,
|
|
@@ -180,33 +177,6 @@ export function SectionWrapper({
|
|
|
180
177
|
}
|
|
181
178
|
}
|
|
182
179
|
|
|
183
|
-
if (isDeleted && isEditMode) {
|
|
184
|
-
return (
|
|
185
|
-
<div
|
|
186
|
-
ref={blockRef}
|
|
187
|
-
className="relative opacity-30 pointer-events-none"
|
|
188
|
-
data-section-id={sectionId}
|
|
189
|
-
data-section-type={sectionType}
|
|
190
|
-
>
|
|
191
|
-
<div className="pointer-events-auto absolute right-0 bottom-full z-30 mb-1">
|
|
192
|
-
<Button
|
|
193
|
-
variant="secondary"
|
|
194
|
-
size="sm"
|
|
195
|
-
onClick={onUndoDelete}
|
|
196
|
-
className="pointer-events-auto"
|
|
197
|
-
>
|
|
198
|
-
Undo delete
|
|
199
|
-
</Button>
|
|
200
|
-
</div>
|
|
201
|
-
{children}
|
|
202
|
-
</div>
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (isDeleted) {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
180
|
if (!isEditMode) {
|
|
211
181
|
if (status && status !== "live") {
|
|
212
182
|
return (
|
|
@@ -46,6 +46,18 @@ export function useEditableRichText({
|
|
|
46
46
|
const ed = new Editor({
|
|
47
47
|
extensions,
|
|
48
48
|
content: valueRef.current,
|
|
49
|
+
// Commit edits to onChange on every transaction, not only on deactivate().
|
|
50
|
+
// Without this, content lives only inside TipTap until blur; clicking a
|
|
51
|
+
// Save button while the editor is still focused flushes persistence before
|
|
52
|
+
// the (150ms-delayed) blur->deactivate commit fires, silently dropping the
|
|
53
|
+
// in-progress edit. Live-committing keeps persistence in sync continuously.
|
|
54
|
+
onUpdate: ({ editor: updated }) => {
|
|
55
|
+
const html = updated.getHTML();
|
|
56
|
+
if (html !== valueRef.current) {
|
|
57
|
+
valueRef.current = html;
|
|
58
|
+
onChangeRef.current(html);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
49
61
|
});
|
|
50
62
|
editorRef.current = ed;
|
|
51
63
|
setEditor(ed);
|
|
@@ -103,6 +103,7 @@ export default function EditorShell({
|
|
|
103
103
|
const [mediaModalMode, setMediaModalMode] = useState<"select" | "manage">("manage");
|
|
104
104
|
const [mediaSelectCallback, setMediaSelectCallback] = useState<((id: string) => void) | null>(null);
|
|
105
105
|
const [deletedSections, setDeletedSections] = useState<Set<string>>(new Set());
|
|
106
|
+
const [pendingDeleteSectionId, setPendingDeleteSectionId] = useState<string | null>(null);
|
|
106
107
|
const [savedSha, setSavedSha] = useState<string | null>(null);
|
|
107
108
|
const [mainSha, setMainSha] = useState<string | null>(null);
|
|
108
109
|
const [changedSectionIds, setChangedSectionIds] = useState<Set<string>>(new Set());
|
|
@@ -114,6 +115,10 @@ export default function EditorShell({
|
|
|
114
115
|
const [rejectedUploads, setRejectedUploads] = useState<{ name: string; reason: "size" | "type" }[]>([]);
|
|
115
116
|
|
|
116
117
|
const siteIndexRef = useRef<SiteIndex>({ siteId, order: [], sections: {} });
|
|
118
|
+
// Ids of sections that exist on the remote (loaded from server, or persisted by
|
|
119
|
+
// a prior save). Used so deleting a section that was only ever added locally
|
|
120
|
+
// doesn't enqueue a GitHub file deletion for a path that was never committed.
|
|
121
|
+
const remoteSectionIdsRef = useRef<Set<string>>(new Set());
|
|
117
122
|
const fontLinkRef = useRef<HTMLLinkElement | null>(null);
|
|
118
123
|
useEffect(() => { siteIndexRef.current = siteIndex; }, [siteIndex]);
|
|
119
124
|
useEffect(() => { void ensureSanitizer(); }, []);
|
|
@@ -146,8 +151,12 @@ export default function EditorShell({
|
|
|
146
151
|
setDirtySectionIds(new Set());
|
|
147
152
|
setDeletedSections(new Set());
|
|
148
153
|
setLocalChangesExist(false);
|
|
154
|
+
// Everything in the index now exists remotely (it was just saved).
|
|
155
|
+
remoteSectionIdsRef.current = new Set(siteIndexRef.current.order);
|
|
149
156
|
},
|
|
150
157
|
mediaManifest,
|
|
158
|
+
manifestDirty: mediaPipeline.manifestDirty,
|
|
159
|
+
clearManifestDirty: () => mediaPipeline.setManifestDirty(false),
|
|
151
160
|
pendingMediaItems: mediaPipeline.pendingMediaItems,
|
|
152
161
|
pendingMediaDeletions: mediaPipeline.pendingDeletions,
|
|
153
162
|
onMediaPublished: (publishedItems, publishedDeletions) => {
|
|
@@ -268,6 +277,7 @@ export default function EditorShell({
|
|
|
268
277
|
if (cancelled) return;
|
|
269
278
|
setSections(loadedSections);
|
|
270
279
|
setSiteIndex(loadedIndex);
|
|
280
|
+
remoteSectionIdsRef.current = new Set(loadedIndex.order);
|
|
271
281
|
setSiteConfig(loadedConfig);
|
|
272
282
|
setMediaManifest(loadedManifest);
|
|
273
283
|
siteIndexRef.current = loadedIndex;
|
|
@@ -330,6 +340,15 @@ export default function EditorShell({
|
|
|
330
340
|
}).filter((s): s is LoadedSection => s !== null),
|
|
331
341
|
);
|
|
332
342
|
setSiteIndex(restoredIndex);
|
|
343
|
+
// Re-derive pending deletions: any remotely-existing section absent from the
|
|
344
|
+
// restored index was deleted locally before this reload. The deletion intent
|
|
345
|
+
// isn't persisted directly, so reconstruct it here — otherwise the section's
|
|
346
|
+
// GitHub file would be orphaned (index omits it, but it's never deleted).
|
|
347
|
+
const restoredIds = new Set(restoredIndex.order);
|
|
348
|
+
const rediscoveredDeletions = new Set(
|
|
349
|
+
[...remoteSectionIdsRef.current].filter((id) => !restoredIds.has(id)),
|
|
350
|
+
);
|
|
351
|
+
setDeletedSections(rediscoveredDeletions);
|
|
333
352
|
} else {
|
|
334
353
|
setSections((prev) =>
|
|
335
354
|
prev.map((loaded) => {
|
|
@@ -360,6 +379,8 @@ export default function EditorShell({
|
|
|
360
379
|
await discardLocalChanges();
|
|
361
380
|
setLocalChangesExist(false);
|
|
362
381
|
setDirtySectionIds(new Set());
|
|
382
|
+
setDeletedSections(new Set());
|
|
383
|
+
mediaPipeline.setManifestDirty(false);
|
|
363
384
|
for (const url of Object.values(mediaPipeline.pendingLocalUrls)) {
|
|
364
385
|
URL.revokeObjectURL(url);
|
|
365
386
|
}
|
|
@@ -371,6 +392,7 @@ export default function EditorShell({
|
|
|
371
392
|
setSections(cached.sections);
|
|
372
393
|
setSiteIndex(cached.index);
|
|
373
394
|
siteIndexRef.current = cached.index;
|
|
395
|
+
remoteSectionIdsRef.current = new Set(cached.index.order);
|
|
374
396
|
const parsed = SiteConfigSchema.safeParse(cached.siteConfig);
|
|
375
397
|
if (parsed.success) {
|
|
376
398
|
setSiteConfig(parsed.data);
|
|
@@ -444,9 +466,28 @@ export default function EditorShell({
|
|
|
444
466
|
[persistence],
|
|
445
467
|
);
|
|
446
468
|
|
|
447
|
-
|
|
469
|
+
// Runs after the confirmation modal is accepted. Removes the section from the
|
|
470
|
+
// live editor immediately (no ghost/undo), persists the filtered index, and
|
|
471
|
+
// records the id so its JSON file is deleted from GitHub on the next save.
|
|
472
|
+
const performDeleteSection = useCallback(
|
|
448
473
|
(sectionId: string) => {
|
|
449
|
-
|
|
474
|
+
setSections((prev) => prev.filter((loaded) => loaded.section.id !== sectionId));
|
|
475
|
+
setSiteIndex((prev) => {
|
|
476
|
+
const nextSections = { ...prev.sections };
|
|
477
|
+
delete nextSections[sectionId];
|
|
478
|
+
return {
|
|
479
|
+
...prev,
|
|
480
|
+
order: prev.order.filter((id) => id !== sectionId),
|
|
481
|
+
sections: nextSections,
|
|
482
|
+
};
|
|
483
|
+
});
|
|
484
|
+
// Only schedule a GitHub file deletion if the section exists remotely. A
|
|
485
|
+
// section that was added locally and never saved has no committed file, so
|
|
486
|
+
// sending a deletion for it would target a non-existent path.
|
|
487
|
+
if (remoteSectionIdsRef.current.has(sectionId)) {
|
|
488
|
+
setDeletedSections((prev) => new Set(prev).add(sectionId));
|
|
489
|
+
}
|
|
490
|
+
persistence.removeSection(sectionId);
|
|
450
491
|
setLocalChangesExist(true);
|
|
451
492
|
setDirtySectionIds((prev) => {
|
|
452
493
|
const next = new Set(prev);
|
|
@@ -454,16 +495,13 @@ export default function EditorShell({
|
|
|
454
495
|
return next;
|
|
455
496
|
});
|
|
456
497
|
},
|
|
457
|
-
[],
|
|
498
|
+
[persistence],
|
|
458
499
|
);
|
|
459
500
|
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return next;
|
|
465
|
-
});
|
|
466
|
-
}, []);
|
|
501
|
+
const confirmDeleteSection = useCallback(() => {
|
|
502
|
+
if (pendingDeleteSectionId) performDeleteSection(pendingDeleteSectionId);
|
|
503
|
+
setPendingDeleteSectionId(null);
|
|
504
|
+
}, [pendingDeleteSectionId, performDeleteSection]);
|
|
467
505
|
|
|
468
506
|
const onReorderSections = useCallback(
|
|
469
507
|
(fromIndex: number, toIndex: number) => {
|
|
@@ -655,12 +693,10 @@ export default function EditorShell({
|
|
|
655
693
|
sections={sections}
|
|
656
694
|
audiences={audiences}
|
|
657
695
|
dirtySectionIds={dirtySectionIds}
|
|
658
|
-
deletedSections={deletedSections}
|
|
659
696
|
isPublishing={publishAction !== "idle"}
|
|
660
697
|
onSectionChange={onSectionChange}
|
|
661
698
|
onAddSection={onAddSection}
|
|
662
|
-
onDeleteSection={
|
|
663
|
-
onUndoDelete={handleUndoDelete}
|
|
699
|
+
onDeleteSection={setPendingDeleteSectionId}
|
|
664
700
|
onReorderSections={onReorderSections}
|
|
665
701
|
onAccessChange={onAccessChange}
|
|
666
702
|
onStatusChange={onStatusChange}
|
|
@@ -679,6 +715,23 @@ export default function EditorShell({
|
|
|
679
715
|
capabilities={capabilities}
|
|
680
716
|
currentUser={currentUser}
|
|
681
717
|
/>
|
|
718
|
+
<EditorModal
|
|
719
|
+
isOpen={pendingDeleteSectionId !== null}
|
|
720
|
+
onClose={() => setPendingDeleteSectionId(null)}
|
|
721
|
+
title="Delete Section"
|
|
722
|
+
>
|
|
723
|
+
<p className="mb-4 text-sm text-base-contrast-light">
|
|
724
|
+
Delete this section? It will be removed from the page now and deleted permanently when you save or publish.
|
|
725
|
+
</p>
|
|
726
|
+
<div className="flex justify-end gap-3">
|
|
727
|
+
<Button variant="secondary" size="md" onClick={() => setPendingDeleteSectionId(null)}>
|
|
728
|
+
Cancel
|
|
729
|
+
</Button>
|
|
730
|
+
<Button variant="destructive" size="md" onClick={confirmDeleteSection}>
|
|
731
|
+
Delete
|
|
732
|
+
</Button>
|
|
733
|
+
</div>
|
|
734
|
+
</EditorModal>
|
|
682
735
|
<EditorModal
|
|
683
736
|
isOpen={showDiscardConfirm}
|
|
684
737
|
onClose={() => setShowDiscardConfirm(false)}
|
|
@@ -860,12 +913,10 @@ function EditorContent({
|
|
|
860
913
|
sections,
|
|
861
914
|
audiences,
|
|
862
915
|
dirtySectionIds,
|
|
863
|
-
deletedSections,
|
|
864
916
|
isPublishing,
|
|
865
917
|
onSectionChange,
|
|
866
918
|
onAddSection,
|
|
867
919
|
onDeleteSection,
|
|
868
|
-
onUndoDelete,
|
|
869
920
|
onReorderSections,
|
|
870
921
|
onAccessChange,
|
|
871
922
|
onStatusChange,
|
|
@@ -876,12 +927,10 @@ function EditorContent({
|
|
|
876
927
|
sections: LoadedSection[];
|
|
877
928
|
audiences: Audience[];
|
|
878
929
|
dirtySectionIds: Set<string>;
|
|
879
|
-
deletedSections: Set<string>;
|
|
880
930
|
isPublishing: boolean;
|
|
881
931
|
onSectionChange: (sectionId: string, content: SectionContent) => void;
|
|
882
932
|
onAddSection: (insertIndex: number, type: string) => void;
|
|
883
933
|
onDeleteSection: (sectionId: string) => void;
|
|
884
|
-
onUndoDelete: (sectionId: string) => void;
|
|
885
934
|
onReorderSections: (fromIndex: number, toIndex: number) => void;
|
|
886
935
|
onAccessChange: (sectionId: string, access: string[]) => void;
|
|
887
936
|
onStatusChange: (sectionId: string, status: "draft" | "live" | "archived") => void;
|
|
@@ -971,8 +1020,6 @@ function EditorContent({
|
|
|
971
1020
|
}}
|
|
972
1021
|
audiences={audiences}
|
|
973
1022
|
access={meta.access}
|
|
974
|
-
isDeleted={deletedSections.has(section.id)}
|
|
975
|
-
onUndoDelete={() => onUndoDelete(section.id)}
|
|
976
1023
|
onAccessChange={editingEnabled ? (a) => onAccessChange(section.id, a) : undefined}
|
|
977
1024
|
onStatusChange={editingEnabled ? (s) => onStatusChange(section.id, s) : undefined}
|
|
978
1025
|
onSectionChange={editingEnabled ? (settingsResult) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useRef, useEffect } from "react";
|
|
2
2
|
import type { SectionContent } from "../schemas/sections";
|
|
3
3
|
import type { SiteIndex, SiteConfig } from "../schemas/site-config";
|
|
4
|
-
import { persistAll, persistSiteConfig } from "../lib/dexie";
|
|
4
|
+
import { persistAll, persistSiteConfig, deleteSectionRows } from "../lib/dexie";
|
|
5
5
|
|
|
6
6
|
interface PersistenceState {
|
|
7
7
|
pendingSections: Map<string, SectionContent>;
|
|
@@ -60,6 +60,32 @@ export function useEditorPersistence(siteIndexRef: React.RefObject<SiteIndex>) {
|
|
|
60
60
|
};
|
|
61
61
|
}, [flushToDexie]);
|
|
62
62
|
|
|
63
|
+
// Flush pending edits when the tab is hidden or unloaded. The unmount cleanup
|
|
64
|
+
// above is fire-and-forget and does not reliably complete during real document
|
|
65
|
+
// teardown (closing the tab / navigating away), so a debounced edit made within
|
|
66
|
+
// the flush window would otherwise be lost before it ever reached Dexie.
|
|
67
|
+
// visibilitychange->hidden is the reliable teardown signal on desktop + mobile;
|
|
68
|
+
// pagehide is a secondary trigger for hard navigations.
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (typeof document === "undefined") return;
|
|
71
|
+
const flushOnExit = () => {
|
|
72
|
+
if (flushTimerRef.current) {
|
|
73
|
+
clearTimeout(flushTimerRef.current);
|
|
74
|
+
flushTimerRef.current = null;
|
|
75
|
+
}
|
|
76
|
+
void flushToDexie();
|
|
77
|
+
};
|
|
78
|
+
const onVisibilityChange = () => {
|
|
79
|
+
if (document.visibilityState === "hidden") flushOnExit();
|
|
80
|
+
};
|
|
81
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
82
|
+
window.addEventListener("pagehide", flushOnExit);
|
|
83
|
+
return () => {
|
|
84
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
85
|
+
window.removeEventListener("pagehide", flushOnExit);
|
|
86
|
+
};
|
|
87
|
+
}, [flushToDexie]);
|
|
88
|
+
|
|
63
89
|
const markSectionDirty = useCallback(
|
|
64
90
|
(sectionId: string, content: SectionContent) => {
|
|
65
91
|
state.current.pendingSections.set(sectionId, content);
|
|
@@ -73,6 +99,19 @@ export function useEditorPersistence(siteIndexRef: React.RefObject<SiteIndex>) {
|
|
|
73
99
|
scheduleFlush();
|
|
74
100
|
}, [scheduleFlush]);
|
|
75
101
|
|
|
102
|
+
// Drop a section from local persistence (used when it is deleted). Removes any
|
|
103
|
+
// pending in-memory edit and the persisted Dexie row so a now-orphaned dirty
|
|
104
|
+
// row can't be sent to /api/save (which would reject it as absent from the
|
|
105
|
+
// index), and marks the index dirty so the filtered order is persisted.
|
|
106
|
+
const removeSection = useCallback((sectionId: string) => {
|
|
107
|
+
state.current.pendingSections.delete(sectionId);
|
|
108
|
+
state.current.indexDirty = true;
|
|
109
|
+
deleteSectionRows([sectionId]).catch((err) => {
|
|
110
|
+
console.error("Failed to delete section row from Dexie:", err);
|
|
111
|
+
});
|
|
112
|
+
scheduleFlush();
|
|
113
|
+
}, [scheduleFlush]);
|
|
114
|
+
|
|
76
115
|
const persistConfig = useCallback(async (config: SiteConfig) => {
|
|
77
116
|
state.current.configDirty = true;
|
|
78
117
|
await persistSiteConfig(config);
|
|
@@ -91,6 +130,7 @@ export function useEditorPersistence(siteIndexRef: React.RefObject<SiteIndex>) {
|
|
|
91
130
|
return {
|
|
92
131
|
markSectionDirty,
|
|
93
132
|
markIndexDirty,
|
|
133
|
+
removeSection,
|
|
94
134
|
persistConfig,
|
|
95
135
|
flushNow: flushToDexie,
|
|
96
136
|
cancelPendingFlush,
|
|
@@ -2,7 +2,7 @@ import { useState, useCallback, useRef, useEffect } from "react";
|
|
|
2
2
|
import type { SiteIndex, SiteConfig } from "../schemas/site-config";
|
|
3
3
|
import type { LoadedSection } from "../lib/loader";
|
|
4
4
|
import type { MediaManifest, MediaItem } from "../media/types";
|
|
5
|
-
import {
|
|
5
|
+
import { getDirtySectionRows, hasLocalChanges, discardSavedChanges, cacheContent, getPendingMediaBlobs, clearPendingMedia } from "../lib/dexie";
|
|
6
6
|
|
|
7
7
|
function blobToBase64(blob: Blob): Promise<string> {
|
|
8
8
|
return new Promise((resolve, reject) => {
|
|
@@ -27,6 +27,8 @@ interface PublishDeps {
|
|
|
27
27
|
deletedSectionIds?: string[];
|
|
28
28
|
onSuccess: () => void;
|
|
29
29
|
mediaManifest: MediaManifest;
|
|
30
|
+
manifestDirty: boolean;
|
|
31
|
+
clearManifestDirty: () => void;
|
|
30
32
|
pendingMediaItems: MediaItem[];
|
|
31
33
|
pendingMediaDeletions: string[];
|
|
32
34
|
onMediaPublished: (publishedItems: MediaItem[], publishedDeletions: string[]) => void;
|
|
@@ -53,6 +55,8 @@ export function useEditorPublish({
|
|
|
53
55
|
deletedSectionIds,
|
|
54
56
|
onSuccess,
|
|
55
57
|
mediaManifest,
|
|
58
|
+
manifestDirty,
|
|
59
|
+
clearManifestDirty,
|
|
56
60
|
pendingMediaItems,
|
|
57
61
|
pendingMediaDeletions,
|
|
58
62
|
onMediaPublished,
|
|
@@ -80,8 +84,9 @@ export function useEditorPublish({
|
|
|
80
84
|
pendingItems: MediaItem[],
|
|
81
85
|
pendingDeletions: string[],
|
|
82
86
|
manifest: MediaManifest,
|
|
87
|
+
manifestOnlyDirty: boolean,
|
|
83
88
|
): Promise<GatheredMedia> {
|
|
84
|
-
const hasMediaChanges = pendingItems.length > 0 || pendingDeletions.length > 0;
|
|
89
|
+
const hasMediaChanges = pendingItems.length > 0 || pendingDeletions.length > 0 || manifestOnlyDirty;
|
|
85
90
|
const mediaUploads: { item: MediaItem; blobs: { path: string; base64: string }[] }[] = [];
|
|
86
91
|
|
|
87
92
|
const deletionSet = new Set(pendingDeletions);
|
|
@@ -134,12 +139,14 @@ export function useEditorPublish({
|
|
|
134
139
|
pendingMediaItems: MediaItem[];
|
|
135
140
|
pendingMediaDeletions: string[];
|
|
136
141
|
mediaManifest: MediaManifest;
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
manifestDirty: boolean;
|
|
143
|
+
}): Promise<{ sha: string; savedSections: { sectionId: string; updatedAt: string }[] }> {
|
|
144
|
+
const dirty = await getDirtySectionRows();
|
|
139
145
|
const gathered = await gatherMediaPayload(
|
|
140
146
|
args.pendingMediaItems,
|
|
141
147
|
args.pendingMediaDeletions,
|
|
142
148
|
args.mediaManifest,
|
|
149
|
+
args.manifestDirty,
|
|
143
150
|
);
|
|
144
151
|
|
|
145
152
|
let siteIndex = args.siteIndexRef.current;
|
|
@@ -189,7 +196,10 @@ export function useEditorPublish({
|
|
|
189
196
|
if (!responseData?.sha) {
|
|
190
197
|
throw new Error("Save response missing SHA");
|
|
191
198
|
}
|
|
192
|
-
return
|
|
199
|
+
return {
|
|
200
|
+
sha: responseData.sha as string,
|
|
201
|
+
savedSections: dirty.map(({ sectionId, updatedAt }) => ({ sectionId, updatedAt })),
|
|
202
|
+
};
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
const handleSave = useCallback(async () => {
|
|
@@ -204,21 +214,25 @@ export function useEditorPublish({
|
|
|
204
214
|
await flushNow();
|
|
205
215
|
|
|
206
216
|
const hasChanges = await hasLocalChanges();
|
|
207
|
-
const hasMediaChanges = pendingMediaItems.length > 0 || pendingMediaDeletions.length > 0;
|
|
217
|
+
const hasMediaChanges = pendingMediaItems.length > 0 || pendingMediaDeletions.length > 0 || manifestDirty;
|
|
208
218
|
const hasDeletedSections = (deletedSectionIds?.length ?? 0) > 0;
|
|
209
219
|
if (!hasChanges && !isConfigDirty() && !hasMediaChanges && !hasDeletedSections) {
|
|
210
220
|
setPublishAction("idle");
|
|
211
221
|
return;
|
|
212
222
|
}
|
|
213
223
|
|
|
214
|
-
const { sha } = await performSave({
|
|
224
|
+
const { sha, savedSections } = await performSave({
|
|
215
225
|
siteConfig, siteIndexRef, deletedSectionIds,
|
|
216
|
-
isConfigDirty, pendingMediaItems, pendingMediaDeletions, mediaManifest,
|
|
226
|
+
isConfigDirty, pendingMediaItems, pendingMediaDeletions, mediaManifest, manifestDirty,
|
|
217
227
|
});
|
|
218
228
|
|
|
219
|
-
|
|
229
|
+
// Clears only the section rows we actually sent (preserving edits typed
|
|
230
|
+
// during the request) and invalidates contentCache so a reload reflects
|
|
231
|
+
// the just-saved content instead of showing the stale pre-save snapshot.
|
|
232
|
+
await discardSavedChanges(savedSections);
|
|
220
233
|
await clearPendingMedia();
|
|
221
234
|
clearConfigDirty();
|
|
235
|
+
clearManifestDirty();
|
|
222
236
|
onSuccess();
|
|
223
237
|
onMediaPublished(pendingMediaItems, pendingMediaDeletions);
|
|
224
238
|
onShasUpdated(sha, null);
|
|
@@ -231,7 +245,7 @@ export function useEditorPublish({
|
|
|
231
245
|
inFlightRef.current = false;
|
|
232
246
|
setPublishAction("idle");
|
|
233
247
|
}
|
|
234
|
-
}, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback]);
|
|
248
|
+
}, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, deletedSectionIds, onSuccess, mediaManifest, manifestDirty, clearManifestDirty, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback]);
|
|
235
249
|
|
|
236
250
|
const handlePublish = useCallback(async () => {
|
|
237
251
|
setPublishAction("publishing");
|
|
@@ -276,15 +290,16 @@ export function useEditorPublish({
|
|
|
276
290
|
await flushNow();
|
|
277
291
|
|
|
278
292
|
const hasChanges = await hasLocalChanges();
|
|
279
|
-
const hasMediaChanges = pendingMediaItems.length > 0 || pendingMediaDeletions.length > 0;
|
|
293
|
+
const hasMediaChanges = pendingMediaItems.length > 0 || pendingMediaDeletions.length > 0 || manifestDirty;
|
|
280
294
|
const hasDeletedSections = (deletedSectionIds?.length ?? 0) > 0;
|
|
281
295
|
const hasLocalEdits = hasChanges || isConfigDirty() || hasMediaChanges || hasDeletedSections;
|
|
282
296
|
|
|
297
|
+
let savedSections: { sectionId: string; updatedAt: string }[] = [];
|
|
283
298
|
if (hasLocalEdits) {
|
|
284
|
-
await performSave({
|
|
299
|
+
({ savedSections } = await performSave({
|
|
285
300
|
siteConfig, siteIndexRef, deletedSectionIds,
|
|
286
|
-
isConfigDirty, pendingMediaItems, pendingMediaDeletions, mediaManifest,
|
|
287
|
-
});
|
|
301
|
+
isConfigDirty, pendingMediaItems, pendingMediaDeletions, mediaManifest, manifestDirty,
|
|
302
|
+
}));
|
|
288
303
|
}
|
|
289
304
|
|
|
290
305
|
setPublishAction("publishing");
|
|
@@ -306,10 +321,11 @@ export function useEditorPublish({
|
|
|
306
321
|
const { sha } = publishData;
|
|
307
322
|
|
|
308
323
|
if (hasLocalEdits) {
|
|
309
|
-
await
|
|
324
|
+
await discardSavedChanges(savedSections);
|
|
310
325
|
await clearPendingMedia();
|
|
311
326
|
await cacheContent(sha, sections, siteIndexRef.current, siteConfig);
|
|
312
327
|
clearConfigDirty();
|
|
328
|
+
clearManifestDirty();
|
|
313
329
|
onSuccess();
|
|
314
330
|
onMediaPublished(pendingMediaItems, pendingMediaDeletions);
|
|
315
331
|
}
|
|
@@ -323,7 +339,7 @@ export function useEditorPublish({
|
|
|
323
339
|
inFlightRef.current = false;
|
|
324
340
|
setPublishAction("idle");
|
|
325
341
|
}
|
|
326
|
-
}, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback, onPublishComplete]);
|
|
342
|
+
}, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, manifestDirty, clearManifestDirty, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback, onPublishComplete]);
|
|
327
343
|
|
|
328
344
|
return { publishAction, publishFeedback, handleSave, handlePublish, handleSaveAndPublish };
|
|
329
345
|
}
|
|
@@ -56,6 +56,13 @@ export function useMediaPipeline({
|
|
|
56
56
|
const [pendingMediaItems, setPendingMediaItems] = useState<LibraryMediaItem[]>([]);
|
|
57
57
|
const [pendingLocalUrls, setPendingLocalUrls] = useState<Record<string, string>>({});
|
|
58
58
|
const [pendingDeletions, setPendingDeletions] = useState<string[]>([]);
|
|
59
|
+
// True when the manifest holds unsaved edits that produce no upload/deletion —
|
|
60
|
+
// e.g. alt text on an already-published image. Without this the save path's
|
|
61
|
+
// hasMediaChanges check (uploads/deletions only) skips the change and the edit
|
|
62
|
+
// is silently dropped. In-session only (not persisted to Dexie): an unsaved alt
|
|
63
|
+
// edit is lost on reload, same as before this flag existed — but it is no longer
|
|
64
|
+
// dropped when the editor DOES save in the same session.
|
|
65
|
+
const [manifestDirty, setManifestDirty] = useState(false);
|
|
59
66
|
|
|
60
67
|
const queueRef = useRef<ProcessingQueue | null>(null);
|
|
61
68
|
const uploadCallbacksRef = useRef<Map<string, (imageId: string) => void>>(new Map());
|
|
@@ -257,6 +264,9 @@ export function useMediaPipeline({
|
|
|
257
264
|
setPendingMediaItems((prev) =>
|
|
258
265
|
prev.map((item) => (item.id === id ? { ...item, alt } : item)),
|
|
259
266
|
);
|
|
267
|
+
// Flag the manifest dirty so the save path includes it (alt-only edits on
|
|
268
|
+
// published images produce no upload/deletion and would otherwise be skipped).
|
|
269
|
+
setManifestDirty(true);
|
|
260
270
|
setLocalChangesExist(true);
|
|
261
271
|
}, [setMediaManifest, setLocalChangesExist]);
|
|
262
272
|
|
|
@@ -347,6 +357,8 @@ export function useMediaPipeline({
|
|
|
347
357
|
setPendingLocalUrls,
|
|
348
358
|
pendingDeletions,
|
|
349
359
|
setPendingDeletions,
|
|
360
|
+
manifestDirty,
|
|
361
|
+
setManifestDirty,
|
|
350
362
|
handleMediaUpload,
|
|
351
363
|
handleMediaDelete,
|
|
352
364
|
handleMediaAltChange,
|
package/src/lib/dexie.ts
CHANGED
|
@@ -242,6 +242,57 @@ export async function getDirtySections(): Promise<
|
|
|
242
242
|
return rows.map((row) => ({ sectionId: row.sectionId, content: row.content }));
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Like getDirtySections but includes each row's updatedAt, so callers can later
|
|
247
|
+
* clear only the rows they actually saved without clobbering edits made during
|
|
248
|
+
* an in-flight save (compare-and-clear on updatedAt).
|
|
249
|
+
*/
|
|
250
|
+
export async function getDirtySectionRows(): Promise<
|
|
251
|
+
{ sectionId: string; content: SectionContent; updatedAt: string }[]
|
|
252
|
+
> {
|
|
253
|
+
const rows = await getDb().sections.filter((row) => row.dirty).toArray();
|
|
254
|
+
return rows.map((row) => ({ sectionId: row.sectionId, content: row.content, updatedAt: row.updatedAt }));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Remove section content rows (used when a section is deleted, so a dirty row
|
|
259
|
+
* for a section no longer in the index can't be sent to /api/save and rejected).
|
|
260
|
+
*/
|
|
261
|
+
export async function deleteSectionRows(ids: string[]): Promise<void> {
|
|
262
|
+
if (ids.length === 0) return;
|
|
263
|
+
await getDb().sections.bulkDelete(ids);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Post-save cleanup that preserves edits made WHILE the save was in flight.
|
|
268
|
+
* Section rows are cleared only when their updatedAt still matches what was sent
|
|
269
|
+
* (i.e. they weren't re-edited during the request). The index/meta/config rows
|
|
270
|
+
* are cleared wholesale (the save persisted them upstream), and contentCache is
|
|
271
|
+
* cleared so a subsequent reload re-fetches the saved-branch content instead of
|
|
272
|
+
* serving the stale pre-save snapshot keyed at the production SHA.
|
|
273
|
+
*/
|
|
274
|
+
export async function discardSavedChanges(
|
|
275
|
+
savedSections: { sectionId: string; updatedAt: string }[],
|
|
276
|
+
): Promise<void> {
|
|
277
|
+
const database = getDb();
|
|
278
|
+
await database.transaction(
|
|
279
|
+
"rw",
|
|
280
|
+
[database.sections, database.siteIndex, database.meta, database.siteConfig, database.contentCache],
|
|
281
|
+
async () => {
|
|
282
|
+
for (const { sectionId, updatedAt } of savedSections) {
|
|
283
|
+
const row = await database.sections.get(sectionId);
|
|
284
|
+
if (row && row.updatedAt === updatedAt) {
|
|
285
|
+
await database.sections.delete(sectionId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
await database.siteIndex.clear();
|
|
289
|
+
await database.meta.clear();
|
|
290
|
+
await database.siteConfig.clear();
|
|
291
|
+
await database.contentCache.clear();
|
|
292
|
+
},
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
245
296
|
export async function persistAll(
|
|
246
297
|
sections: { sectionId: string; content: SectionContent }[],
|
|
247
298
|
siteIndex?: SiteIndex,
|
package/src/lib/registry.ts
CHANGED
|
@@ -82,8 +82,6 @@ export interface WrapperProps {
|
|
|
82
82
|
onReorder?: (fromIndex: number, toIndex: number) => void;
|
|
83
83
|
onRequestInsert?: (index: number) => void;
|
|
84
84
|
onDelete?: () => void;
|
|
85
|
-
isDeleted?: boolean;
|
|
86
|
-
onUndoDelete?: () => void;
|
|
87
85
|
mainStatus?: string | null;
|
|
88
86
|
contentDiffersFromMain?: boolean;
|
|
89
87
|
isLocalOnly?: boolean;
|