@drawnagency/primitives 0.1.116 → 0.1.118
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/{chunk-L7WTERU5.js → chunk-4NSOKEA7.js} +21 -3
- package/dist/{chunk-MSBHMY3C.js → chunk-ERLIL4RU.js} +3 -3
- package/dist/{chunk-LNQUUC2S.js → chunk-HRX3XJBU.js} +1 -1
- package/dist/{chunk-HUOW3ZV2.js → chunk-ZOTRHHUB.js} +1 -1
- package/dist/components/primitives/EditablePlainText.d.ts.map +1 -1
- package/dist/components/primitives/index.js +2 -2
- package/dist/components/sections/brand-guide.js +2 -2
- package/dist/components/sections/register-schemas.js +4 -4
- package/dist/components/sections/register.js +4 -4
- package/dist/components/shell/AudienceDetailsPanel.d.ts +3 -1
- package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/hooks/useEditorPublish.d.ts +9 -1
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/primitives/EditablePlainText.tsx +27 -3
- package/src/components/shell/AudienceDetailsPanel.tsx +6 -4
- package/src/components/shell/EditorShell.tsx +34 -1
- package/src/hooks/useEditorPublish.ts +18 -1
|
@@ -66,6 +66,22 @@ function useEditablePlainText({
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// src/components/primitives/EditablePlainText.tsx
|
|
69
|
+
function caretOffset(el) {
|
|
70
|
+
const sel = window.getSelection();
|
|
71
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
72
|
+
const range = sel.getRangeAt(0);
|
|
73
|
+
if (!el.contains(range.endContainer)) return null;
|
|
74
|
+
const before = document.createRange();
|
|
75
|
+
before.selectNodeContents(el);
|
|
76
|
+
before.setEnd(range.endContainer, range.endOffset);
|
|
77
|
+
return before.toString().length;
|
|
78
|
+
}
|
|
79
|
+
function placeCaret(el, offset) {
|
|
80
|
+
const sel = window.getSelection();
|
|
81
|
+
if (!sel) return;
|
|
82
|
+
if (el.firstChild) sel.collapse(el.firstChild, offset);
|
|
83
|
+
else sel.collapse(el, 0);
|
|
84
|
+
}
|
|
69
85
|
function EditablePlainText({
|
|
70
86
|
tag,
|
|
71
87
|
value,
|
|
@@ -86,9 +102,11 @@ function EditablePlainText({
|
|
|
86
102
|
shouldPreventEnter
|
|
87
103
|
} = useEditablePlainText({ value, onChange, multiline });
|
|
88
104
|
useEffect2(() => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
const el = ref.current;
|
|
106
|
+
if (!el || el.textContent === localValue) return;
|
|
107
|
+
const caret = document.activeElement === el ? caretOffset(el) : null;
|
|
108
|
+
el.textContent = localValue;
|
|
109
|
+
if (caret !== null) placeCaret(el, Math.min(caret, localValue.length));
|
|
92
110
|
}, [localValue]);
|
|
93
111
|
const onInput = useCallback2(() => {
|
|
94
112
|
if (ref.current) {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
pluralize,
|
|
5
5
|
stripHtmlToPlainText,
|
|
6
6
|
truncate
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HRX3XJBU.js";
|
|
8
8
|
import {
|
|
9
9
|
DeleteButton,
|
|
10
10
|
DragHandle,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
EditableRichText,
|
|
26
26
|
defineHeadingSection,
|
|
27
27
|
useSanitizedHtml
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ZOTRHHUB.js";
|
|
29
29
|
import {
|
|
30
30
|
EditablePlainText,
|
|
31
31
|
ImageDropZone,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
containerGridClass,
|
|
36
36
|
masonryColumnsClass,
|
|
37
37
|
spacerHiddenClass
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-4NSOKEA7.js";
|
|
39
39
|
import {
|
|
40
40
|
DropEdgeIndicator,
|
|
41
41
|
useMediaLibrary,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditablePlainText.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/EditablePlainText.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAGf,UAAU,sBACR,SAAQ,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACrD,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"EditablePlainText.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/EditablePlainText.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAGf,UAAU,sBACR,SAAQ,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;IACrD,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAqBD,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,KAAK,EACL,QAAQ,EACR,UAAU,EACV,SAAiB,EACjB,WAAW,EACX,SAAS,EACT,GAAG,SAAS,EACb,EAAE,sBAAsB,0FAoFxB"}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
EditableRichText,
|
|
3
3
|
LinkPopover,
|
|
4
4
|
defineHeadingSection
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-ZOTRHHUB.js";
|
|
6
6
|
import {
|
|
7
7
|
EditableGrid,
|
|
8
8
|
EditableList,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
ImageDropZone,
|
|
11
11
|
MediaBlock,
|
|
12
12
|
ResolvedMedia
|
|
13
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-4NSOKEA7.js";
|
|
14
14
|
import {
|
|
15
15
|
LinkField,
|
|
16
16
|
PagesContext,
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
ColorItemSchema,
|
|
3
3
|
ColorSpaceSchema,
|
|
4
4
|
brandGuideSectionDefs
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-HRX3XJBU.js";
|
|
6
6
|
import "../../chunk-JR24U7FJ.js";
|
|
7
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-4NSOKEA7.js";
|
|
8
8
|
import "../../chunk-RIITGTV3.js";
|
|
9
9
|
import "../../chunk-DIMDRBFK.js";
|
|
10
10
|
import "../../chunk-IIDKW7AV.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
allSectionDefs
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-ERLIL4RU.js";
|
|
4
|
+
import "../../chunk-HRX3XJBU.js";
|
|
5
5
|
import "../../chunk-CEFFP3CH.js";
|
|
6
6
|
import "../../chunk-JR24U7FJ.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-ZOTRHHUB.js";
|
|
8
|
+
import "../../chunk-4NSOKEA7.js";
|
|
9
9
|
import "../../chunk-RIITGTV3.js";
|
|
10
10
|
import "../../chunk-JCDZAFX2.js";
|
|
11
11
|
import "../../chunk-DIMDRBFK.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
allSectionDefs
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-ERLIL4RU.js";
|
|
4
|
+
import "../../chunk-HRX3XJBU.js";
|
|
5
5
|
import "../../chunk-CEFFP3CH.js";
|
|
6
6
|
import "../../chunk-JR24U7FJ.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-ZOTRHHUB.js";
|
|
8
|
+
import "../../chunk-4NSOKEA7.js";
|
|
9
9
|
import "../../chunk-RIITGTV3.js";
|
|
10
10
|
import "../../chunk-JCDZAFX2.js";
|
|
11
11
|
import "../../chunk-DIMDRBFK.js";
|
|
@@ -23,12 +23,14 @@ interface Props {
|
|
|
23
23
|
* an audience-filtered site.
|
|
24
24
|
*/
|
|
25
25
|
canImpersonate?: boolean;
|
|
26
|
+
/** Open the view in a new tab — set by the editor, so it keeps the editor tab. */
|
|
27
|
+
viewAsInNewTab?: boolean;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* The Audience Details page body: who can see what, and what password to share.
|
|
29
31
|
* Reachable by editors and the site's default audience (the client), so the
|
|
30
32
|
* client can hand a new partner access without going through us.
|
|
31
33
|
*/
|
|
32
|
-
export declare function AudienceDetailsPanel({ audiences, revealSupported, canImpersonate }: Props): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
export declare function AudienceDetailsPanel({ audiences, revealSupported, canImpersonate, viewAsInNewTab }: Props): import("react/jsx-runtime").JSX.Element;
|
|
33
35
|
export default AudienceDetailsPanel;
|
|
34
36
|
//# sourceMappingURL=AudienceDetailsPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudienceDetailsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/shell/AudienceDetailsPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,6BAA6B,CAAC;AAElF,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC/D,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,UAAU,KAAK;IACb,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAoOD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,cAAsB,EAAE,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"AudienceDetailsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/shell/AudienceDetailsPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,6BAA6B,CAAC;AAElF,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC/D,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,UAAU,KAAK;IACb,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAoOD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,cAAsB,EAAE,cAAsB,EAAE,EAAE,KAAK,2CAgBzH;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAuDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AA6DnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA8BxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAQD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAuDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AA6DnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA8BxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAQD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CAkwEP;AA8ED;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK/B"}
|
|
@@ -38,6 +38,10 @@ interface PublishDeps {
|
|
|
38
38
|
onShasUpdated: (savedSha: string | null, mainSha: string | null) => void;
|
|
39
39
|
onPublishComplete?: (sha: string) => void;
|
|
40
40
|
getBaseVersion: () => string | null;
|
|
41
|
+
/** The main commit this editor's content was read at. Sent as `mainVersion`
|
|
42
|
+
* with a no-draft save, so a publish landed by someone else since this tab
|
|
43
|
+
* loaded 409s into recoverConflict instead of committing a stale view. */
|
|
44
|
+
getMainVersion?: () => string | null;
|
|
41
45
|
/** Injected by EditorShell. On a 409 (concurrent edit) the save calls this to
|
|
42
46
|
* re-fetch the fresh draft, rebase local unsaved work on top, and refresh the
|
|
43
47
|
* optimistic-concurrency base (savedShaRef). Returns true when recovery
|
|
@@ -77,6 +81,10 @@ interface PublishDeps {
|
|
|
77
81
|
/** Committed document manifest, merged with the staged entries this save
|
|
78
82
|
* commits. Optional so pre-documents callers keep compiling. */
|
|
79
83
|
documentManifest?: DocumentManifest;
|
|
84
|
+
/** Optional ref mirror of `documentManifest`, read at save time — same
|
|
85
|
+
* contract as mediaManifestRef: the 409 retry sees recoverConflict's
|
|
86
|
+
* refreshed sidecar instead of this render's pre-conflict copy. */
|
|
87
|
+
documentManifestRef?: React.RefObject<DocumentManifest | null>;
|
|
80
88
|
pendingDocuments?: {
|
|
81
89
|
id: string;
|
|
82
90
|
entry: DocumentManifestEntry;
|
|
@@ -92,7 +100,7 @@ interface PublishDeps {
|
|
|
92
100
|
onDocumentsDeleted?: (ids: string[]) => void;
|
|
93
101
|
}
|
|
94
102
|
export type PublishAction = "idle" | "saving" | "publishing";
|
|
95
|
-
export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, tagsDegraded, sections, deletedSectionIds, onSuccess, mediaManifest, mediaManifestRef, manifestDirty, clearManifestDirty, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, getBaseVersion, recoverConflict, onBaselineCommitted, onSaveWarnings, saveChunkBudget, savingRef, getEditGeneration, documentManifest, pendingDocuments, pendingDocumentDeletions, onDocumentsPublished, onDocumentsDeleted, }: PublishDeps): {
|
|
103
|
+
export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, tagsDegraded, sections, deletedSectionIds, onSuccess, mediaManifest, mediaManifestRef, manifestDirty, clearManifestDirty, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, getBaseVersion, getMainVersion, recoverConflict, onBaselineCommitted, onSaveWarnings, saveChunkBudget, savingRef, getEditGeneration, documentManifest, documentManifestRef, pendingDocuments, pendingDocumentDeletions, onDocumentsPublished, onDocumentsDeleted, }: PublishDeps): {
|
|
96
104
|
publishAction: PublishAction;
|
|
97
105
|
publishFeedback: import("./useNotice").NoticeState | null;
|
|
98
106
|
saveProgress: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drawnagency/primitives",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.118",
|
|
4
4
|
"description": "Browser-safe foundation of the Brand Portal framework: Zod schemas, section components, editor shell, hooks, pure libs, collab contract, document engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -18,6 +18,25 @@ interface EditablePlainTextProps
|
|
|
18
18
|
placeholder?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function caretOffset(el: HTMLElement): number | null {
|
|
22
|
+
const sel = window.getSelection();
|
|
23
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
24
|
+
const range = sel.getRangeAt(0);
|
|
25
|
+
if (!el.contains(range.endContainer)) return null;
|
|
26
|
+
const before = document.createRange();
|
|
27
|
+
before.selectNodeContents(el);
|
|
28
|
+
before.setEnd(range.endContainer, range.endOffset);
|
|
29
|
+
return before.toString().length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function placeCaret(el: HTMLElement, offset: number) {
|
|
33
|
+
const sel = window.getSelection();
|
|
34
|
+
if (!sel) return;
|
|
35
|
+
// textContent leaves a single text node, or none for an empty string.
|
|
36
|
+
if (el.firstChild) sel.collapse(el.firstChild, offset);
|
|
37
|
+
else sel.collapse(el, 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
export function EditablePlainText({
|
|
22
41
|
tag,
|
|
23
42
|
value,
|
|
@@ -48,9 +67,14 @@ export function EditablePlainText({
|
|
|
48
67
|
|
|
49
68
|
// Set textContent via ref (safe — plain text only, no HTML injection)
|
|
50
69
|
useEffect(() => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
70
|
+
const el = ref.current;
|
|
71
|
+
if (!el || el.textContent === localValue) return;
|
|
72
|
+
// A value that differs from the DOM while the field is focused (a peer
|
|
73
|
+
// save, a rebase or a 409 recovery landing mid-typing) replaces the text
|
|
74
|
+
// node, which collapses the caret to offset 0. Put it back, clamped.
|
|
75
|
+
const caret = document.activeElement === el ? caretOffset(el) : null;
|
|
76
|
+
el.textContent = localValue;
|
|
77
|
+
if (caret !== null) placeCaret(el, Math.min(caret, localValue.length));
|
|
54
78
|
}, [localValue]);
|
|
55
79
|
|
|
56
80
|
const onInput = useCallback(() => {
|
|
@@ -27,6 +27,8 @@ interface Props {
|
|
|
27
27
|
* an audience-filtered site.
|
|
28
28
|
*/
|
|
29
29
|
canImpersonate?: boolean;
|
|
30
|
+
/** Open the view in a new tab — set by the editor, so it keeps the editor tab. */
|
|
31
|
+
viewAsInNewTab?: boolean;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
const PLACEHOLDER_COLOR = "#94a3b8";
|
|
@@ -174,7 +176,7 @@ function PageRow({ page }: { page: OverviewPage }) {
|
|
|
174
176
|
);
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
function AudienceBlock({ audience, revealSupported, canImpersonate }: { audience: AudienceDetail; revealSupported: boolean; canImpersonate: boolean }) {
|
|
179
|
+
function AudienceBlock({ audience, revealSupported, canImpersonate, viewAsInNewTab }: { audience: AudienceDetail; revealSupported: boolean; canImpersonate: boolean; viewAsInNewTab: boolean }) {
|
|
178
180
|
const [open, setOpen] = useState(false);
|
|
179
181
|
const { overview } = audience;
|
|
180
182
|
|
|
@@ -208,7 +210,7 @@ function AudienceBlock({ audience, revealSupported, canImpersonate }: { audience
|
|
|
208
210
|
</button>
|
|
209
211
|
{canImpersonate && (
|
|
210
212
|
// A plain form POST: the route sets the cookie and 303s to the viewer site.
|
|
211
|
-
<form method="post" action="/api/auth/impersonate" className="shrink-0">
|
|
213
|
+
<form method="post" action="/api/auth/impersonate" target={viewAsInNewTab ? "_blank" : undefined} className="shrink-0">
|
|
212
214
|
<input type="hidden" name="audience" value={audience.name} />
|
|
213
215
|
<Button type="submit" variant="secondary" size="sm" aria-label={`View the site as ${audience.displayName}`}>
|
|
214
216
|
<span className="inline-flex items-center gap-1.5">
|
|
@@ -260,7 +262,7 @@ function AudienceBlock({ audience, revealSupported, canImpersonate }: { audience
|
|
|
260
262
|
* Reachable by editors and the site's default audience (the client), so the
|
|
261
263
|
* client can hand a new partner access without going through us.
|
|
262
264
|
*/
|
|
263
|
-
export function AudienceDetailsPanel({ audiences, revealSupported, canImpersonate = false }: Props) {
|
|
265
|
+
export function AudienceDetailsPanel({ audiences, revealSupported, canImpersonate = false, viewAsInNewTab = false }: Props) {
|
|
264
266
|
if (audiences.length === 0) {
|
|
265
267
|
return (
|
|
266
268
|
<p className="text-sm text-base-contrast-light">
|
|
@@ -272,7 +274,7 @@ export function AudienceDetailsPanel({ audiences, revealSupported, canImpersonat
|
|
|
272
274
|
return (
|
|
273
275
|
<div className="flex flex-col gap-3">
|
|
274
276
|
{audiences.map((a) => (
|
|
275
|
-
<AudienceBlock key={a.name} audience={a} revealSupported={revealSupported} canImpersonate={canImpersonate} />
|
|
277
|
+
<AudienceBlock key={a.name} audience={a} revealSupported={revealSupported} canImpersonate={canImpersonate} viewAsInNewTab={viewAsInNewTab} />
|
|
276
278
|
))}
|
|
277
279
|
</div>
|
|
278
280
|
);
|
|
@@ -274,6 +274,11 @@ export default function EditorShell({
|
|
|
274
274
|
useEffect(() => {
|
|
275
275
|
savedShaRef.current = savedSha;
|
|
276
276
|
}, [savedSha]);
|
|
277
|
+
// Same for main: a no-draft save sends it as `mainVersion`.
|
|
278
|
+
const mainShaRef = useRef<string | null>(null);
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
mainShaRef.current = mainSha;
|
|
281
|
+
}, [mainSha]);
|
|
277
282
|
const [changedSectionIds, setChangedSectionIds] = useState<Set<string>>(new Set());
|
|
278
283
|
const [mainIndex, setMainIndex] = useState<SiteIndex | null>(null);
|
|
279
284
|
// Ref mirrors of the diff state, so save-time callbacks (which run inside a
|
|
@@ -323,6 +328,9 @@ export default function EditorShell({
|
|
|
323
328
|
// null = the auth provider has no named sign-ins (`.env` mode → route 501s),
|
|
324
329
|
// which is also what turns the reveal buttons off.
|
|
325
330
|
const [viewerCredentials, setViewerCredentials] = useState<ViewerCredential[] | null>(null);
|
|
331
|
+
// Gates "View as" in Audience Details: without password protection no visitor
|
|
332
|
+
// sees an audience-filtered site, and the impersonate route refuses.
|
|
333
|
+
const [passwordEnabled, setPasswordEnabled] = useState(false);
|
|
326
334
|
const [movingSectionId, setMovingSectionId] = useState<string | null>(null);
|
|
327
335
|
// Set by recoverConflict when a rebase surfaces overlapping edits; drives the
|
|
328
336
|
// keep-mine/take-theirs dialog. `resolve` is the promise resolver recovery
|
|
@@ -652,6 +660,13 @@ export default function EditorShell({
|
|
|
652
660
|
: null;
|
|
653
661
|
if (rebasedManifest) setMediaManifest(rebasedManifest);
|
|
654
662
|
if (remote.mediaManifest) baseMediaManifestRef.current = remote.mediaManifest;
|
|
663
|
+
// The document sidecar only ever holds committed entries (staged uploads
|
|
664
|
+
// and pending deletions live in the pipeline), so the remote one replaces
|
|
665
|
+
// it outright — no rebase. Skipping it left a peer's documents missing
|
|
666
|
+
// from this tab and, via the retry's cache write, from every reload.
|
|
667
|
+
if (remote.documentManifest) setDocumentManifest(remote.documentManifest);
|
|
668
|
+
// No draft: the fetch read main, which is now what this view is based on.
|
|
669
|
+
if (remote.savedBranchSha === null) setMainSha(remote.sha);
|
|
655
670
|
|
|
656
671
|
setSections(mergedSections);
|
|
657
672
|
setSiteIndex(mergedIndex);
|
|
@@ -663,7 +678,9 @@ export default function EditorShell({
|
|
|
663
678
|
// (and onBaselineCommitted reads the diff refs right after it), before
|
|
664
679
|
// React flushes the setState calls above into their mirror effects.
|
|
665
680
|
savedShaRef.current = remote.savedBranchSha;
|
|
681
|
+
if (remote.savedBranchSha === null) mainShaRef.current = remote.sha;
|
|
666
682
|
if (rebasedManifest) mediaManifestRef.current = rebasedManifest;
|
|
683
|
+
if (remote.documentManifest) documentManifestRef.current = remote.documentManifest;
|
|
667
684
|
sectionsRef.current = mergedSections;
|
|
668
685
|
siteIndexRef.current = mergedIndex;
|
|
669
686
|
changedSectionIdsRef.current = new Set(remote.changedSectionIds);
|
|
@@ -774,6 +791,11 @@ export default function EditorShell({
|
|
|
774
791
|
mediaManifestRef.current = rebasedManifest;
|
|
775
792
|
baseMediaManifestRef.current = remote.mediaManifest;
|
|
776
793
|
}
|
|
794
|
+
// Committed-only, so replaced outright — see recoverConflict.
|
|
795
|
+
if (remote.documentManifest) {
|
|
796
|
+
setDocumentManifest(remote.documentManifest);
|
|
797
|
+
documentManifestRef.current = remote.documentManifest;
|
|
798
|
+
}
|
|
777
799
|
setSections(result.sections);
|
|
778
800
|
setSiteIndex(result.index);
|
|
779
801
|
setTagsDegraded(remote.tagsDegraded);
|
|
@@ -999,6 +1021,7 @@ export default function EditorShell({
|
|
|
999
1021
|
mediaManifest,
|
|
1000
1022
|
mediaManifestRef,
|
|
1001
1023
|
documentManifest: documentManifest ?? {},
|
|
1024
|
+
documentManifestRef,
|
|
1002
1025
|
pendingDocuments: mediaPipeline.pendingDocuments,
|
|
1003
1026
|
onDocumentsPublished: (ids) => { void mediaPipeline.publishDocuments(ids); },
|
|
1004
1027
|
pendingDocumentDeletions: mediaPipeline.pendingDocumentDeletions,
|
|
@@ -1054,6 +1077,7 @@ export default function EditorShell({
|
|
|
1054
1077
|
},
|
|
1055
1078
|
onPublishComplete: buildStatus.startTracking,
|
|
1056
1079
|
getBaseVersion: () => savedShaRef.current,
|
|
1080
|
+
getMainVersion: () => mainShaRef.current,
|
|
1057
1081
|
recoverConflict,
|
|
1058
1082
|
// C1: re-prime the clean content-cache baseline after every successful plain
|
|
1059
1083
|
// save (discardSavedChanges just cleared it). The hook hands us the COMMITTED
|
|
@@ -1200,6 +1224,15 @@ export default function EditorShell({
|
|
|
1200
1224
|
if (!cancelled) setViewerCredentials(null);
|
|
1201
1225
|
}
|
|
1202
1226
|
})();
|
|
1227
|
+
(async () => {
|
|
1228
|
+
try {
|
|
1229
|
+
const res = await fetch("/api/auth/audiences");
|
|
1230
|
+
const enabled = res.ok && (await res.json()).passwordEnabled === true;
|
|
1231
|
+
if (!cancelled) setPasswordEnabled(enabled);
|
|
1232
|
+
} catch {
|
|
1233
|
+
if (!cancelled) setPasswordEnabled(false);
|
|
1234
|
+
}
|
|
1235
|
+
})();
|
|
1203
1236
|
return () => { cancelled = true; };
|
|
1204
1237
|
}, [showAudienceDetails]);
|
|
1205
1238
|
|
|
@@ -2287,7 +2320,7 @@ export default function EditorShell({
|
|
|
2287
2320
|
current working copy — add or remove sign-ins under Site settings → Audiences.
|
|
2288
2321
|
</p>
|
|
2289
2322
|
<div className="mt-6">
|
|
2290
|
-
<AudienceDetailsPanel audiences={audienceDetails} revealSupported={viewerCredentials !== null} />
|
|
2323
|
+
<AudienceDetailsPanel audiences={audienceDetails} revealSupported={viewerCredentials !== null} canImpersonate={passwordEnabled} viewAsInNewTab />
|
|
2291
2324
|
</div>
|
|
2292
2325
|
</div>
|
|
2293
2326
|
) : (
|
|
@@ -93,6 +93,10 @@ interface PublishDeps {
|
|
|
93
93
|
// no draft exists yet). Sent with saves so the server can 409 on a concurrent
|
|
94
94
|
// edit instead of silently overwriting it. Read at save time (latest value).
|
|
95
95
|
getBaseVersion: () => string | null;
|
|
96
|
+
/** The main commit this editor's content was read at. Sent as `mainVersion`
|
|
97
|
+
* with a no-draft save, so a publish landed by someone else since this tab
|
|
98
|
+
* loaded 409s into recoverConflict instead of committing a stale view. */
|
|
99
|
+
getMainVersion?: () => string | null;
|
|
96
100
|
/** Injected by EditorShell. On a 409 (concurrent edit) the save calls this to
|
|
97
101
|
* re-fetch the fresh draft, rebase local unsaved work on top, and refresh the
|
|
98
102
|
* optimistic-concurrency base (savedShaRef). Returns true when recovery
|
|
@@ -125,6 +129,10 @@ interface PublishDeps {
|
|
|
125
129
|
/** Committed document manifest, merged with the staged entries this save
|
|
126
130
|
* commits. Optional so pre-documents callers keep compiling. */
|
|
127
131
|
documentManifest?: DocumentManifest;
|
|
132
|
+
/** Optional ref mirror of `documentManifest`, read at save time — same
|
|
133
|
+
* contract as mediaManifestRef: the 409 retry sees recoverConflict's
|
|
134
|
+
* refreshed sidecar instead of this render's pre-conflict copy. */
|
|
135
|
+
documentManifestRef?: React.RefObject<DocumentManifest | null>;
|
|
128
136
|
pendingDocuments?: { id: string; entry: DocumentManifestEntry }[];
|
|
129
137
|
/** Ids of COMMITTED documents the editor has deleted but not yet saved. They
|
|
130
138
|
* ride the final request as `deletedDocumentIds`; a staged-but-uncommitted
|
|
@@ -166,6 +174,7 @@ export function useEditorPublish({
|
|
|
166
174
|
onShasUpdated,
|
|
167
175
|
onPublishComplete,
|
|
168
176
|
getBaseVersion,
|
|
177
|
+
getMainVersion,
|
|
169
178
|
recoverConflict,
|
|
170
179
|
onBaselineCommitted,
|
|
171
180
|
onSaveWarnings,
|
|
@@ -173,6 +182,7 @@ export function useEditorPublish({
|
|
|
173
182
|
savingRef,
|
|
174
183
|
getEditGeneration,
|
|
175
184
|
documentManifest = {},
|
|
185
|
+
documentManifestRef,
|
|
176
186
|
pendingDocuments = [],
|
|
177
187
|
pendingDocumentDeletions = [],
|
|
178
188
|
onDocumentsPublished,
|
|
@@ -358,6 +368,9 @@ export function useEditorPublish({
|
|
|
358
368
|
}
|
|
359
369
|
|
|
360
370
|
let baseVersion = getBaseVersion();
|
|
371
|
+
const mainVersion = getMainVersion?.() ?? null;
|
|
372
|
+
// Only the request that forks the draft carries it; later chunks have a base.
|
|
373
|
+
const pinMain = () => (baseVersion === null && mainVersion !== null ? { mainVersion } : {});
|
|
361
374
|
const uploadedItems: MediaItem[] = [];
|
|
362
375
|
const totalUploads = gathered.mediaUploads.length;
|
|
363
376
|
|
|
@@ -413,6 +426,7 @@ export function useEditorPublish({
|
|
|
413
426
|
}
|
|
414
427
|
const data = await postSave({
|
|
415
428
|
baseVersion,
|
|
429
|
+
...pinMain(),
|
|
416
430
|
media: {
|
|
417
431
|
uploads: chunk.map(({ id, path, base64 }) => ({
|
|
418
432
|
// content-ops ignores `item` for blob emission; the folder segment
|
|
@@ -441,6 +455,7 @@ export function useEditorPublish({
|
|
|
441
455
|
for (const { id, entry } of manifestOnly) committedDocuments[id] = entry;
|
|
442
456
|
const data = await postSave({
|
|
443
457
|
baseVersion,
|
|
458
|
+
...pinMain(),
|
|
444
459
|
documentManifest: documentManifestToSend(),
|
|
445
460
|
});
|
|
446
461
|
baseVersion = data.sha;
|
|
@@ -461,6 +476,7 @@ export function useEditorPublish({
|
|
|
461
476
|
};
|
|
462
477
|
const data = await postSave({
|
|
463
478
|
baseVersion,
|
|
479
|
+
...pinMain(),
|
|
464
480
|
media: { uploads: chunk, deletions: [], manifest },
|
|
465
481
|
});
|
|
466
482
|
baseVersion = data.sha;
|
|
@@ -486,6 +502,7 @@ export function useEditorPublish({
|
|
|
486
502
|
sections: dirty.map(({ sectionId, content }) => ({ id: sectionId, content })),
|
|
487
503
|
siteIndex,
|
|
488
504
|
baseVersion,
|
|
505
|
+
...pinMain(),
|
|
489
506
|
...(args.deletedSectionIds?.length ? { deletedSectionIds: args.deletedSectionIds } : {}),
|
|
490
507
|
...(args.isConfigDirty() ? { siteConfig: args.siteConfig } : {}),
|
|
491
508
|
...(gathered.hasMediaChanges ? {
|
|
@@ -607,7 +624,7 @@ export function useEditorPublish({
|
|
|
607
624
|
// Read through the ref (when the shell supplies one) so the 409 retry
|
|
608
625
|
// below sees recoverConflict's rebased manifest, not this render's copy.
|
|
609
626
|
mediaManifest: mediaManifestRef?.current ?? mediaManifest,
|
|
610
|
-
documentManifest,
|
|
627
|
+
documentManifest: documentManifestRef?.current ?? documentManifest,
|
|
611
628
|
pendingDocuments,
|
|
612
629
|
pendingDocumentDeletions,
|
|
613
630
|
saveChunkBudget,
|