@portabletext/plugin-sdk-value 8.0.5 → 8.0.6

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { DocumentHandle, usePresenceForDocument } from "@sanity/sdk-react";
1
+ import { DocumentHandle, DocumentResource, usePresenceForDocument } from "@sanity/sdk-react";
2
2
  import { EditorSelection, Patch, PortableTextBlock, PortableTextEditableProps, RangeDecoration } from "@portabletext/editor";
3
3
  import { PropsWithChildren, ReactElement } from "react";
4
4
  import "@portabletext/patches";
@@ -41,6 +41,10 @@ interface SDKRemoteCursor {
41
41
  * @public
42
42
  */
43
43
  interface SDKPresencePluginProps extends DocumentHandle {
44
+ /**
45
+ * @deprecated Use `resource` instead.
46
+ */
47
+ source?: DocumentResource;
44
48
  /**
45
49
  * The document path of the Portable Text field, for example `content`. The
46
50
  * same form `SDKValuePlugin` takes.
@@ -53,6 +57,10 @@ interface SDKPresencePluginProps extends DocumentHandle {
53
57
  * @public
54
58
  */
55
59
  interface UseSDKPresenceCursorsOptions extends DocumentHandle {
60
+ /**
61
+ * @deprecated Use `resource` instead.
62
+ */
63
+ source?: DocumentResource;
56
64
  path: string;
57
65
  renderCursor: RenderCursorFunction;
58
66
  }
@@ -87,6 +95,7 @@ declare function SDKPresencePlugin(props: SDKPresencePluginProps): null;
87
95
  */
88
96
  declare function useSDKPresenceCursors(options: UseSDKPresenceCursorsOptions): RangeDecoration[];
89
97
  interface SDKValuePluginProps extends DocumentHandle {
98
+ source?: DocumentResource;
90
99
  path: string;
91
100
  }
92
101
  /**
@@ -132,6 +141,10 @@ declare function ValueSyncPlugin(props: ValueSyncConfig): null;
132
141
  * @public
133
142
  */
134
143
  interface SDKPortableTextEditableProps extends DocumentHandle, Omit<PortableTextEditableProps, keyof DocumentHandle> {
144
+ /**
145
+ * @deprecated Use `resource` instead.
146
+ */
147
+ source?: DocumentResource;
135
148
  /**
136
149
  * The document path of the Portable Text field, for example `content`.
137
150
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/plugin.sdk-presence.tsx","../src/plugin.sdk-value.tsx","../src/sdk-editable.tsx"],"mappings":";;;;;;;;;KAeK,mBAAmB,kBACf;;;;;;;KASG,wBACV,QAAQ,qBACJ,OAAO,sBAAsB;;;;;;;;;UAUlB;;;;;EAKf;;;;EAIA,WAAW;EACX,MAAM;;;;;;;UAQS,+BAA+B;;;;;EAK9C;;;;;;;UAQe,qCAAqC;EACpD;EACA,cAAc;;;;;;;;;;;;;;;;iBAiBA,kBAAkB,OAAO;;;;;;;;;;;;;;;iBAwBzB,sBACd,SAAS,+BACR;UC48BO,4BAA4B;EACpC;;;;;iBAqCc,eAAe,OAAO,sCAAmB,IAAA;;;;KAiFpD;EACH,sBAAsB;EACtB,YAAY,OAAO;EACnB,sBAAsB;;;;;;;;EAQtB,mBACE,WAAW,SAAS;;;;;;EAOtB,eAAe,SAAS;;;;;;;;;;;;iBAaV,gBAAgB,OAAO;;;;;;UChsCtB,qCAEb,gBAGA,KAAK,iCAAiC;;;;EAIxC;;;;;;EAMA,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CD,wBAAwB,OAAO,+CAA4B,IAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/plugin.sdk-presence.tsx","../src/plugin.sdk-value.tsx","../src/sdk-editable.tsx"],"mappings":";;;;;;;;;KAiBK,mBAAmB,kBACf;;;;;;;KASG,wBACV,QAAQ,qBACJ,OAAO,sBAAsB;;;;;;;;;UAUlB;;;;;EAKf;;;;EAIA,WAAW;EACX,MAAM;;;;;;;UAQS,+BAA+B;;;;EAI9C,SAAS;;;;;EAKT;;;;;;;UAQe,qCAAqC;;;;EAIpD,SAAS;EACT;EACA,cAAc;;;;;;;;;;;;;;;;iBAiBA,kBAAkB,OAAO;;;;;;;;;;;;;;;iBAwBzB,sBACd,SAAS,+BACR;UCo8BO,4BAA4B;EACpC,SAAS;EACT;;;;;iBAqCc,eAAe,OAAO,sCAAmB,IAAA;;;;KAkFpD;EACH,sBAAsB;EACtB,YAAY,OAAO;EACnB,sBAAsB;;;;;;;;EAQtB,mBACE,WAAW,SAAS;;;;;;EAOtB,eAAe,SAAS;;;;;;;;;;;;iBAaV,gBAAgB,OAAO;;;;;;UCnsCtB,qCAEb,gBAGA,KAAK,iCAAiC;;;;EAIxC,SAAS;;;;EAIT;;;;;;EAMA,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CD,wBAAwB,OAAO,+CAA4B,IAAA"}
package/dist/index.js CHANGED
@@ -125,7 +125,15 @@ const debug = {
125
125
  push: createDebugger("push"),
126
126
  remote: createDebugger("remote"),
127
127
  repair: createDebugger("repair")
128
- }, ARRAYIFY_ERROR_MESSAGE = "Unexpected path format from diffValue output. Please report this issue.";
128
+ };
129
+ function normalizeDocumentHandle(handle) {
130
+ let { source, ...normalizedHandle } = handle;
131
+ return normalizedHandle.resource !== void 0 || source === void 0 ? normalizedHandle : {
132
+ ...normalizedHandle,
133
+ resource: source
134
+ };
135
+ }
136
+ const ARRAYIFY_ERROR_MESSAGE = "Unexpected path format from diffValue output. Please report this issue.";
129
137
  function* getSegments(node) {
130
138
  node.base && (yield* getSegments(node.base)), node.segment.type !== "This" && (yield node.segment);
131
139
  }
@@ -691,14 +699,12 @@ function getPublishedDocumentId(id) {
691
699
  * @public
692
700
  */
693
701
  function SDKValuePlugin(props) {
694
- let $ = c(20), { documentId, documentType, path } = props, setSdkValue = useEditDocument(props), instance = useSanityInstance(props), applyActions = useApplyDocumentActions(), t0;
695
- $[0] !== documentId || $[1] !== documentType || $[2] !== instance || $[3] !== path ? (t0 = getDocumentState(instance, {
702
+ let $ = c(15), normalizedProps = normalizeDocumentHandle(props), { documentId, documentType, path } = normalizedProps, setSdkValue = useEditDocument(normalizedProps), instance = useSanityInstance(), applyActions = useApplyDocumentActions(), { getCurrent, subscribe } = getDocumentState(instance, {
696
703
  documentId,
697
704
  documentType,
698
705
  path
699
- }), $[0] = documentId, $[1] = documentType, $[2] = instance, $[3] = path, $[4] = t0) : t0 = $[4];
700
- let { getCurrent, subscribe } = t0, t1;
701
- $[5] !== documentId || $[6] !== instance || $[7] !== path ? (t1 = (callback) => subscribeDocumentEvents(instance, { eventHandler: (event) => {
706
+ }), t0;
707
+ $[0] !== documentId || $[1] !== instance || $[2] !== path ? (t0 = (callback) => subscribeDocumentEvents(instance, { eventHandler: (event) => {
702
708
  let candidate = event;
703
709
  if (!isRemotePatchesEvent(candidate) || candidate.origin !== "remote" || getPublishedDocumentId(candidate.documentId) !== getPublishedDocumentId(documentId)) return;
704
710
  let patches;
@@ -708,9 +714,9 @@ function SDKValuePlugin(props) {
708
714
  return;
709
715
  }
710
716
  patches && patches.length > 0 && callback(patches);
711
- } }), $[5] = documentId, $[6] = instance, $[7] = path, $[8] = t1) : t1 = $[8];
712
- let onRemotePatches = t1, t2;
713
- $[9] !== applyActions || $[10] !== documentId || $[11] !== documentType || $[12] !== path ? (t2 = (patches_0) => {
717
+ } }), $[0] = documentId, $[1] = instance, $[2] = path, $[3] = t0) : t0 = $[3];
718
+ let onRemotePatches = t0, t1;
719
+ $[4] !== applyActions || $[5] !== documentId || $[6] !== documentType || $[7] !== path ? (t1 = (patches_0) => {
714
720
  let sanityPatches = convertPatchesToSanity(patches_0, { prefix: path }), action = {
715
721
  ...editDocument({
716
722
  documentId,
@@ -719,15 +725,15 @@ function SDKValuePlugin(props) {
719
725
  preserveOperations: !0
720
726
  };
721
727
  applyActions(action);
722
- }, $[9] = applyActions, $[10] = documentId, $[11] = documentType, $[12] = path, $[13] = t2) : t2 = $[13];
723
- let pushPatches = t2, t3;
724
- return $[14] !== getCurrent || $[15] !== onRemotePatches || $[16] !== pushPatches || $[17] !== setSdkValue || $[18] !== subscribe ? (t3 = /* @__PURE__ */ jsx(ValueSyncPlugin, {
728
+ }, $[4] = applyActions, $[5] = documentId, $[6] = documentType, $[7] = path, $[8] = t1) : t1 = $[8];
729
+ let pushPatches = t1, t2;
730
+ return $[9] !== getCurrent || $[10] !== onRemotePatches || $[11] !== pushPatches || $[12] !== setSdkValue || $[13] !== subscribe ? (t2 = /* @__PURE__ */ jsx(ValueSyncPlugin, {
725
731
  getRemoteValue: getCurrent,
726
732
  pushValue: setSdkValue,
727
733
  onRemoteValueChange: subscribe,
728
734
  onRemotePatches,
729
735
  pushPatches
730
- }), $[14] = getCurrent, $[15] = onRemotePatches, $[16] = pushPatches, $[17] = setSdkValue, $[18] = subscribe, $[19] = t3) : t3 = $[19], t3;
736
+ }), $[9] = getCurrent, $[10] = onRemotePatches, $[11] = pushPatches, $[12] = setSdkValue, $[13] = subscribe, $[14] = t2) : t2 = $[14], t2;
731
737
  }
732
738
  /**
733
739
  * NOTE: You are probably looking for SDKValuePlugin instead of this.
@@ -775,14 +781,16 @@ function ValueSyncPlugin(props) {
775
781
  * @public
776
782
  */
777
783
  function SDKPresencePlugin(props) {
778
- let $ = c(7), handle, path;
779
- $[0] === props ? (handle = $[1], path = $[2]) : ({path, ...handle} = props, $[0] = props, $[1] = handle, $[2] = path);
780
- let selection = useLocalSelection(), fieldPath = useFieldPath(path), t0;
781
- return $[3] !== fieldPath || $[4] !== handle || $[5] !== selection ? (t0 = {
784
+ let $ = c(9), t0;
785
+ $[0] === props ? t0 = $[1] : (t0 = normalizeDocumentHandle(props), $[0] = props, $[1] = t0);
786
+ let handle, path;
787
+ $[2] === t0 ? (handle = $[3], path = $[4]) : ({path, ...handle} = t0, $[2] = t0, $[3] = handle, $[4] = path);
788
+ let selection = useLocalSelection(), fieldPath = useFieldPath(path), t1;
789
+ return $[5] !== fieldPath || $[6] !== handle || $[7] !== selection ? (t1 = {
782
790
  ...handle,
783
791
  path: fieldPath,
784
792
  selection
785
- }, $[3] = fieldPath, $[4] = handle, $[5] = selection, $[6] = t0) : t0 = $[6], useReportPresence(t0), null;
793
+ }, $[5] = fieldPath, $[6] = handle, $[7] = selection, $[8] = t1) : t1 = $[8], useReportPresence(t1), null;
786
794
  }
787
795
  /**
788
796
  * Other people's carets in a Portable Text field, as range decorations.
@@ -799,21 +807,23 @@ function SDKPresencePlugin(props) {
799
807
  * @public
800
808
  */
801
809
  function useSDKPresenceCursors(options) {
802
- let $ = c(12), handle, path, renderCursor;
803
- $[0] === options ? (handle = $[1], path = $[2], renderCursor = $[3]) : ({path, renderCursor, ...handle} = options, $[0] = options, $[1] = handle, $[2] = path, $[3] = renderCursor);
804
- let fieldPath = useFieldPath(path), t0;
805
- $[4] !== fieldPath || $[5] !== handle ? (t0 = {
810
+ let $ = c(14), t0;
811
+ $[0] === options ? t0 = $[1] : (t0 = normalizeDocumentHandle(options), $[0] = options, $[1] = t0);
812
+ let handle, path, renderCursor;
813
+ $[2] === t0 ? (handle = $[3], path = $[4], renderCursor = $[5]) : ({path, renderCursor, ...handle} = t0, $[2] = t0, $[3] = handle, $[4] = path, $[5] = renderCursor);
814
+ let fieldPath = useFieldPath(path), t1;
815
+ $[6] !== fieldPath || $[7] !== handle ? (t1 = {
806
816
  ...handle,
807
817
  path: fieldPath,
808
818
  excludeVersions: !0
809
- }, $[4] = fieldPath, $[5] = handle, $[6] = t0) : t0 = $[6];
810
- let { presence } = usePresenceForDocument(t0), t1;
811
- $[7] === presence ? t1 = $[8] : (t1 = presence.flatMap(_temp), $[7] = presence, $[8] = t1);
812
- let cursors = t1, t2;
813
- return $[9] !== cursors || $[10] !== renderCursor ? (t2 = {
819
+ }, $[6] = fieldPath, $[7] = handle, $[8] = t1) : t1 = $[8];
820
+ let { presence } = usePresenceForDocument(t1), t2;
821
+ $[9] === presence ? t2 = $[10] : (t2 = presence.flatMap(_temp), $[9] = presence, $[10] = t2);
822
+ let cursors = t2, t3;
823
+ return $[11] !== cursors || $[12] !== renderCursor ? (t3 = {
814
824
  cursors,
815
825
  renderCursor
816
- }, $[9] = cursors, $[10] = renderCursor, $[11] = t2) : t2 = $[11], useRemoteCursors(t2);
826
+ }, $[11] = cursors, $[12] = renderCursor, $[13] = t3) : t3 = $[13], useRemoteCursors(t3);
817
827
  }
818
828
  function _temp(participant) {
819
829
  return participant.selection ? [{
@@ -977,16 +987,15 @@ function SDKPortableTextEditable(props) {
977
987
  * @internal
978
988
  */
979
989
  function splitEditableProps(props) {
980
- let { documentId, documentType, projectId, dataset, resource, resourceName, source, liveEdit, perspective, path, renderCursor, rangeDecorations, ...editableProps } = props;
990
+ let normalizedProps = normalizeDocumentHandle(props), { documentId, documentType, projectId, dataset, resource, resourceName, liveEdit, perspective, path, renderCursor, rangeDecorations, ...editableProps } = normalizedProps;
981
991
  return {
982
992
  handle: {
983
993
  documentId,
984
994
  documentType,
985
995
  ..."projectId" in props && { projectId },
986
996
  ..."dataset" in props && { dataset },
987
- ..."resource" in props && { resource },
997
+ ..."resource" in normalizedProps && { resource },
988
998
  ..."resourceName" in props && { resourceName },
989
- ..."source" in props && { source },
990
999
  ..."liveEdit" in props && { liveEdit },
991
1000
  ..."perspective" in props && { perspective }
992
1001
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["(cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n }","useCallback(\n (cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n },\n [cursors],\n )","toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved})","useMemo(\n () => toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved}),\n [cursors, overrides, renderCursor, onCursorMoved],\n )","getDocumentState<PortableTextBlock[]>(\n instance,\n handle,\n )","(callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n }","useCallback(\n (callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n },\n [instance, documentId, path],\n )","(patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n }","patches","useCallback(\n (patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n },\n [applyActions, documentId, documentType, path],\n )","<ValueSyncPlugin\n getRemoteValue={getCurrent}\n pushValue={setSdkValue}\n onRemoteValueChange={subscribe}\n onRemotePatches={onRemotePatches}\n pushPatches={pushPatches}\n />","valueSyncMachine.provide({\n actions: {\n 'push to remote': ({context, event}) => {\n if (event.type !== 'mutation flushed') {\n return\n }\n\n if (pushPatches && event.patches.length > 0) {\n try {\n const mergeable = toMergeableMarkDefsPatches(\n event.patches,\n context.getRemoteValue,\n )\n debug.push('pushing patches %o', mergeable)\n pushPatches(mergeable)\n return\n } catch {\n // fall back to pushing the whole value below\n }\n }\n\n if (debug.push.enabled) {\n debug.push(\n 'pushing whole value %s',\n debugTextOf(\n event.value ?? context.editor.getSnapshot().context.value,\n ),\n )\n }\n pushValue(event.value ?? context.editor.getSnapshot().context.value)\n },\n },\n })","{context, event}","{\n input: {\n editor,\n getRemoteValue,\n onRemoteValueChange,\n onRemotePatches,\n },\n }","{...handle, path: fieldPath, selection}","{\n ...handle,\n path: fieldPath,\n excludeVersions: true,\n }","presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n )","(participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : []","useMemo(\n () =>\n presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n ),\n [presence],\n )","{cursors, renderCursor}","arrayifyPath(path)","useMemo(() => arrayifyPath(path), [path])","getCaretColor(cursor.user.sanityUserId)","sanityUserId","{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }","`2px solid ${color}`","{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }","<span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />","`presence-caret-dot-${cursor.sessionId}`","<span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>","`presence-caret-${cursor.sessionId}`","<>\n <span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>\n {children}\n </>","resolveCursorRenderer(renderCursor)","{\n ...handle,\n path,\n renderCursor: renderer.renderCursor,\n }","renderCursor","mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors)","drawCursors","useMemo(\n () =>\n mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors),\n [cursors, rangeDecorations, renderer.drawCursors],\n )","<PortableTextEditable {...editableProps} rangeDecorations={decorations} />","<SDKValuePlugin {...handle} path={path} />","<SDKPresencePlugin {...handle} path={path} />","<>\n <PortableTextEditable {...editableProps} rangeDecorations={decorations} />\n <SDKValuePlugin {...handle} path={path} />\n <SDKPresencePlugin {...handle} path={path} />\n </>"],"sources":["../src/presence-cursors.ts","../src/plugin.presence-sync.tsx","../src/debug.ts","../src/plugin.sdk-value.tsx","../src/plugin.sdk-presence.tsx","../src/presence-caret.tsx","../src/sdk-editable.tsx"],"sourcesContent":["import type {EditorSelection, RangeDecoration} from '@portabletext/editor'\nimport {isEqualSelections} from '@portabletext/editor/utils'\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * A remote participant's position in the editor.\n *\n * @public\n */\nexport interface RemoteCursor {\n /**\n * Identifies one editing session rather than one person. The same user in two\n * tabs reports two sessions and therefore draws two carets.\n */\n sessionId: string\n /**\n * The selection the participant last reported, or `null` when they have none.\n */\n selection: EditorSelection\n}\n\n/**\n * Where the local editor has pushed a remote caret, and the reported selection\n * it was pushed from.\n *\n * Typing above or inside a remote caret moves it, and the editor reports the\n * new position through `onMoved`. Keeping the selection it moved from is what\n * lets a later report from that participant take over again.\n *\n * @public\n */\nexport interface CursorOverride {\n reported: EditorSelection\n current: EditorSelection\n}\n\n/**\n * Options for {@link toRangeDecorations}.\n *\n * @public\n */\nexport interface RangeDecorationOptions<TCursor extends RemoteCursor> {\n cursors: readonly TCursor[]\n overrides: ReadonlyMap<string, CursorOverride>\n /**\n * Builds the component that draws one caret. The plugin has no opinion about\n * how a caret looks, so this is the caller's to provide.\n */\n renderCursor: (cursor: TCursor) => (props: PropsWithChildren) => ReactElement\n onCursorMoved?: (cursor: TCursor, selection: EditorSelection) => void\n}\n\n/**\n * Reduces a selection to a caret at its focus point.\n *\n * Presence answers where someone is, so decorating their whole selection would\n * highlight text the local user never selected. The Studio collapses the same\n * way, which keeps the two consistent when both are open on a document.\n *\n * @public\n */\nexport function collapseToCaret(selection: EditorSelection): EditorSelection {\n if (selection === null) {\n return null\n }\n return {anchor: selection.focus, focus: selection.focus}\n}\n\n/**\n * Where one remote caret should be drawn.\n *\n * A caret the local editor has moved keeps that moved position for as long as\n * the participant keeps reporting the same selection. A changed report wins,\n * because the participant has actually moved.\n *\n * @public\n */\nexport function resolveCursorSelection(\n cursor: RemoteCursor,\n override: CursorOverride | undefined,\n): EditorSelection {\n if (override && isEqualSelections(override.reported, cursor.selection)) {\n return override.current\n }\n return collapseToCaret(cursor.selection)\n}\n\n/**\n * Records where the editor moved a caret to.\n *\n * Overrides for sessions that are no longer present are dropped, so a long\n * editing session does not accumulate one entry per participant who ever\n * visited. Returns the map untouched when nothing changed.\n *\n * @public\n */\nexport function recordCursorMove(\n overrides: ReadonlyMap<string, CursorOverride>,\n cursors: readonly RemoteCursor[],\n cursor: RemoteCursor,\n selection: EditorSelection,\n): ReadonlyMap<string, CursorOverride> {\n const moved = collapseToCaret(selection)\n const existing = overrides.get(cursor.sessionId)\n const alreadyRecorded =\n existing !== undefined &&\n isEqualSelections(existing.reported, cursor.selection) &&\n isEqualSelections(existing.current, moved)\n\n const live = new Set(cursors.map((candidate) => candidate.sessionId))\n const staleKeys = [...overrides.keys()].filter((key) => !live.has(key))\n\n if (alreadyRecorded && staleKeys.length === 0) {\n return overrides\n }\n\n const next = new Map(overrides)\n for (const key of staleKeys) {\n next.delete(key)\n }\n next.set(cursor.sessionId, {reported: cursor.selection, current: moved})\n return next\n}\n\n/**\n * Maps remote cursors to range decorations for\n * `<PortableTextEditable rangeDecorations={...} />`.\n *\n * Participants with no caret to draw are skipped, so someone who clears their\n * selection stops drawing without being treated as having left.\n *\n * @public\n */\nexport function toRangeDecorations<TCursor extends RemoteCursor>(\n options: RangeDecorationOptions<TCursor>,\n): RangeDecoration[] {\n const {cursors, overrides, renderCursor, onCursorMoved} = options\n const decorations: RangeDecoration[] = []\n\n for (const cursor of cursors) {\n const selection = resolveCursorSelection(\n cursor,\n overrides.get(cursor.sessionId),\n )\n if (selection === null) {\n continue\n }\n decorations.push({\n component: renderCursor(cursor),\n selection,\n payload: {sessionId: cursor.sessionId},\n onMoved: onCursorMoved\n ? (details) => onCursorMoved(cursor, details.newSelection)\n : undefined,\n })\n }\n\n return decorations\n}\n","import {\n useEditor,\n useEditorSelector,\n type EditorSelection,\n type RangeDecoration,\n} from '@portabletext/editor'\nimport {getSelection} from '@portabletext/editor/selectors'\nimport {isEqualSelections} from '@portabletext/editor/utils'\nimport {\n useCallback,\n useMemo,\n useState,\n type PropsWithChildren,\n type ReactElement,\n} from 'react'\nimport {\n recordCursorMove,\n toRangeDecorations,\n type CursorOverride,\n type RemoteCursor,\n} from './presence-cursors'\n\n/**\n * Options for {@link useRemoteCursors}.\n *\n * @public\n */\nexport interface UseRemoteCursorsOptions<TCursor extends RemoteCursor> {\n /**\n * The remote participants to draw. Keep this array referentially stable, for\n * example with `useMemo`, so that decorations are not rebuilt on every\n * render.\n */\n cursors: readonly TCursor[]\n renderCursor: (cursor: TCursor) => (props: PropsWithChildren) => ReactElement\n}\n\n/**\n * The local user's selection, deduped by value.\n *\n * The editor produces a fresh selection object on every snapshot change, so\n * subscribing to it directly would report presence far more often than the\n * caret actually moves.\n *\n * @public\n */\nexport function useLocalSelection(): EditorSelection {\n const editor = useEditor()\n // Direction is ignored, which `isEqualSelections` already does: a caret is\n // drawn at the focus point, so reversing a selection moves nothing.\n return useEditorSelector(editor, getSelection, isEqualSelections)\n}\n\n/**\n * Turns remote cursors into range decorations, keeping each caret anchored as\n * the local user edits.\n *\n * Knows nothing about Sanity or the SDK, so it can also drive carets from\n * another source or from a test fixture. `useSDKPresenceCursors` is the version\n * wired to SDK presence.\n *\n * @public\n */\nexport function useRemoteCursors<TCursor extends RemoteCursor>(\n options: UseRemoteCursorsOptions<TCursor>,\n): RangeDecoration[] {\n const {cursors, renderCursor} = options\n // Only the moved carets need to survive a render. Where every caret is drawn\n // is derived from them plus the latest report, so there is no state to keep\n // in step with the incoming cursors.\n const [overrides, setOverrides] =\n useState<ReadonlyMap<string, CursorOverride>>(EMPTY_OVERRIDES)\n\n const onCursorMoved = useCallback(\n (cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n },\n [cursors],\n )\n\n return useMemo(\n () => toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved}),\n [cursors, overrides, renderCursor, onCursorMoved],\n )\n}\n\nconst EMPTY_OVERRIDES: ReadonlyMap<string, CursorOverride> = new Map()\n","import rawDebug from 'debug'\n\n// Keep in sync with `packages/editor/src/internal-utils/debug.ts`: sharing\n// the `pte:` root lets `localStorage.debug = 'pte:*'` interleave this\n// plugin's sync traces with the editor's own output on one timeline.\nconst rootName = 'pte:plugin-sdk-value:'\n\nfunction createDebugger(name: string): rawDebug.Debugger {\n const namespace = `${rootName}${name}`\n if (rawDebug && rawDebug.enabled(namespace)) {\n return rawDebug(namespace)\n }\n return rawDebug(rootName)\n}\n\nexport const debug = {\n mutation: createDebugger('mutation'),\n push: createDebugger('push'),\n remote: createDebugger('remote'),\n repair: createDebugger('repair'),\n}\n","import {\n useEditor,\n type Editor,\n type PortableTextBlock,\n type Patch as PtePatch,\n} from '@portabletext/editor'\nimport {\n applyAll,\n type JSONValue,\n type Path,\n type PathSegment,\n type InsertPatch as PteInsertPatch,\n} from '@portabletext/patches'\nimport {diffValue, type SanityPatchOperations} from '@sanity/diff-patch'\nimport {\n parsePath,\n type ExprNode,\n type PathNode,\n type SegmentNode,\n type ThisNode,\n} from '@sanity/json-match'\nimport {\n editDocument,\n getDocumentState,\n subscribeDocumentEvents,\n useApplyDocumentActions,\n useEditDocument,\n useSanityInstance,\n type DocumentHandle,\n type EditDocumentAction,\n} from '@sanity/sdk-react'\nimport {useActorRef} from '@xstate/react'\nimport {useCallback} from 'react'\nimport {fromCallback, setup, type AnyEventObject} from 'xstate'\nimport {debug} from './debug'\n\ntype InsertPatch = Required<Pick<SanityPatchOperations, 'insert'>>\n\nconst ARRAYIFY_ERROR_MESSAGE =\n 'Unexpected path format from diffValue output. Please report this issue.'\n\nfunction* getSegments(\n node: PathNode,\n): Generator<Exclude<SegmentNode, ThisNode>> {\n if (node.base) {\n yield* getSegments(node.base)\n }\n if (node.segment.type !== 'This') {\n yield node.segment\n }\n}\n\nfunction isKeyPath(node: ExprNode): node is PathNode {\n if (node.type !== 'Path') {\n return false\n }\n if (node.base) {\n return false\n }\n if (node.recursive) {\n return false\n }\n if (node.segment.type !== 'Identifier') {\n return false\n }\n return node.segment.name === '_key'\n}\n\nexport function arrayifyPath(pathExpr: string): Path {\n const node = parsePath(pathExpr)\n if (!node) {\n return []\n }\n if (node.type !== 'Path') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n\n return Array.from(getSegments(node)).map((segment): PathSegment => {\n if (segment.type === 'Identifier') {\n return segment.name\n }\n if (segment.type !== 'Subscript') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n if (segment.elements.length !== 1) {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n\n const [element] = segment.elements\n if (element.type === 'Number') {\n return element.value\n }\n\n if (element.type !== 'Comparison') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n if (element.operator !== '==') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n const keyPathNode = [element.left, element.right].find(isKeyPath)\n if (!keyPathNode) {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n const other = element.left === keyPathNode ? element.right : element.left\n if (other.type !== 'String') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n return {_key: other.value}\n })\n}\n\nexport function convertPatches(patches: SanityPatchOperations[]): PtePatch[] {\n return patches.flatMap((p) => {\n return Object.entries(p).flatMap(([type, values]): PtePatch[] => {\n const origin = 'remote'\n\n switch (type) {\n case 'set':\n case 'setIfMissing':\n case 'diffMatchPatch':\n case 'inc':\n case 'dec': {\n return Object.entries(values).map(\n ([pathExpr, value]) =>\n ({type, value, origin, path: arrayifyPath(pathExpr)}) as PtePatch,\n )\n }\n case 'unset': {\n if (!Array.isArray(values)) {\n return []\n }\n return values.map(arrayifyPath).map((path) => ({type, origin, path}))\n }\n case 'insert': {\n const {items, ...rest} = values as InsertPatch['insert']\n type InsertPosition = PteInsertPatch['position']\n const position = Object.keys(rest).at(0) as InsertPosition | undefined\n\n if (!position) {\n return []\n }\n const pathExpr = (rest as {[K in InsertPosition]: string})[position]\n const insertPatch: PteInsertPatch = {\n type,\n origin,\n position,\n path: arrayifyPath(pathExpr),\n items: items as JSONValue[],\n }\n\n return [insertPatch]\n }\n\n default: {\n return []\n }\n }\n })\n })\n}\n\nconst STRINGIFY_ERROR_MESSAGE =\n 'Unable to convert an editor patch path to a Sanity path expression.'\n\n/**\n * Converts a Portable Text Editor patch path (an array of segments) into a\n * Sanity json-match path expression. The inverse of `arrayifyPath`.\n *\n * @internal\n */\nexport function stringifyPatchPath(path: Path): string {\n let result = ''\n for (const segment of path) {\n if (typeof segment === 'string') {\n result = result === '' ? segment : `${result}.${segment}`\n } else if (typeof segment === 'number') {\n result = `${result}[${segment}]`\n } else if (\n typeof segment === 'object' &&\n segment !== null &&\n '_key' in segment\n ) {\n result = `${result}[_key==\"${segment._key}\"]`\n } else {\n throw new Error(STRINGIFY_ERROR_MESSAGE)\n }\n }\n return result\n}\n\nfunction prefixPathExpression(prefix: string, expression: string): string {\n if (expression === '') {\n return prefix\n }\n return expression.startsWith('[')\n ? `${prefix}${expression}`\n : `${prefix}.${expression}`\n}\n\n/**\n * `SanityPatchOperations` from `@sanity/diff-patch` only covers the\n * operations `diffValue` emits; the editor can additionally produce these.\n */\ntype SanityPatchOperationsWithExtras = SanityPatchOperations & {\n setIfMissing?: {[path: string]: unknown}\n inc?: {[path: string]: number}\n dec?: {[path: string]: number}\n}\n\n/**\n * Converts Portable Text Editor patches into Sanity patch operations rooted\n * at the given document field path. Throws if a patch cannot be converted;\n * callers should fall back to pushing the whole value.\n *\n * @internal\n */\nexport function convertPatchesToSanity(\n patches: PtePatch[],\n options: {prefix: string},\n): SanityPatchOperationsWithExtras[] {\n return patches.map((patch): SanityPatchOperationsWithExtras => {\n const pathExpression = prefixPathExpression(\n options.prefix,\n stringifyPatchPath(patch.path),\n )\n\n switch (patch.type) {\n case 'set':\n return {set: {[pathExpression]: patch.value}}\n case 'setIfMissing':\n return {setIfMissing: {[pathExpression]: patch.value}}\n case 'unset':\n // The editor unsets the whole field when it becomes empty. Write an\n // empty array instead: unsetting the field would leave other clients\n // unable to reconcile against it (their remote value disappears).\n if (patch.path.length === 0) {\n return {set: {[pathExpression]: []}}\n }\n return {unset: [pathExpression]}\n case 'diffMatchPatch':\n return {diffMatchPatch: {[pathExpression]: patch.value}}\n case 'inc':\n return {inc: {[pathExpression]: patch.value as number}}\n case 'dec':\n return {dec: {[pathExpression]: patch.value as number}}\n case 'insert':\n return {\n insert: {\n [patch.position]: pathExpression,\n items: patch.items,\n } as SanityPatchOperations['insert'],\n }\n default:\n throw new Error(STRINGIFY_ERROR_MESSAGE)\n }\n })\n}\n\nfunction segmentsEqual(a: PathSegment, b: PathSegment): boolean {\n if (typeof a === 'string' || typeof a === 'number') {\n return a === b\n }\n if (typeof b === 'string' || typeof b === 'number') {\n return false\n }\n if (Array.isArray(a) || Array.isArray(b)) {\n return false\n }\n return a._key === b._key\n}\n\nfunction pathsEqual(a: Path, b: Path): boolean {\n return (\n a.length === b.length &&\n a.every((segment, index) => segmentsEqual(segment, b[index]))\n )\n}\n\n/**\n * The editor engine can only resolve keyed/indexed segments against the\n * root block array and (possibly nested) `children` arrays. Operations\n * that address items of any other array, e.g. a block's `markDefs` or a\n * span's `marks`, misapply. When a patch path enters such a sidecar\n * array, this returns the path of the array itself so callers can fall\n * back to replacing the whole property; returns `null` for paths the\n * engine can apply.\n *\n * @internal\n */\nexport function findSidecarArrayPath(path: Path): Path | null {\n // a keyed/numeric segment is only resolvable first (root block array)\n // or directly after a `children` property\n let expectNode = true\n for (let index = 0; index < path.length; index++) {\n const segment = path[index]\n if (typeof segment === 'string') {\n expectNode = segment === 'children'\n } else {\n if (!expectNode) {\n return path.slice(0, index)\n }\n expectNode = false\n }\n }\n return null\n}\n\nfunction getValueAtPath(value: JSONValue, path: Path): JSONValue | undefined {\n let current: JSONValue | undefined = value\n for (const segment of path) {\n if (current === null || current === undefined) {\n return undefined\n }\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) {\n return undefined\n }\n current = current[segment < 0 ? current.length + segment : segment]\n } else if (typeof segment === 'string') {\n if (typeof current !== 'object' || Array.isArray(current)) {\n return undefined\n }\n current = (current as {[key: string]: JSONValue})[segment]\n } else if (Array.isArray(segment)) {\n // index tuples address ranges, not single values\n return undefined\n } else {\n if (!Array.isArray(current)) {\n return undefined\n }\n current = current.find(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n !Array.isArray(item) &&\n (item as {_key?: unknown})._key === segment._key,\n )\n }\n }\n return current\n}\n\n/**\n * Converts a target-value diff into patches the editor engine can apply.\n * Patches addressing items inside sidecar arrays are coalesced into whole\n * `set`s (or `unset`s) of the owning property, taken from the target\n * value.\n *\n * @internal\n */\nexport function toEngineSafePatches(\n patches: PtePatch[],\n targetValue: PortableTextBlock[],\n): PtePatch[] {\n const safe: PtePatch[] = []\n const sidecarPaths: Path[] = []\n\n for (const patch of patches) {\n const sidecarPath = findSidecarArrayPath(patch.path)\n if (!sidecarPath) {\n safe.push(patch)\n continue\n }\n if (!sidecarPaths.some((existing) => pathsEqual(existing, sidecarPath))) {\n sidecarPaths.push(sidecarPath)\n }\n }\n\n for (const sidecarPath of sidecarPaths) {\n const value = getValueAtPath(\n targetValue as unknown as JSONValue,\n sidecarPath,\n )\n safe.push(\n value === undefined\n ? {type: 'unset', path: sidecarPath, origin: 'remote'}\n : {type: 'set', path: sidecarPath, value, origin: 'remote'},\n )\n }\n\n return safe\n}\n\n/**\n * Drops insert items whose `_key` is absent from the remote value. A text\n * paste can stage a temporary span and remove it again within the same\n * transaction; applying the insert without the cleanup would flash the\n * staged content. Callers must only run this once the store value reflects\n * the transaction the patches belong to (see `'apply remote patches'`),\n * otherwise every legitimately new node would be dropped.\n */\nfunction filterInsertsMissingFromRemoteValue(\n patches: PtePatch[],\n remoteValue: PortableTextBlock[],\n): PtePatch[] {\n return patches.flatMap((patch): PtePatch[] => {\n if (patch.type !== 'insert') {\n return [patch]\n }\n\n const parentValue = getValueAtPath(\n remoteValue as unknown as JSONValue,\n patch.path.slice(0, -1),\n )\n if (!Array.isArray(parentValue)) {\n return [patch]\n }\n\n const items = patch.items.filter((item) => {\n const itemKey = getKey(item)\n if (!itemKey) {\n return true\n }\n return parentValue.some((candidate) => getKey(candidate) === itemKey)\n })\n\n return items.length > 0 ? [{...patch, items}] : []\n })\n}\n\nfunction getKey(value: JSONValue): string | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return undefined\n }\n const key = (value as {_key?: unknown})._key\n return typeof key === 'string' ? key : undefined\n}\n\n/**\n * The editor writes `markDefs` as whole-array `set`s. Two clients\n * formatting the same block concurrently then overwrite each other's\n * arrays at the server (last writer wins) while both clients' span\n * `marks` references survive, stranding marks without definitions.\n * Outgoing `markDefs` sets are therefore decomposed into item-level\n * operations against the store (server truth): new definitions insert,\n * changed definitions set by key, and removed definitions unset by key,\n * except definitions the store's own spans still reference (a diverged\n * client's normalizer prunes those spuriously; a later, converged flush\n * removes them for real). Item-keyed operations merge at the server\n * instead of overwriting.\n *\n * @internal\n */\nexport function toMergeableMarkDefsPatches(\n patches: PtePatch[],\n getCurrentValue: () => PortableTextBlock[] | null | undefined,\n): PtePatch[] {\n return patches.flatMap((patch): PtePatch[] => {\n if (\n patch.type !== 'set' ||\n patch.path.at(-1) !== 'markDefs' ||\n !Array.isArray(patch.value)\n ) {\n return [patch]\n }\n const currentValue = getCurrentValue()\n if (!currentValue) {\n return [patch]\n }\n const root = currentValue as unknown as JSONValue\n const storeMarkDefs = getValueAtPath(root, patch.path)\n const storeBlock = getValueAtPath(root, patch.path.slice(0, -1))\n if (\n !Array.isArray(storeMarkDefs) ||\n typeof storeBlock !== 'object' ||\n storeBlock === null\n ) {\n return [patch]\n }\n\n const local = patch.value as Array<{_key?: string}>\n const store = storeMarkDefs as Array<{_key?: string}>\n if (local.some((item) => item._key === undefined)) {\n return [patch]\n }\n\n const referencedKeys = new Set(\n (\n (storeBlock as {children?: Array<{marks?: string[]}>}).children ?? []\n ).flatMap((child) => child.marks ?? []),\n )\n const storeByKey = new Map(store.map((item) => [item._key, item]))\n const localKeys = new Set(local.map((item) => item._key))\n const origin = patch.origin\n\n const ops: PtePatch[] = []\n\n const inserted = local.filter((item) => !storeByKey.has(item._key))\n if (inserted.length > 0) {\n for (const item of inserted) {\n if (item._key === undefined) {\n continue\n }\n // The store view can wrongly lack an in-flight insert (a colliding\n // transaction makes the optimistic rebase fail silently), so a\n // re-send would stack a duplicate. The keyed unset makes the\n // insert an upsert: it is a no-op while the key is absent, and\n // the client's own transactions apply in order, so a re-send\n // self-cleans any earlier copy instead of duplicating it.\n ops.push({\n type: 'unset',\n origin,\n path: [...patch.path, {_key: item._key}],\n })\n }\n ops.push({\n type: 'insert',\n origin,\n position: 'after',\n path: [...patch.path, -1],\n items: inserted as JSONValue[],\n })\n }\n\n for (const item of local) {\n const existing = storeByKey.get(item._key)\n if (existing && JSON.stringify(existing) !== JSON.stringify(item)) {\n ops.push({\n type: 'set',\n origin,\n path: [...patch.path, {_key: item._key as string}],\n value: item as JSONValue,\n })\n }\n }\n\n for (const item of store) {\n if (\n item._key !== undefined &&\n !localKeys.has(item._key) &&\n !referencedKeys.has(item._key)\n ) {\n ops.push({\n type: 'unset',\n origin,\n path: [...patch.path, {_key: item._key}],\n })\n }\n }\n\n return ops\n })\n}\n\n/**\n * Whether a remote patch can resolve against the given editor value.\n * Concurrent edits routinely produce operations addressing nodes another\n * client has already removed or not yet created; sending those into the\n * engine fails loudly (console errors) before being skipped. Callers drop\n * unresolvable patches up front and rely on the follow-up repair sync to\n * converge instead.\n *\n * @internal\n */\nexport function canApplyToValue(\n patch: PtePatch,\n value: PortableTextBlock[] | undefined,\n): boolean {\n if (!value) {\n return true\n }\n const root = value as unknown as JSONValue\n switch (patch.type) {\n // unset needs the node itself; insert needs the sibling at `path`;\n // diffMatchPatch needs the existing string\n case 'unset':\n case 'insert':\n case 'diffMatchPatch':\n return getValueAtPath(root, patch.path) !== undefined\n // set creates its target property, so only the parent must resolve\n case 'set':\n return (\n patch.path.length === 0 ||\n getValueAtPath(root, patch.path.slice(0, -1)) !== undefined\n )\n default:\n return true\n }\n}\n\n/**\n * Filters a patch batch down to the patches that can resolve (see\n * `canApplyToValue`), checking each patch against the value as it stands\n * after the preceding patches applied. A transaction routinely inserts a\n * node and then addresses it, e.g. a span split followed by a `marks` set\n * on the new span, so checking every patch against the starting value\n * would drop valid operations.\n */\nfunction filterResolvablePatches(\n patches: PtePatch[],\n value: PortableTextBlock[] | undefined,\n): PtePatch[] {\n let projected = value\n const resolvable: PtePatch[] = []\n for (const patch of patches) {\n if (!canApplyToValue(patch, projected)) {\n continue\n }\n resolvable.push(patch)\n if (projected) {\n try {\n projected = applyAll(projected, [patch])\n } catch {\n // the engine applies best-effort too; keep the projection as-is\n }\n }\n }\n return resolvable\n}\n\n/**\n * Scopes document-rooted Sanity patches to the given field path, returning\n * field-relative Portable Text Editor patches. Patches outside the field are\n * dropped. Returns `null` when the field (or an ancestor of it) is replaced\n * wholesale, in which case the caller should fall back to a full value sync.\n * Throws when a path expression cannot be converted.\n *\n * @internal\n */\nexport function scopeRemotePatches(\n patches: SanityPatchOperations[],\n fieldPath: string,\n): PtePatch[] | null {\n const prefix = arrayifyPath(fieldPath)\n const converted = convertPatches(patches)\n const scoped: PtePatch[] = []\n\n for (const patch of converted) {\n const overlap = Math.min(patch.path.length, prefix.length)\n let touchesField = true\n for (let index = 0; index < overlap; index++) {\n if (!segmentsEqual(patch.path[index], prefix[index])) {\n touchesField = false\n break\n }\n }\n if (!touchesField) {\n continue\n }\n if (patch.path.length <= prefix.length) {\n // the patch targets the field itself or an ancestor of it, which\n // cannot be expressed as a field-relative operation\n return null\n }\n scoped.push({...patch, path: patch.path.slice(prefix.length)})\n }\n\n return scoped\n}\n\nfunction debugTextOf(value: unknown): string {\n if (!Array.isArray(value)) {\n return ''\n }\n return value\n .map((block) =>\n Array.isArray((block as {children?: unknown}).children)\n ? ((block as {children: Array<{text?: string}>}).children ?? [])\n .map((child) => child.text ?? '')\n .join('')\n : '',\n )\n .join('\\n')\n}\n\n/**\n * How long an editor-versus-store divergence must persist, unchanged,\n * before the whole-value repair acts on it. When a remote transaction\n * arrives interleaved with the listener echoes of this client's own recent\n * edits, the store value is transiently wrong until the echo returns and\n * the rebase corrects it. A repair fired inside that window copies the\n * garbage into the editor and a follow-up repair restores the text at a\n * drifted offset, scrambling words the user typed in the meantime. The\n * window therefore has to outlast a slow listener echo round trip; real\n * divergence is stable and loses nothing by being repaired a beat later.\n *\n * Tests use a short window: their mock stores are synchronous, so echo\n * transients cannot occur, and waiting the production interval would only\n * slow every repair assertion down.\n */\nconst REPAIR_CONFIRM_DELAY =\n // @ts-expect-error - dot notation required for Vite to replace at build time\n process.env.NODE_ENV === 'test' ? 150 : 1000\n\ntype PendingRepair = {signature: string; timer: ReturnType<typeof setTimeout>}\nconst pendingRepairs = new WeakMap<Editor, PendingRepair>()\n\n/**\n * Local edits the editor has emitted but not yet flushed into a mutation.\n * While any exist, the store necessarily lags the editor and a repair diff\n * would \"repair away\" the user's unflushed keystrokes, so repairs must wait.\n */\nconst unflushedEdits = new WeakMap<Editor, boolean>()\n\nfunction computeRepair(\n editor: Editor,\n remoteValue: PortableTextBlock[],\n): {patches: PtePatch[]; convertible: boolean; signature: string} {\n const snapshot = editor.getSnapshot().context.value\n // The signature covers the full (editor, store) state, not just the diff:\n // with repetitive text, two different transients can produce an identical\n // diff, and a repair must only act when the world actually stood still.\n const stateSignature = JSON.stringify([snapshot, remoteValue])\n try {\n const patches = toEngineSafePatches(\n convertPatches(diffValue(snapshot, remoteValue)),\n remoteValue,\n )\n return {patches, convertible: true, signature: stateSignature}\n } catch {\n // diffValue can emit path shapes the converter does not understand\n // (e.g. array slices when multiple items are dropped). The repair then\n // has to fall back to a whole-value update.\n return {patches: [], convertible: false, signature: `!${stateSignature}`}\n }\n}\n\nfunction cancelPendingRepair(editor: Editor) {\n const pending = pendingRepairs.get(editor)\n if (pending) {\n clearTimeout(pending.timer)\n pendingRepairs.delete(editor)\n }\n}\n\nfunction applySync({\n editor,\n getRemoteValue,\n}: {\n editor: Editor\n getRemoteValue: () => PortableTextBlock[] | null | undefined\n}) {\n const remoteValue = getRemoteValue()\n\n if (!remoteValue) {\n return\n }\n\n const first = computeRepair(editor, remoteValue)\n if (first.convertible && first.patches.length === 0) {\n cancelPendingRepair(editor)\n return\n }\n\n if (debug.repair.enabled) {\n debug.repair('editor and store texts diverged %o', {\n editorText: debugTextOf(editor.getSnapshot().context.value),\n remoteText: debugTextOf(remoteValue),\n })\n }\n\n // Same divergence already awaiting confirmation: let that timer decide.\n const pending = pendingRepairs.get(editor)\n if (pending && pending.signature === first.signature) {\n return\n }\n cancelPendingRepair(editor)\n\n const timer = setTimeout(() => {\n pendingRepairs.delete(editor)\n\n // Unflushed local keystrokes mean the store lags the editor by design;\n // a repair now would delete them. Re-arm and wait for the flush (the\n // divergence either disappears once the push round-trips, or persists\n // and gets repaired then).\n if (unflushedEdits.get(editor)) {\n applySync({editor, getRemoteValue})\n return\n }\n\n // Recompute from scratch: the store may have corrected itself (the\n // transient case) or the user may have typed (their edits will push\n // and reconverge through the normal flow); only a divergence that is\n // still byte-for-byte identical is real and safe to repair.\n const latestRemote = getRemoteValue()\n if (!latestRemote) {\n return\n }\n const second = computeRepair(editor, latestRemote)\n if (second.convertible && second.patches.length === 0) {\n return\n }\n if (second.signature !== first.signature) {\n // Still diverged but differently: re-arm so a stable state eventually\n // confirms. Transients converge to the empty diff; genuine divergence\n // stabilizes to a fixed signature within one flush cycle.\n applySync({editor, getRemoteValue})\n return\n }\n\n if (!second.convertible) {\n debug.repair('escalating to whole-value sync (unconvertible diff)')\n editor.send({type: 'update value', value: latestRemote})\n return\n }\n\n const snapshot = editor.getSnapshot().context.value\n debug.repair('applying confirmed repair patches %o', second.patches)\n editor.send({type: 'patches', patches: second.patches, snapshot})\n\n // Patch application is best-effort: the editor skips operations it\n // cannot resolve against its current tree, and concurrent edits to the\n // same range produce keyed operations whose targets no longer exist\n // locally. When the editor is still diverged after the diff-based\n // repair, escalate to the full value sync machinery, which reconciles\n // arbitrary divergence block by block.\n const valueAfterPatches = editor.getSnapshot().context.value\n if (diffValue(valueAfterPatches, latestRemote).length > 0) {\n if (debug.repair.enabled) {\n debug.repair('escalating to whole-value sync %o', {\n editorText: debugTextOf(valueAfterPatches),\n remoteText: debugTextOf(latestRemote),\n })\n }\n editor.send({type: 'update value', value: latestRemote})\n }\n }, REPAIR_CONFIRM_DELAY)\n\n pendingRepairs.set(editor, {signature: first.signature, timer})\n}\n\nconst listenToEditor = fromCallback<AnyEventObject, {editor: Editor}>(\n ({sendBack, input}) => {\n const patchSubscription = input.editor.on('patch', () => {\n // Every 'patch' event is a local edit (remote application suppresses\n // patch generation), so the store now lags the editor until the next\n // mutation flush.\n unflushedEdits.set(input.editor, true)\n sendBack({type: 'patch emitted'})\n })\n\n const mutationSubscription = input.editor.on('mutation', (event) => {\n unflushedEdits.set(input.editor, false)\n if (debug.mutation.enabled) {\n debug.mutation('flushed %o', {\n flushText: debugTextOf(event.value),\n snapshotText: debugTextOf(input.editor.getSnapshot().context.value),\n })\n }\n sendBack({\n type: 'mutation flushed',\n value: event.value,\n patches: event.patches,\n })\n })\n\n return () => {\n patchSubscription.unsubscribe()\n mutationSubscription.unsubscribe()\n }\n },\n)\n\nconst listenToRemote = fromCallback<\n AnyEventObject,\n {onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']}\n>(({sendBack, input}) => {\n return input.onRemoteValueChange(() => {\n sendBack({type: 'remote value changed'})\n })\n})\n\nconst listenToRemotePatches = fromCallback<\n AnyEventObject,\n {onRemotePatches: ValueSyncConfig['onRemotePatches']}\n>(({sendBack, input}) => {\n return input.onRemotePatches?.((patches) => {\n sendBack({type: 'remote patches received', patches})\n })\n})\n\n/**\n * How long the machine must sit in 'idle' (no local keystrokes, no store\n * events) before the one-shot whole-value repair runs. Long enough for the\n * editor's external value snapshot to catch up after the last flush; short\n * enough that residual divergence from concurrent editing heals promptly.\n */\nconst QUIESCENT_REPAIR_DELAY = 500\n\nconst valueSyncMachine = setup({\n types: {\n context: {} as {\n editor: Editor\n getRemoteValue: ValueSyncConfig['getRemoteValue']\n onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']\n onRemotePatches: ValueSyncConfig['onRemotePatches']\n },\n input: {} as {\n editor: Editor\n getRemoteValue: ValueSyncConfig['getRemoteValue']\n onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']\n onRemotePatches: ValueSyncConfig['onRemotePatches']\n },\n events: {} as\n | {type: 'patch emitted'}\n | {\n type: 'mutation flushed'\n value: PortableTextBlock[] | undefined\n patches: PtePatch[]\n }\n | {type: 'remote value changed'}\n | {type: 'remote patches received'; patches: PtePatch[]},\n },\n actions: {\n 'send initial value': ({context}) => {\n context.editor.send({\n type: 'update value',\n value: context.getRemoteValue() ?? [],\n })\n },\n 'push to remote': () => {\n throw new Error('push to remote must be provided via .provide()')\n },\n 'apply sync': ({context}) => {\n applySync({\n editor: context.editor,\n getRemoteValue: context.getRemoteValue,\n })\n },\n 'apply remote patches': ({context, event}) => {\n if (event.type !== 'remote patches received') {\n return\n }\n debug.remote('applying remote patches %o', event.patches)\n // The SDK emits `remote-patches` while it is still computing the\n // state update for that transaction, so the store value visible at\n // this moment does not include it yet. The commit lands within the\n // same synchronous task, so after one microtask the store reflects\n // the transaction, whether the event announced a fresh transaction\n // or replayed one the store already had. Only then can the store\n // value serve as the reference for dropping inserts the transaction\n // itself cleaned up again, and as the target for coalescing\n // sidecar-array item operations (which the engine misroutes) into\n // whole-property sets.\n queueMicrotask(() => {\n const snapshot = context.editor.getSnapshot().context.value\n const remoteValue = context.getRemoteValue()\n const coalesced = remoteValue\n ? toEngineSafePatches(event.patches, remoteValue)\n : event.patches\n const patches = filterResolvablePatches(\n remoteValue\n ? filterInsertsMissingFromRemoteValue(coalesced, remoteValue)\n : coalesced,\n snapshot,\n )\n if (patches.length === 0) {\n return\n }\n context.editor.send({type: 'patches', patches, snapshot})\n })\n },\n },\n actors: {\n 'listen to editor': listenToEditor,\n 'listen to remote': listenToRemote,\n 'listen to remote patches': listenToRemotePatches,\n },\n}).createMachine({\n id: 'value sync',\n context: ({input}) => ({\n editor: input.editor,\n getRemoteValue: input.getRemoteValue,\n onRemoteValueChange: input.onRemoteValueChange,\n onRemotePatches: input.onRemotePatches,\n }),\n entry: ['send initial value'],\n invoke: [\n {\n src: 'listen to editor',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'listen to remote',\n input: ({context}) => ({\n onRemoteValueChange: context.onRemoteValueChange,\n }),\n },\n {\n src: 'listen to remote patches',\n input: ({context}) => ({\n onRemotePatches: context.onRemotePatches,\n }),\n },\n ],\n // Two invariants, learned the hard way:\n //\n // 1. EVERY 'mutation flushed' event must be pushed, in every state. The\n // editor emits mutation events in bursts (one per input batch, e.g.\n // each backspace of a quick delete), and any state without a handler\n // silently drops the flush. A dropped flush permanently diverges the\n // store from the editor, and the whole-value repair then \"heals\" the\n // editor backwards, resurrecting deleted text.\n // 2. The whole-value repair must only run when no local edits can be in\n // flight (quiescent 'idle'). Running it mid-typing diffs the editor\n // against a store that lags the user's keystrokes and stomps them.\n //\n // Operational patches from other clients still apply immediately in\n // every state; the editor merges them with in-flight local changes.\n initial: 'idle',\n states: {\n 'idle': {\n // One-shot repair after a quiet period. Covers divergence left by\n // best-effort patch application while local edits were in flight\n // (those states never repair; see below) when no further store\n // event arrives to trigger the on-change repair.\n after: {\n [QUIESCENT_REPAIR_DELAY]: {\n actions: ['apply sync'],\n },\n },\n on: {\n 'patch emitted': {\n target: 'local write',\n },\n // Mutation events arrive in bursts (one per input batch), so a\n // flush can land after a sibling flush already advanced the state.\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {\n actions: ['apply sync'],\n },\n // No immediate repair here: every remote patch batch also updates\n // the store value, so the accompanying 'remote value changed'\n // event runs the whole-value repair right after.\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n 'local write': {\n on: {\n 'patch emitted': {},\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {\n target: 'pending sync',\n },\n 'remote patches received': {\n target: 'pending sync',\n actions: ['apply remote patches'],\n },\n },\n },\n 'pushing to remote': {\n on: {\n 'patch emitted': {\n target: 'local write',\n },\n 'mutation flushed': {\n actions: ['push to remote'],\n },\n // No repair on the push acknowledgment: the editor snapshot can\n // lag the live document while the user keeps typing, so a diff\n // against the store here resurrects deleted text and duplicates\n // in-flight keystrokes. Once truly idle, the store change or the\n // quiescent delay runs the repair with a caught-up snapshot.\n 'remote value changed': {\n target: 'idle',\n },\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n 'pending sync': {\n on: {\n 'patch emitted': {},\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {},\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n },\n})\n\ninterface SDKValuePluginProps extends DocumentHandle {\n path: string\n}\n\n/**\n * The shape of the `remote-patches` document event emitted by\n * `@sanity/sdk` >= 2.17. Declared structurally so the plugin stays\n * compatible with older SDK typings until its dependency is bumped.\n */\ntype RemotePatchesDocumentEvent = {\n type: 'remote-patches'\n documentId: string\n transactionId: string\n timestamp: string\n previousRev?: string\n patches: SanityPatchOperations[]\n origin: 'local' | 'remote'\n}\n\nfunction isRemotePatchesEvent(event: {\n type: string\n}): event is RemotePatchesDocumentEvent {\n return event.type === 'remote-patches'\n}\n\nfunction getPublishedDocumentId(id: string): string {\n if (id.startsWith('drafts.')) {\n return id.slice('drafts.'.length)\n }\n if (id.startsWith('versions.')) {\n return id.split('.').slice(2).join('.')\n }\n return id\n}\n\n/**\n * @public\n */\nexport function SDKValuePlugin(props: SDKValuePluginProps) {\n const {documentId, documentType, path} = props\n const setSdkValue = useEditDocument(props)\n const instance = useSanityInstance(props)\n const applyActions = useApplyDocumentActions()\n\n const handle = {documentId, documentType, path}\n const {getCurrent, subscribe} = getDocumentState<PortableTextBlock[]>(\n instance,\n handle,\n )\n\n const onRemotePatches = useCallback(\n (callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n },\n [instance, documentId, path],\n )\n\n const pushPatches = useCallback(\n (patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n },\n [applyActions, documentId, documentType, path],\n )\n\n return (\n <ValueSyncPlugin\n getRemoteValue={getCurrent}\n pushValue={setSdkValue}\n onRemoteValueChange={subscribe}\n onRemotePatches={onRemotePatches}\n pushPatches={pushPatches}\n />\n )\n}\n\n/**\n * @internal\n */\ntype ValueSyncConfig = {\n getRemoteValue: () => PortableTextBlock[] | null | undefined\n pushValue: (value: PortableTextBlock[]) => void\n onRemoteValueChange: (callback: () => void) => () => void\n /**\n * Optional patch channel. When provided, operational patches from other\n * clients are applied directly to the editor (in every state) instead of\n * waiting for a whole-value diff, and local editor patches are pushed\n * through `pushPatches`. The whole-value sync remains as a fallback for\n * anything the patch channel cannot express.\n */\n onRemotePatches?: (\n callback: (patches: PtePatch[]) => void,\n ) => (() => void) | undefined\n /**\n * Pushes the editor's own operational patches to the remote store. May\n * throw when a patch cannot be converted, in which case the plugin falls\n * back to pushing the whole value.\n */\n pushPatches?: (patches: PtePatch[]) => void\n}\n\n/**\n * NOTE: You are probably looking for SDKValuePlugin instead of this.\n * This is a lower-level plugin that only handles syncing the value\n * between the editor and a remote source. It does not know anything\n * about Sanity documents or how to fetch/update them.\n *\n * May be removed in the future, do not rely on this directly.\n *\n * @internal\n */\nexport function ValueSyncPlugin(props: ValueSyncConfig) {\n const {\n getRemoteValue,\n pushValue,\n onRemoteValueChange,\n onRemotePatches,\n pushPatches,\n } = props\n const editor = useEditor()\n\n useActorRef(\n valueSyncMachine.provide({\n actions: {\n 'push to remote': ({context, event}) => {\n if (event.type !== 'mutation flushed') {\n return\n }\n\n if (pushPatches && event.patches.length > 0) {\n try {\n const mergeable = toMergeableMarkDefsPatches(\n event.patches,\n context.getRemoteValue,\n )\n debug.push('pushing patches %o', mergeable)\n pushPatches(mergeable)\n return\n } catch {\n // fall back to pushing the whole value below\n }\n }\n\n if (debug.push.enabled) {\n debug.push(\n 'pushing whole value %s',\n debugTextOf(\n event.value ?? context.editor.getSnapshot().context.value,\n ),\n )\n }\n pushValue(event.value ?? context.editor.getSnapshot().context.value)\n },\n },\n }),\n {\n input: {\n editor,\n getRemoteValue,\n onRemoteValueChange,\n onRemotePatches,\n },\n },\n )\n\n return null\n}\n","import type {EditorSelection, RangeDecoration} from '@portabletext/editor'\nimport {\n usePresenceForDocument,\n useReportPresence,\n type DocumentHandle,\n type UseReportPresenceOptions,\n} from '@sanity/sdk-react'\nimport {useMemo, type PropsWithChildren, type ReactElement} from 'react'\nimport {useLocalSelection, useRemoteCursors} from './plugin.presence-sync'\nimport {arrayifyPath} from './plugin.sdk-value'\n\n/**\n * `@sanity/sdk-react` exports the presence hooks and their option types but not\n * the presence data types, so these are derived from what it does export.\n */\ntype DocumentPresence = ReturnType<\n typeof usePresenceForDocument\n>['presence'][number]\n\n/**\n * Draws one remote participant's caret. This package has no opinion about how a\n * caret looks, so it is the caller's to provide.\n *\n * @public\n */\nexport type RenderCursorFunction = (\n cursor: SDKRemoteCursor,\n) => (props: PropsWithChildren) => ReactElement\n\n/**\n * A remote participant's caret, together with who they are.\n *\n * Declared in full rather than extending the internal cursor type, so nothing in\n * the public API refers to a type consumers cannot import.\n *\n * @public\n */\nexport interface SDKRemoteCursor {\n /**\n * Identifies one editing session rather than one person. The same user in two\n * tabs reports two sessions and therefore draws two carets.\n */\n sessionId: string\n /**\n * Where the participant last reported their selection.\n */\n selection: EditorSelection\n user: DocumentPresence['user']\n}\n\n/**\n * Props for {@link SDKPresencePlugin}.\n *\n * @public\n */\nexport interface SDKPresencePluginProps extends DocumentHandle {\n /**\n * The document path of the Portable Text field, for example `content`. The\n * same form `SDKValuePlugin` takes.\n */\n path: string\n}\n\n/**\n * Options for {@link useSDKPresenceCursors}.\n *\n * @public\n */\nexport interface UseSDKPresenceCursorsOptions extends DocumentHandle {\n path: string\n renderCursor: RenderCursorFunction\n}\n\n/**\n * Reports the local user's caret in a Portable Text field, so other people in\n * the same document can see where they are.\n *\n * Place it inside `EditorProvider`. Prefer `SDKPortableTextEditable`, which\n * reports and draws in one component; reach for this when you render\n * `PortableTextEditable` yourself.\n *\n * Which document is reported follows the handle's perspective, or the ambient\n * one from `ResourceProvider`. Pass the plain document id and let the\n * perspective select the draft, the published document, or a release version.\n *\n * @public\n */\nexport function SDKPresencePlugin(props: SDKPresencePluginProps) {\n const {path, ...handle} = props\n const selection = useLocalSelection()\n const fieldPath = useFieldPath(path)\n\n useReportPresence({...handle, path: fieldPath, selection})\n\n return null\n}\n\n/**\n * Other people's carets in a Portable Text field, as range decorations.\n *\n * Pass the result to `<PortableTextEditable rangeDecorations={...} />`, or use\n * `SDKPortableTextEditable` and skip the wiring. Each caret stays anchored as\n * the local user types, and disappears when the participant leaves or their\n * session expires.\n *\n * The local user is never included, so an app does not draw its own caret.\n * Participants are counted by session, so the same person in two tabs draws two\n * carets.\n *\n * @public\n */\nexport function useSDKPresenceCursors(\n options: UseSDKPresenceCursorsOptions,\n): RangeDecoration[] {\n const {path, renderCursor, ...handle} = options\n const fieldPath = useFieldPath(path)\n // Exact id matching: a caret reported while editing a draft must not be drawn\n // in a release version of the same document, where the text differs.\n const {presence} = usePresenceForDocument({\n ...handle,\n path: fieldPath,\n excludeVersions: true,\n })\n\n const cursors = useMemo(\n () =>\n presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n ),\n [presence],\n )\n\n return useRemoteCursors({cursors, renderCursor})\n}\n\n/**\n * The SDK's presence hooks address fields by path array, because a field-level\n * path inside Portable Text needs keyed segments. This package takes the same\n * string expression as `SDKValuePlugin` everywhere and converts here.\n */\nfunction useFieldPath(\n path: string,\n): NonNullable<UseReportPresenceOptions['path']> {\n return useMemo(() => arrayifyPath(path), [path])\n}\n","import type {PropsWithChildren} from 'react'\nimport type {RenderCursorFunction, SDKRemoteCursor} from './plugin.sdk-presence'\n\n/**\n * Mid-tone hues, so a caret stays legible whether the app is light or dark. This\n * package cannot read the app's theme, so it does not try.\n */\nconst CARET_COLORS = [\n '#e0508a',\n '#c05fd8',\n '#7c66e8',\n '#2f8fdd',\n '#1f9c8f',\n '#4f9c2f',\n '#c98a1c',\n '#d4603a',\n]\n\nconst DOT_SIZE = 6\n\n/**\n * Picks a stable colour for a participant.\n *\n * Keyed on the user rather than the session, so one person in two tabs draws two\n * carets in the same colour. The Studio colours by user for the same reason.\n *\n * @public\n */\nexport function getCaretColor(userId: string): string {\n let hash = 0\n for (let index = 0; index < userId.length; index++) {\n hash = (hash * 31 + userId.charCodeAt(index)) % 1000003\n }\n return CARET_COLORS[hash % CARET_COLORS.length]\n}\n\n/**\n * Draws a remote caret when no `renderCursor` was given: a coloured line with a\n * dot above it, and the participant's name on hover.\n *\n * Deliberately plain, and styled inline so it needs no stylesheet. Pass your own\n * `renderCursor` to match your design.\n *\n * @public\n */\nexport const renderDefaultCursor: RenderCursorFunction =\n (cursor) => (props) => (\n <DefaultCaret cursor={cursor}>{props.children}</DefaultCaret>\n )\n\nfunction DefaultCaret(props: PropsWithChildren<{cursor: SDKRemoteCursor}>) {\n const {cursor, children} = props\n const color = getCaretColor(cursor.user.sanityUserId)\n const displayName = cursor.user.profile.displayName\n\n return (\n <>\n <span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>\n {children}\n </>\n )\n}\n","import {\n PortableTextEditable,\n type PortableTextEditableProps,\n type RangeDecoration,\n} from '@portabletext/editor'\nimport type {DocumentHandle} from '@sanity/sdk-react'\nimport {useMemo} from 'react'\nimport {\n SDKPresencePlugin,\n useSDKPresenceCursors,\n type RenderCursorFunction,\n} from './plugin.sdk-presence'\nimport {SDKValuePlugin} from './plugin.sdk-value'\nimport {renderDefaultCursor} from './presence-caret'\n\n/**\n * Props for {@link SDKPortableTextEditable}.\n *\n * @public\n */\nexport interface SDKPortableTextEditableProps\n extends\n DocumentHandle,\n // `resource` is both a document handle field and an RDFa HTML attribute, so\n // the handle wins. Dropping the attribute costs nothing in an editor.\n Omit<PortableTextEditableProps, keyof DocumentHandle> {\n /**\n * The document path of the Portable Text field, for example `content`.\n */\n path: string\n /**\n * Draws one remote participant's caret. Omit it for the built-in caret, which\n * needs no styling of your own. Pass `null` to draw no carets at all while\n * still reporting the local user's presence.\n */\n renderCursor?: RenderCursorFunction | null\n}\n\n/**\n * What {@link splitEditableProps} pulls apart.\n *\n * @internal\n */\nexport interface SplitEditableProps {\n handle: DocumentHandle\n path: string\n renderCursor: RenderCursorFunction | null | undefined\n rangeDecorations: RangeDecoration[] | undefined\n editableProps: Omit<\n PortableTextEditableProps,\n keyof DocumentHandle | 'rangeDecorations'\n >\n}\n\n/**\n * A `PortableTextEditable` wired to a Sanity document: the field's value syncs\n * both ways, the local user's caret is reported, and other people's carets are\n * drawn.\n *\n * Place it inside `EditorProvider` in place of `PortableTextEditable`. Every\n * other prop is forwarded untouched, and `rangeDecorations` you pass are kept\n * and merged with the presence carets rather than replaced.\n *\n * Nothing else is needed: this replaces a separate `SDKValuePlugin`.\n *\n * @example\n * ```tsx\n * <EditorProvider initialConfig={{schemaDefinition}}>\n * <SDKPortableTextEditable\n * {...documentHandle}\n * path=\"content\"\n * renderCursor={({user}) => (props) => (\n * <Caret user={user}>{props.children}</Caret>\n * )}\n * />\n * </EditorProvider>\n * ```\n *\n * @public\n */\nexport function SDKPortableTextEditable(props: SDKPortableTextEditableProps) {\n const {handle, path, renderCursor, rangeDecorations, editableProps} =\n splitEditableProps(props)\n\n const renderer = resolveCursorRenderer(renderCursor)\n\n const cursors = useSDKPresenceCursors({\n ...handle,\n path,\n renderCursor: renderer.renderCursor,\n })\n\n const decorations = useMemo(\n () =>\n mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors),\n [cursors, rangeDecorations, renderer.drawCursors],\n )\n\n return (\n <>\n <PortableTextEditable {...editableProps} rangeDecorations={decorations} />\n <SDKValuePlugin {...handle} path={path} />\n <SDKPresencePlugin {...handle} path={path} />\n </>\n )\n}\n\n/**\n * Splits the props into the document handle, this component's own props, and\n * what is forwarded to `PortableTextEditable`. Keeping handle fields out of the\n * forwarded set is what stops them reaching the DOM as attributes.\n *\n * @internal\n */\nexport function splitEditableProps(\n props: SDKPortableTextEditableProps,\n): SplitEditableProps {\n const {\n documentId,\n documentType,\n projectId,\n dataset,\n resource,\n resourceName,\n source,\n liveEdit,\n perspective,\n path,\n renderCursor,\n rangeDecorations,\n ...editableProps\n } = props\n\n return {\n // Only the fields the caller actually passed. The SDK resolves an ambient\n // perspective and resource from context with `Object.hasOwn`, so forwarding\n // `perspective: undefined` would override what `ResourceProvider` set rather\n // than defer to it, and the field would sync and report against the draft\n // instead of the release the app is showing.\n handle: {\n documentId,\n documentType,\n ...('projectId' in props && {projectId}),\n ...('dataset' in props && {dataset}),\n ...('resource' in props && {resource}),\n ...('resourceName' in props && {resourceName}),\n ...('source' in props && {source}),\n ...('liveEdit' in props && {liveEdit}),\n ...('perspective' in props && {perspective}),\n },\n path,\n renderCursor,\n rangeDecorations,\n editableProps,\n }\n}\n\n/**\n * Decides which caret component to draw with, and whether to draw at all.\n *\n * Presence is subscribed to either way, because hooks cannot be called\n * conditionally, so switching carets off discards the decorations rather than\n * skipping the work.\n *\n * @internal\n */\nexport function resolveCursorRenderer(\n renderCursor: RenderCursorFunction | null | undefined,\n): {renderCursor: RenderCursorFunction; drawCursors: boolean} {\n return {\n renderCursor: renderCursor ?? renderDefaultCursor,\n drawCursors: renderCursor !== null,\n }\n}\n\n/**\n * Appends the presence carets to whatever decorations the caller passed, so\n * theirs survive. The Studio merges the same way. When carets are switched off\n * the caller's own decorations pass straight through.\n *\n * @internal\n */\nexport function mergePresenceDecorations(\n rangeDecorations: RangeDecoration[] | undefined,\n cursors: RangeDecoration[],\n drawCursors: boolean,\n): RangeDecoration[] | undefined {\n if (!drawCursors) {\n return rangeDecorations\n }\n return [...(rangeDecorations ?? []), ...cursors]\n}\n\n/**\n * Splitting the props by hand is what keeps document handle fields off the DOM,\n * so every field has to be listed above. `sdk-editable.test.ts` fails to compile\n * if `DocumentHandle` gains one. Note that `@sanity/sdk-react` adds fields to the\n * core handle, so it has to be read from there.\n */\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["(cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n }","useCallback(\n (cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n },\n [cursors],\n )","toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved})","useMemo(\n () => toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved}),\n [cursors, overrides, renderCursor, onCursorMoved],\n )","(callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n }","useCallback(\n (callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n },\n [instance, documentId, path],\n )","(patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n }","patches","useCallback(\n (patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n },\n [applyActions, documentId, documentType, path],\n )","<ValueSyncPlugin\n getRemoteValue={getCurrent}\n pushValue={setSdkValue}\n onRemoteValueChange={subscribe}\n onRemotePatches={onRemotePatches}\n pushPatches={pushPatches}\n />","valueSyncMachine.provide({\n actions: {\n 'push to remote': ({context, event}) => {\n if (event.type !== 'mutation flushed') {\n return\n }\n\n if (pushPatches && event.patches.length > 0) {\n try {\n const mergeable = toMergeableMarkDefsPatches(\n event.patches,\n context.getRemoteValue,\n )\n debug.push('pushing patches %o', mergeable)\n pushPatches(mergeable)\n return\n } catch {\n // fall back to pushing the whole value below\n }\n }\n\n if (debug.push.enabled) {\n debug.push(\n 'pushing whole value %s',\n debugTextOf(\n event.value ?? context.editor.getSnapshot().context.value,\n ),\n )\n }\n pushValue(event.value ?? context.editor.getSnapshot().context.value)\n },\n },\n })","{context, event}","{\n input: {\n editor,\n getRemoteValue,\n onRemoteValueChange,\n onRemotePatches,\n },\n }","normalizeDocumentHandle(props)","{...handle, path: fieldPath, selection}","normalizeDocumentHandle(options)","{\n ...handle,\n path: fieldPath,\n excludeVersions: true,\n }","presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n )","(participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : []","useMemo(\n () =>\n presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n ),\n [presence],\n )","{cursors, renderCursor}","arrayifyPath(path)","useMemo(() => arrayifyPath(path), [path])","getCaretColor(cursor.user.sanityUserId)","sanityUserId","{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }","`2px solid ${color}`","{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }","<span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />","`presence-caret-dot-${cursor.sessionId}`","<span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>","`presence-caret-${cursor.sessionId}`","<>\n <span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>\n {children}\n </>","resolveCursorRenderer(renderCursor)","{\n ...handle,\n path,\n renderCursor: renderer.renderCursor,\n }","renderCursor","mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors)","drawCursors","useMemo(\n () =>\n mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors),\n [cursors, rangeDecorations, renderer.drawCursors],\n )","<PortableTextEditable {...editableProps} rangeDecorations={decorations} />","<SDKValuePlugin {...handle} path={path} />","<SDKPresencePlugin {...handle} path={path} />","<>\n <PortableTextEditable {...editableProps} rangeDecorations={decorations} />\n <SDKValuePlugin {...handle} path={path} />\n <SDKPresencePlugin {...handle} path={path} />\n </>"],"sources":["../src/presence-cursors.ts","../src/plugin.presence-sync.tsx","../src/debug.ts","../src/sdk-document-handle.ts","../src/plugin.sdk-value.tsx","../src/plugin.sdk-presence.tsx","../src/presence-caret.tsx","../src/sdk-editable.tsx"],"sourcesContent":["import type {EditorSelection, RangeDecoration} from '@portabletext/editor'\nimport {isEqualSelections} from '@portabletext/editor/utils'\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * A remote participant's position in the editor.\n *\n * @public\n */\nexport interface RemoteCursor {\n /**\n * Identifies one editing session rather than one person. The same user in two\n * tabs reports two sessions and therefore draws two carets.\n */\n sessionId: string\n /**\n * The selection the participant last reported, or `null` when they have none.\n */\n selection: EditorSelection\n}\n\n/**\n * Where the local editor has pushed a remote caret, and the reported selection\n * it was pushed from.\n *\n * Typing above or inside a remote caret moves it, and the editor reports the\n * new position through `onMoved`. Keeping the selection it moved from is what\n * lets a later report from that participant take over again.\n *\n * @public\n */\nexport interface CursorOverride {\n reported: EditorSelection\n current: EditorSelection\n}\n\n/**\n * Options for {@link toRangeDecorations}.\n *\n * @public\n */\nexport interface RangeDecorationOptions<TCursor extends RemoteCursor> {\n cursors: readonly TCursor[]\n overrides: ReadonlyMap<string, CursorOverride>\n /**\n * Builds the component that draws one caret. The plugin has no opinion about\n * how a caret looks, so this is the caller's to provide.\n */\n renderCursor: (cursor: TCursor) => (props: PropsWithChildren) => ReactElement\n onCursorMoved?: (cursor: TCursor, selection: EditorSelection) => void\n}\n\n/**\n * Reduces a selection to a caret at its focus point.\n *\n * Presence answers where someone is, so decorating their whole selection would\n * highlight text the local user never selected. The Studio collapses the same\n * way, which keeps the two consistent when both are open on a document.\n *\n * @public\n */\nexport function collapseToCaret(selection: EditorSelection): EditorSelection {\n if (selection === null) {\n return null\n }\n return {anchor: selection.focus, focus: selection.focus}\n}\n\n/**\n * Where one remote caret should be drawn.\n *\n * A caret the local editor has moved keeps that moved position for as long as\n * the participant keeps reporting the same selection. A changed report wins,\n * because the participant has actually moved.\n *\n * @public\n */\nexport function resolveCursorSelection(\n cursor: RemoteCursor,\n override: CursorOverride | undefined,\n): EditorSelection {\n if (override && isEqualSelections(override.reported, cursor.selection)) {\n return override.current\n }\n return collapseToCaret(cursor.selection)\n}\n\n/**\n * Records where the editor moved a caret to.\n *\n * Overrides for sessions that are no longer present are dropped, so a long\n * editing session does not accumulate one entry per participant who ever\n * visited. Returns the map untouched when nothing changed.\n *\n * @public\n */\nexport function recordCursorMove(\n overrides: ReadonlyMap<string, CursorOverride>,\n cursors: readonly RemoteCursor[],\n cursor: RemoteCursor,\n selection: EditorSelection,\n): ReadonlyMap<string, CursorOverride> {\n const moved = collapseToCaret(selection)\n const existing = overrides.get(cursor.sessionId)\n const alreadyRecorded =\n existing !== undefined &&\n isEqualSelections(existing.reported, cursor.selection) &&\n isEqualSelections(existing.current, moved)\n\n const live = new Set(cursors.map((candidate) => candidate.sessionId))\n const staleKeys = [...overrides.keys()].filter((key) => !live.has(key))\n\n if (alreadyRecorded && staleKeys.length === 0) {\n return overrides\n }\n\n const next = new Map(overrides)\n for (const key of staleKeys) {\n next.delete(key)\n }\n next.set(cursor.sessionId, {reported: cursor.selection, current: moved})\n return next\n}\n\n/**\n * Maps remote cursors to range decorations for\n * `<PortableTextEditable rangeDecorations={...} />`.\n *\n * Participants with no caret to draw are skipped, so someone who clears their\n * selection stops drawing without being treated as having left.\n *\n * @public\n */\nexport function toRangeDecorations<TCursor extends RemoteCursor>(\n options: RangeDecorationOptions<TCursor>,\n): RangeDecoration[] {\n const {cursors, overrides, renderCursor, onCursorMoved} = options\n const decorations: RangeDecoration[] = []\n\n for (const cursor of cursors) {\n const selection = resolveCursorSelection(\n cursor,\n overrides.get(cursor.sessionId),\n )\n if (selection === null) {\n continue\n }\n decorations.push({\n component: renderCursor(cursor),\n selection,\n payload: {sessionId: cursor.sessionId},\n onMoved: onCursorMoved\n ? (details) => onCursorMoved(cursor, details.newSelection)\n : undefined,\n })\n }\n\n return decorations\n}\n","import {\n useEditor,\n useEditorSelector,\n type EditorSelection,\n type RangeDecoration,\n} from '@portabletext/editor'\nimport {getSelection} from '@portabletext/editor/selectors'\nimport {isEqualSelections} from '@portabletext/editor/utils'\nimport {\n useCallback,\n useMemo,\n useState,\n type PropsWithChildren,\n type ReactElement,\n} from 'react'\nimport {\n recordCursorMove,\n toRangeDecorations,\n type CursorOverride,\n type RemoteCursor,\n} from './presence-cursors'\n\n/**\n * Options for {@link useRemoteCursors}.\n *\n * @public\n */\nexport interface UseRemoteCursorsOptions<TCursor extends RemoteCursor> {\n /**\n * The remote participants to draw. Keep this array referentially stable, for\n * example with `useMemo`, so that decorations are not rebuilt on every\n * render.\n */\n cursors: readonly TCursor[]\n renderCursor: (cursor: TCursor) => (props: PropsWithChildren) => ReactElement\n}\n\n/**\n * The local user's selection, deduped by value.\n *\n * The editor produces a fresh selection object on every snapshot change, so\n * subscribing to it directly would report presence far more often than the\n * caret actually moves.\n *\n * @public\n */\nexport function useLocalSelection(): EditorSelection {\n const editor = useEditor()\n // Direction is ignored, which `isEqualSelections` already does: a caret is\n // drawn at the focus point, so reversing a selection moves nothing.\n return useEditorSelector(editor, getSelection, isEqualSelections)\n}\n\n/**\n * Turns remote cursors into range decorations, keeping each caret anchored as\n * the local user edits.\n *\n * Knows nothing about Sanity or the SDK, so it can also drive carets from\n * another source or from a test fixture. `useSDKPresenceCursors` is the version\n * wired to SDK presence.\n *\n * @public\n */\nexport function useRemoteCursors<TCursor extends RemoteCursor>(\n options: UseRemoteCursorsOptions<TCursor>,\n): RangeDecoration[] {\n const {cursors, renderCursor} = options\n // Only the moved carets need to survive a render. Where every caret is drawn\n // is derived from them plus the latest report, so there is no state to keep\n // in step with the incoming cursors.\n const [overrides, setOverrides] =\n useState<ReadonlyMap<string, CursorOverride>>(EMPTY_OVERRIDES)\n\n const onCursorMoved = useCallback(\n (cursor: TCursor, selection: EditorSelection) => {\n setOverrides((previous) =>\n recordCursorMove(previous, cursors, cursor, selection),\n )\n },\n [cursors],\n )\n\n return useMemo(\n () => toRangeDecorations({cursors, overrides, renderCursor, onCursorMoved}),\n [cursors, overrides, renderCursor, onCursorMoved],\n )\n}\n\nconst EMPTY_OVERRIDES: ReadonlyMap<string, CursorOverride> = new Map()\n","import rawDebug from 'debug'\n\n// Keep in sync with `packages/editor/src/internal-utils/debug.ts`: sharing\n// the `pte:` root lets `localStorage.debug = 'pte:*'` interleave this\n// plugin's sync traces with the editor's own output on one timeline.\nconst rootName = 'pte:plugin-sdk-value:'\n\nfunction createDebugger(name: string): rawDebug.Debugger {\n const namespace = `${rootName}${name}`\n if (rawDebug && rawDebug.enabled(namespace)) {\n return rawDebug(namespace)\n }\n return rawDebug(rootName)\n}\n\nexport const debug = {\n mutation: createDebugger('mutation'),\n push: createDebugger('push'),\n remote: createDebugger('remote'),\n repair: createDebugger('repair'),\n}\n","import type {DocumentHandle, DocumentResource} from '@sanity/sdk-react'\n\ntype DocumentHandleWithLegacySource = DocumentHandle & {\n source?: DocumentResource\n}\n\nexport function normalizeDocumentHandle<T extends DocumentHandle>(\n handle: T & DocumentHandleWithLegacySource,\n): Omit<T, 'source'> {\n const {source, ...normalizedHandle} = handle\n\n if (normalizedHandle.resource !== undefined || source === undefined) {\n return normalizedHandle\n }\n\n return {...normalizedHandle, resource: source}\n}\n","import {\n useEditor,\n type Editor,\n type PortableTextBlock,\n type Patch as PtePatch,\n} from '@portabletext/editor'\nimport {\n applyAll,\n type JSONValue,\n type Path,\n type PathSegment,\n type InsertPatch as PteInsertPatch,\n} from '@portabletext/patches'\nimport {diffValue, type SanityPatchOperations} from '@sanity/diff-patch'\nimport {\n parsePath,\n type ExprNode,\n type PathNode,\n type SegmentNode,\n type ThisNode,\n} from '@sanity/json-match'\nimport {\n editDocument,\n getDocumentState,\n subscribeDocumentEvents,\n useApplyDocumentActions,\n useEditDocument,\n useSanityInstance,\n type DocumentHandle,\n type DocumentResource,\n type EditDocumentAction,\n} from '@sanity/sdk-react'\nimport {useActorRef} from '@xstate/react'\nimport {useCallback} from 'react'\nimport {fromCallback, setup, type AnyEventObject} from 'xstate'\nimport {debug} from './debug'\nimport {normalizeDocumentHandle} from './sdk-document-handle'\n\ntype InsertPatch = Required<Pick<SanityPatchOperations, 'insert'>>\n\nconst ARRAYIFY_ERROR_MESSAGE =\n 'Unexpected path format from diffValue output. Please report this issue.'\n\nfunction* getSegments(\n node: PathNode,\n): Generator<Exclude<SegmentNode, ThisNode>> {\n if (node.base) {\n yield* getSegments(node.base)\n }\n if (node.segment.type !== 'This') {\n yield node.segment\n }\n}\n\nfunction isKeyPath(node: ExprNode): node is PathNode {\n if (node.type !== 'Path') {\n return false\n }\n if (node.base) {\n return false\n }\n if (node.recursive) {\n return false\n }\n if (node.segment.type !== 'Identifier') {\n return false\n }\n return node.segment.name === '_key'\n}\n\nexport function arrayifyPath(pathExpr: string): Path {\n const node = parsePath(pathExpr)\n if (!node) {\n return []\n }\n if (node.type !== 'Path') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n\n return Array.from(getSegments(node)).map((segment): PathSegment => {\n if (segment.type === 'Identifier') {\n return segment.name\n }\n if (segment.type !== 'Subscript') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n if (segment.elements.length !== 1) {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n\n const [element] = segment.elements\n if (element.type === 'Number') {\n return element.value\n }\n\n if (element.type !== 'Comparison') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n if (element.operator !== '==') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n const keyPathNode = [element.left, element.right].find(isKeyPath)\n if (!keyPathNode) {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n const other = element.left === keyPathNode ? element.right : element.left\n if (other.type !== 'String') {\n throw new Error(ARRAYIFY_ERROR_MESSAGE)\n }\n return {_key: other.value}\n })\n}\n\nexport function convertPatches(patches: SanityPatchOperations[]): PtePatch[] {\n return patches.flatMap((p) => {\n return Object.entries(p).flatMap(([type, values]): PtePatch[] => {\n const origin = 'remote'\n\n switch (type) {\n case 'set':\n case 'setIfMissing':\n case 'diffMatchPatch':\n case 'inc':\n case 'dec': {\n return Object.entries(values).map(\n ([pathExpr, value]) =>\n ({type, value, origin, path: arrayifyPath(pathExpr)}) as PtePatch,\n )\n }\n case 'unset': {\n if (!Array.isArray(values)) {\n return []\n }\n return values.map(arrayifyPath).map((path) => ({type, origin, path}))\n }\n case 'insert': {\n const {items, ...rest} = values as InsertPatch['insert']\n type InsertPosition = PteInsertPatch['position']\n const position = Object.keys(rest).at(0) as InsertPosition | undefined\n\n if (!position) {\n return []\n }\n const pathExpr = (rest as {[K in InsertPosition]: string})[position]\n const insertPatch: PteInsertPatch = {\n type,\n origin,\n position,\n path: arrayifyPath(pathExpr),\n items: items as JSONValue[],\n }\n\n return [insertPatch]\n }\n\n default: {\n return []\n }\n }\n })\n })\n}\n\nconst STRINGIFY_ERROR_MESSAGE =\n 'Unable to convert an editor patch path to a Sanity path expression.'\n\n/**\n * Converts a Portable Text Editor patch path (an array of segments) into a\n * Sanity json-match path expression. The inverse of `arrayifyPath`.\n *\n * @internal\n */\nexport function stringifyPatchPath(path: Path): string {\n let result = ''\n for (const segment of path) {\n if (typeof segment === 'string') {\n result = result === '' ? segment : `${result}.${segment}`\n } else if (typeof segment === 'number') {\n result = `${result}[${segment}]`\n } else if (\n typeof segment === 'object' &&\n segment !== null &&\n '_key' in segment\n ) {\n result = `${result}[_key==\"${segment._key}\"]`\n } else {\n throw new Error(STRINGIFY_ERROR_MESSAGE)\n }\n }\n return result\n}\n\nfunction prefixPathExpression(prefix: string, expression: string): string {\n if (expression === '') {\n return prefix\n }\n return expression.startsWith('[')\n ? `${prefix}${expression}`\n : `${prefix}.${expression}`\n}\n\n/**\n * `SanityPatchOperations` from `@sanity/diff-patch` only covers the\n * operations `diffValue` emits; the editor can additionally produce these.\n */\ntype SanityPatchOperationsWithExtras = SanityPatchOperations & {\n setIfMissing?: {[path: string]: unknown}\n inc?: {[path: string]: number}\n dec?: {[path: string]: number}\n}\n\n/**\n * Converts Portable Text Editor patches into Sanity patch operations rooted\n * at the given document field path. Throws if a patch cannot be converted;\n * callers should fall back to pushing the whole value.\n *\n * @internal\n */\nexport function convertPatchesToSanity(\n patches: PtePatch[],\n options: {prefix: string},\n): SanityPatchOperationsWithExtras[] {\n return patches.map((patch): SanityPatchOperationsWithExtras => {\n const pathExpression = prefixPathExpression(\n options.prefix,\n stringifyPatchPath(patch.path),\n )\n\n switch (patch.type) {\n case 'set':\n return {set: {[pathExpression]: patch.value}}\n case 'setIfMissing':\n return {setIfMissing: {[pathExpression]: patch.value}}\n case 'unset':\n // The editor unsets the whole field when it becomes empty. Write an\n // empty array instead: unsetting the field would leave other clients\n // unable to reconcile against it (their remote value disappears).\n if (patch.path.length === 0) {\n return {set: {[pathExpression]: []}}\n }\n return {unset: [pathExpression]}\n case 'diffMatchPatch':\n return {diffMatchPatch: {[pathExpression]: patch.value}}\n case 'inc':\n return {inc: {[pathExpression]: patch.value as number}}\n case 'dec':\n return {dec: {[pathExpression]: patch.value as number}}\n case 'insert':\n return {\n insert: {\n [patch.position]: pathExpression,\n items: patch.items,\n } as SanityPatchOperations['insert'],\n }\n default:\n throw new Error(STRINGIFY_ERROR_MESSAGE)\n }\n })\n}\n\nfunction segmentsEqual(a: PathSegment, b: PathSegment): boolean {\n if (typeof a === 'string' || typeof a === 'number') {\n return a === b\n }\n if (typeof b === 'string' || typeof b === 'number') {\n return false\n }\n if (Array.isArray(a) || Array.isArray(b)) {\n return false\n }\n return a._key === b._key\n}\n\nfunction pathsEqual(a: Path, b: Path): boolean {\n return (\n a.length === b.length &&\n a.every((segment, index) => segmentsEqual(segment, b[index]))\n )\n}\n\n/**\n * The editor engine can only resolve keyed/indexed segments against the\n * root block array and (possibly nested) `children` arrays. Operations\n * that address items of any other array, e.g. a block's `markDefs` or a\n * span's `marks`, misapply. When a patch path enters such a sidecar\n * array, this returns the path of the array itself so callers can fall\n * back to replacing the whole property; returns `null` for paths the\n * engine can apply.\n *\n * @internal\n */\nexport function findSidecarArrayPath(path: Path): Path | null {\n // a keyed/numeric segment is only resolvable first (root block array)\n // or directly after a `children` property\n let expectNode = true\n for (let index = 0; index < path.length; index++) {\n const segment = path[index]\n if (typeof segment === 'string') {\n expectNode = segment === 'children'\n } else {\n if (!expectNode) {\n return path.slice(0, index)\n }\n expectNode = false\n }\n }\n return null\n}\n\nfunction getValueAtPath(value: JSONValue, path: Path): JSONValue | undefined {\n let current: JSONValue | undefined = value\n for (const segment of path) {\n if (current === null || current === undefined) {\n return undefined\n }\n if (typeof segment === 'number') {\n if (!Array.isArray(current)) {\n return undefined\n }\n current = current[segment < 0 ? current.length + segment : segment]\n } else if (typeof segment === 'string') {\n if (typeof current !== 'object' || Array.isArray(current)) {\n return undefined\n }\n current = (current as {[key: string]: JSONValue})[segment]\n } else if (Array.isArray(segment)) {\n // index tuples address ranges, not single values\n return undefined\n } else {\n if (!Array.isArray(current)) {\n return undefined\n }\n current = current.find(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n !Array.isArray(item) &&\n (item as {_key?: unknown})._key === segment._key,\n )\n }\n }\n return current\n}\n\n/**\n * Converts a target-value diff into patches the editor engine can apply.\n * Patches addressing items inside sidecar arrays are coalesced into whole\n * `set`s (or `unset`s) of the owning property, taken from the target\n * value.\n *\n * @internal\n */\nexport function toEngineSafePatches(\n patches: PtePatch[],\n targetValue: PortableTextBlock[],\n): PtePatch[] {\n const safe: PtePatch[] = []\n const sidecarPaths: Path[] = []\n\n for (const patch of patches) {\n const sidecarPath = findSidecarArrayPath(patch.path)\n if (!sidecarPath) {\n safe.push(patch)\n continue\n }\n if (!sidecarPaths.some((existing) => pathsEqual(existing, sidecarPath))) {\n sidecarPaths.push(sidecarPath)\n }\n }\n\n for (const sidecarPath of sidecarPaths) {\n const value = getValueAtPath(\n targetValue as unknown as JSONValue,\n sidecarPath,\n )\n safe.push(\n value === undefined\n ? {type: 'unset', path: sidecarPath, origin: 'remote'}\n : {type: 'set', path: sidecarPath, value, origin: 'remote'},\n )\n }\n\n return safe\n}\n\n/**\n * Drops insert items whose `_key` is absent from the remote value. A text\n * paste can stage a temporary span and remove it again within the same\n * transaction; applying the insert without the cleanup would flash the\n * staged content. Callers must only run this once the store value reflects\n * the transaction the patches belong to (see `'apply remote patches'`),\n * otherwise every legitimately new node would be dropped.\n */\nfunction filterInsertsMissingFromRemoteValue(\n patches: PtePatch[],\n remoteValue: PortableTextBlock[],\n): PtePatch[] {\n return patches.flatMap((patch): PtePatch[] => {\n if (patch.type !== 'insert') {\n return [patch]\n }\n\n const parentValue = getValueAtPath(\n remoteValue as unknown as JSONValue,\n patch.path.slice(0, -1),\n )\n if (!Array.isArray(parentValue)) {\n return [patch]\n }\n\n const items = patch.items.filter((item) => {\n const itemKey = getKey(item)\n if (!itemKey) {\n return true\n }\n return parentValue.some((candidate) => getKey(candidate) === itemKey)\n })\n\n return items.length > 0 ? [{...patch, items}] : []\n })\n}\n\nfunction getKey(value: JSONValue): string | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return undefined\n }\n const key = (value as {_key?: unknown})._key\n return typeof key === 'string' ? key : undefined\n}\n\n/**\n * The editor writes `markDefs` as whole-array `set`s. Two clients\n * formatting the same block concurrently then overwrite each other's\n * arrays at the server (last writer wins) while both clients' span\n * `marks` references survive, stranding marks without definitions.\n * Outgoing `markDefs` sets are therefore decomposed into item-level\n * operations against the store (server truth): new definitions insert,\n * changed definitions set by key, and removed definitions unset by key,\n * except definitions the store's own spans still reference (a diverged\n * client's normalizer prunes those spuriously; a later, converged flush\n * removes them for real). Item-keyed operations merge at the server\n * instead of overwriting.\n *\n * @internal\n */\nexport function toMergeableMarkDefsPatches(\n patches: PtePatch[],\n getCurrentValue: () => PortableTextBlock[] | null | undefined,\n): PtePatch[] {\n return patches.flatMap((patch): PtePatch[] => {\n if (\n patch.type !== 'set' ||\n patch.path.at(-1) !== 'markDefs' ||\n !Array.isArray(patch.value)\n ) {\n return [patch]\n }\n const currentValue = getCurrentValue()\n if (!currentValue) {\n return [patch]\n }\n const root = currentValue as unknown as JSONValue\n const storeMarkDefs = getValueAtPath(root, patch.path)\n const storeBlock = getValueAtPath(root, patch.path.slice(0, -1))\n if (\n !Array.isArray(storeMarkDefs) ||\n typeof storeBlock !== 'object' ||\n storeBlock === null\n ) {\n return [patch]\n }\n\n const local = patch.value as Array<{_key?: string}>\n const store = storeMarkDefs as Array<{_key?: string}>\n if (local.some((item) => item._key === undefined)) {\n return [patch]\n }\n\n const referencedKeys = new Set(\n (\n (storeBlock as {children?: Array<{marks?: string[]}>}).children ?? []\n ).flatMap((child) => child.marks ?? []),\n )\n const storeByKey = new Map(store.map((item) => [item._key, item]))\n const localKeys = new Set(local.map((item) => item._key))\n const origin = patch.origin\n\n const ops: PtePatch[] = []\n\n const inserted = local.filter((item) => !storeByKey.has(item._key))\n if (inserted.length > 0) {\n for (const item of inserted) {\n if (item._key === undefined) {\n continue\n }\n // The store view can wrongly lack an in-flight insert (a colliding\n // transaction makes the optimistic rebase fail silently), so a\n // re-send would stack a duplicate. The keyed unset makes the\n // insert an upsert: it is a no-op while the key is absent, and\n // the client's own transactions apply in order, so a re-send\n // self-cleans any earlier copy instead of duplicating it.\n ops.push({\n type: 'unset',\n origin,\n path: [...patch.path, {_key: item._key}],\n })\n }\n ops.push({\n type: 'insert',\n origin,\n position: 'after',\n path: [...patch.path, -1],\n items: inserted as JSONValue[],\n })\n }\n\n for (const item of local) {\n const existing = storeByKey.get(item._key)\n if (existing && JSON.stringify(existing) !== JSON.stringify(item)) {\n ops.push({\n type: 'set',\n origin,\n path: [...patch.path, {_key: item._key as string}],\n value: item as JSONValue,\n })\n }\n }\n\n for (const item of store) {\n if (\n item._key !== undefined &&\n !localKeys.has(item._key) &&\n !referencedKeys.has(item._key)\n ) {\n ops.push({\n type: 'unset',\n origin,\n path: [...patch.path, {_key: item._key}],\n })\n }\n }\n\n return ops\n })\n}\n\n/**\n * Whether a remote patch can resolve against the given editor value.\n * Concurrent edits routinely produce operations addressing nodes another\n * client has already removed or not yet created; sending those into the\n * engine fails loudly (console errors) before being skipped. Callers drop\n * unresolvable patches up front and rely on the follow-up repair sync to\n * converge instead.\n *\n * @internal\n */\nexport function canApplyToValue(\n patch: PtePatch,\n value: PortableTextBlock[] | undefined,\n): boolean {\n if (!value) {\n return true\n }\n const root = value as unknown as JSONValue\n switch (patch.type) {\n // unset needs the node itself; insert needs the sibling at `path`;\n // diffMatchPatch needs the existing string\n case 'unset':\n case 'insert':\n case 'diffMatchPatch':\n return getValueAtPath(root, patch.path) !== undefined\n // set creates its target property, so only the parent must resolve\n case 'set':\n return (\n patch.path.length === 0 ||\n getValueAtPath(root, patch.path.slice(0, -1)) !== undefined\n )\n default:\n return true\n }\n}\n\n/**\n * Filters a patch batch down to the patches that can resolve (see\n * `canApplyToValue`), checking each patch against the value as it stands\n * after the preceding patches applied. A transaction routinely inserts a\n * node and then addresses it, e.g. a span split followed by a `marks` set\n * on the new span, so checking every patch against the starting value\n * would drop valid operations.\n */\nfunction filterResolvablePatches(\n patches: PtePatch[],\n value: PortableTextBlock[] | undefined,\n): PtePatch[] {\n let projected = value\n const resolvable: PtePatch[] = []\n for (const patch of patches) {\n if (!canApplyToValue(patch, projected)) {\n continue\n }\n resolvable.push(patch)\n if (projected) {\n try {\n projected = applyAll(projected, [patch])\n } catch {\n // the engine applies best-effort too; keep the projection as-is\n }\n }\n }\n return resolvable\n}\n\n/**\n * Scopes document-rooted Sanity patches to the given field path, returning\n * field-relative Portable Text Editor patches. Patches outside the field are\n * dropped. Returns `null` when the field (or an ancestor of it) is replaced\n * wholesale, in which case the caller should fall back to a full value sync.\n * Throws when a path expression cannot be converted.\n *\n * @internal\n */\nexport function scopeRemotePatches(\n patches: SanityPatchOperations[],\n fieldPath: string,\n): PtePatch[] | null {\n const prefix = arrayifyPath(fieldPath)\n const converted = convertPatches(patches)\n const scoped: PtePatch[] = []\n\n for (const patch of converted) {\n const overlap = Math.min(patch.path.length, prefix.length)\n let touchesField = true\n for (let index = 0; index < overlap; index++) {\n if (!segmentsEqual(patch.path[index], prefix[index])) {\n touchesField = false\n break\n }\n }\n if (!touchesField) {\n continue\n }\n if (patch.path.length <= prefix.length) {\n // the patch targets the field itself or an ancestor of it, which\n // cannot be expressed as a field-relative operation\n return null\n }\n scoped.push({...patch, path: patch.path.slice(prefix.length)})\n }\n\n return scoped\n}\n\nfunction debugTextOf(value: unknown): string {\n if (!Array.isArray(value)) {\n return ''\n }\n return value\n .map((block) =>\n Array.isArray((block as {children?: unknown}).children)\n ? ((block as {children: Array<{text?: string}>}).children ?? [])\n .map((child) => child.text ?? '')\n .join('')\n : '',\n )\n .join('\\n')\n}\n\n/**\n * How long an editor-versus-store divergence must persist, unchanged,\n * before the whole-value repair acts on it. When a remote transaction\n * arrives interleaved with the listener echoes of this client's own recent\n * edits, the store value is transiently wrong until the echo returns and\n * the rebase corrects it. A repair fired inside that window copies the\n * garbage into the editor and a follow-up repair restores the text at a\n * drifted offset, scrambling words the user typed in the meantime. The\n * window therefore has to outlast a slow listener echo round trip; real\n * divergence is stable and loses nothing by being repaired a beat later.\n *\n * Tests use a short window: their mock stores are synchronous, so echo\n * transients cannot occur, and waiting the production interval would only\n * slow every repair assertion down.\n */\nconst REPAIR_CONFIRM_DELAY =\n // @ts-expect-error - dot notation required for Vite to replace at build time\n process.env.NODE_ENV === 'test' ? 150 : 1000\n\ntype PendingRepair = {signature: string; timer: ReturnType<typeof setTimeout>}\nconst pendingRepairs = new WeakMap<Editor, PendingRepair>()\n\n/**\n * Local edits the editor has emitted but not yet flushed into a mutation.\n * While any exist, the store necessarily lags the editor and a repair diff\n * would \"repair away\" the user's unflushed keystrokes, so repairs must wait.\n */\nconst unflushedEdits = new WeakMap<Editor, boolean>()\n\nfunction computeRepair(\n editor: Editor,\n remoteValue: PortableTextBlock[],\n): {patches: PtePatch[]; convertible: boolean; signature: string} {\n const snapshot = editor.getSnapshot().context.value\n // The signature covers the full (editor, store) state, not just the diff:\n // with repetitive text, two different transients can produce an identical\n // diff, and a repair must only act when the world actually stood still.\n const stateSignature = JSON.stringify([snapshot, remoteValue])\n try {\n const patches = toEngineSafePatches(\n convertPatches(diffValue(snapshot, remoteValue)),\n remoteValue,\n )\n return {patches, convertible: true, signature: stateSignature}\n } catch {\n // diffValue can emit path shapes the converter does not understand\n // (e.g. array slices when multiple items are dropped). The repair then\n // has to fall back to a whole-value update.\n return {patches: [], convertible: false, signature: `!${stateSignature}`}\n }\n}\n\nfunction cancelPendingRepair(editor: Editor) {\n const pending = pendingRepairs.get(editor)\n if (pending) {\n clearTimeout(pending.timer)\n pendingRepairs.delete(editor)\n }\n}\n\nfunction applySync({\n editor,\n getRemoteValue,\n}: {\n editor: Editor\n getRemoteValue: () => PortableTextBlock[] | null | undefined\n}) {\n const remoteValue = getRemoteValue()\n\n if (!remoteValue) {\n return\n }\n\n const first = computeRepair(editor, remoteValue)\n if (first.convertible && first.patches.length === 0) {\n cancelPendingRepair(editor)\n return\n }\n\n if (debug.repair.enabled) {\n debug.repair('editor and store texts diverged %o', {\n editorText: debugTextOf(editor.getSnapshot().context.value),\n remoteText: debugTextOf(remoteValue),\n })\n }\n\n // Same divergence already awaiting confirmation: let that timer decide.\n const pending = pendingRepairs.get(editor)\n if (pending && pending.signature === first.signature) {\n return\n }\n cancelPendingRepair(editor)\n\n const timer = setTimeout(() => {\n pendingRepairs.delete(editor)\n\n // Unflushed local keystrokes mean the store lags the editor by design;\n // a repair now would delete them. Re-arm and wait for the flush (the\n // divergence either disappears once the push round-trips, or persists\n // and gets repaired then).\n if (unflushedEdits.get(editor)) {\n applySync({editor, getRemoteValue})\n return\n }\n\n // Recompute from scratch: the store may have corrected itself (the\n // transient case) or the user may have typed (their edits will push\n // and reconverge through the normal flow); only a divergence that is\n // still byte-for-byte identical is real and safe to repair.\n const latestRemote = getRemoteValue()\n if (!latestRemote) {\n return\n }\n const second = computeRepair(editor, latestRemote)\n if (second.convertible && second.patches.length === 0) {\n return\n }\n if (second.signature !== first.signature) {\n // Still diverged but differently: re-arm so a stable state eventually\n // confirms. Transients converge to the empty diff; genuine divergence\n // stabilizes to a fixed signature within one flush cycle.\n applySync({editor, getRemoteValue})\n return\n }\n\n if (!second.convertible) {\n debug.repair('escalating to whole-value sync (unconvertible diff)')\n editor.send({type: 'update value', value: latestRemote})\n return\n }\n\n const snapshot = editor.getSnapshot().context.value\n debug.repair('applying confirmed repair patches %o', second.patches)\n editor.send({type: 'patches', patches: second.patches, snapshot})\n\n // Patch application is best-effort: the editor skips operations it\n // cannot resolve against its current tree, and concurrent edits to the\n // same range produce keyed operations whose targets no longer exist\n // locally. When the editor is still diverged after the diff-based\n // repair, escalate to the full value sync machinery, which reconciles\n // arbitrary divergence block by block.\n const valueAfterPatches = editor.getSnapshot().context.value\n if (diffValue(valueAfterPatches, latestRemote).length > 0) {\n if (debug.repair.enabled) {\n debug.repair('escalating to whole-value sync %o', {\n editorText: debugTextOf(valueAfterPatches),\n remoteText: debugTextOf(latestRemote),\n })\n }\n editor.send({type: 'update value', value: latestRemote})\n }\n }, REPAIR_CONFIRM_DELAY)\n\n pendingRepairs.set(editor, {signature: first.signature, timer})\n}\n\nconst listenToEditor = fromCallback<AnyEventObject, {editor: Editor}>(\n ({sendBack, input}) => {\n const patchSubscription = input.editor.on('patch', () => {\n // Every 'patch' event is a local edit (remote application suppresses\n // patch generation), so the store now lags the editor until the next\n // mutation flush.\n unflushedEdits.set(input.editor, true)\n sendBack({type: 'patch emitted'})\n })\n\n const mutationSubscription = input.editor.on('mutation', (event) => {\n unflushedEdits.set(input.editor, false)\n if (debug.mutation.enabled) {\n debug.mutation('flushed %o', {\n flushText: debugTextOf(event.value),\n snapshotText: debugTextOf(input.editor.getSnapshot().context.value),\n })\n }\n sendBack({\n type: 'mutation flushed',\n value: event.value,\n patches: event.patches,\n })\n })\n\n return () => {\n patchSubscription.unsubscribe()\n mutationSubscription.unsubscribe()\n }\n },\n)\n\nconst listenToRemote = fromCallback<\n AnyEventObject,\n {onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']}\n>(({sendBack, input}) => {\n return input.onRemoteValueChange(() => {\n sendBack({type: 'remote value changed'})\n })\n})\n\nconst listenToRemotePatches = fromCallback<\n AnyEventObject,\n {onRemotePatches: ValueSyncConfig['onRemotePatches']}\n>(({sendBack, input}) => {\n return input.onRemotePatches?.((patches) => {\n sendBack({type: 'remote patches received', patches})\n })\n})\n\n/**\n * How long the machine must sit in 'idle' (no local keystrokes, no store\n * events) before the one-shot whole-value repair runs. Long enough for the\n * editor's external value snapshot to catch up after the last flush; short\n * enough that residual divergence from concurrent editing heals promptly.\n */\nconst QUIESCENT_REPAIR_DELAY = 500\n\nconst valueSyncMachine = setup({\n types: {\n context: {} as {\n editor: Editor\n getRemoteValue: ValueSyncConfig['getRemoteValue']\n onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']\n onRemotePatches: ValueSyncConfig['onRemotePatches']\n },\n input: {} as {\n editor: Editor\n getRemoteValue: ValueSyncConfig['getRemoteValue']\n onRemoteValueChange: ValueSyncConfig['onRemoteValueChange']\n onRemotePatches: ValueSyncConfig['onRemotePatches']\n },\n events: {} as\n | {type: 'patch emitted'}\n | {\n type: 'mutation flushed'\n value: PortableTextBlock[] | undefined\n patches: PtePatch[]\n }\n | {type: 'remote value changed'}\n | {type: 'remote patches received'; patches: PtePatch[]},\n },\n actions: {\n 'send initial value': ({context}) => {\n context.editor.send({\n type: 'update value',\n value: context.getRemoteValue() ?? [],\n })\n },\n 'push to remote': () => {\n throw new Error('push to remote must be provided via .provide()')\n },\n 'apply sync': ({context}) => {\n applySync({\n editor: context.editor,\n getRemoteValue: context.getRemoteValue,\n })\n },\n 'apply remote patches': ({context, event}) => {\n if (event.type !== 'remote patches received') {\n return\n }\n debug.remote('applying remote patches %o', event.patches)\n // The SDK emits `remote-patches` while it is still computing the\n // state update for that transaction, so the store value visible at\n // this moment does not include it yet. The commit lands within the\n // same synchronous task, so after one microtask the store reflects\n // the transaction, whether the event announced a fresh transaction\n // or replayed one the store already had. Only then can the store\n // value serve as the reference for dropping inserts the transaction\n // itself cleaned up again, and as the target for coalescing\n // sidecar-array item operations (which the engine misroutes) into\n // whole-property sets.\n queueMicrotask(() => {\n const snapshot = context.editor.getSnapshot().context.value\n const remoteValue = context.getRemoteValue()\n const coalesced = remoteValue\n ? toEngineSafePatches(event.patches, remoteValue)\n : event.patches\n const patches = filterResolvablePatches(\n remoteValue\n ? filterInsertsMissingFromRemoteValue(coalesced, remoteValue)\n : coalesced,\n snapshot,\n )\n if (patches.length === 0) {\n return\n }\n context.editor.send({type: 'patches', patches, snapshot})\n })\n },\n },\n actors: {\n 'listen to editor': listenToEditor,\n 'listen to remote': listenToRemote,\n 'listen to remote patches': listenToRemotePatches,\n },\n}).createMachine({\n id: 'value sync',\n context: ({input}) => ({\n editor: input.editor,\n getRemoteValue: input.getRemoteValue,\n onRemoteValueChange: input.onRemoteValueChange,\n onRemotePatches: input.onRemotePatches,\n }),\n entry: ['send initial value'],\n invoke: [\n {\n src: 'listen to editor',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'listen to remote',\n input: ({context}) => ({\n onRemoteValueChange: context.onRemoteValueChange,\n }),\n },\n {\n src: 'listen to remote patches',\n input: ({context}) => ({\n onRemotePatches: context.onRemotePatches,\n }),\n },\n ],\n // Two invariants, learned the hard way:\n //\n // 1. EVERY 'mutation flushed' event must be pushed, in every state. The\n // editor emits mutation events in bursts (one per input batch, e.g.\n // each backspace of a quick delete), and any state without a handler\n // silently drops the flush. A dropped flush permanently diverges the\n // store from the editor, and the whole-value repair then \"heals\" the\n // editor backwards, resurrecting deleted text.\n // 2. The whole-value repair must only run when no local edits can be in\n // flight (quiescent 'idle'). Running it mid-typing diffs the editor\n // against a store that lags the user's keystrokes and stomps them.\n //\n // Operational patches from other clients still apply immediately in\n // every state; the editor merges them with in-flight local changes.\n initial: 'idle',\n states: {\n 'idle': {\n // One-shot repair after a quiet period. Covers divergence left by\n // best-effort patch application while local edits were in flight\n // (those states never repair; see below) when no further store\n // event arrives to trigger the on-change repair.\n after: {\n [QUIESCENT_REPAIR_DELAY]: {\n actions: ['apply sync'],\n },\n },\n on: {\n 'patch emitted': {\n target: 'local write',\n },\n // Mutation events arrive in bursts (one per input batch), so a\n // flush can land after a sibling flush already advanced the state.\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {\n actions: ['apply sync'],\n },\n // No immediate repair here: every remote patch batch also updates\n // the store value, so the accompanying 'remote value changed'\n // event runs the whole-value repair right after.\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n 'local write': {\n on: {\n 'patch emitted': {},\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {\n target: 'pending sync',\n },\n 'remote patches received': {\n target: 'pending sync',\n actions: ['apply remote patches'],\n },\n },\n },\n 'pushing to remote': {\n on: {\n 'patch emitted': {\n target: 'local write',\n },\n 'mutation flushed': {\n actions: ['push to remote'],\n },\n // No repair on the push acknowledgment: the editor snapshot can\n // lag the live document while the user keeps typing, so a diff\n // against the store here resurrects deleted text and duplicates\n // in-flight keystrokes. Once truly idle, the store change or the\n // quiescent delay runs the repair with a caught-up snapshot.\n 'remote value changed': {\n target: 'idle',\n },\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n 'pending sync': {\n on: {\n 'patch emitted': {},\n 'mutation flushed': {\n target: 'pushing to remote',\n actions: ['push to remote'],\n },\n 'remote value changed': {},\n 'remote patches received': {\n actions: ['apply remote patches'],\n },\n },\n },\n },\n})\n\ninterface SDKValuePluginProps extends DocumentHandle {\n source?: DocumentResource\n path: string\n}\n\n/**\n * The shape of the `remote-patches` document event emitted by\n * `@sanity/sdk` >= 2.17. Declared structurally so the plugin stays\n * compatible with older SDK typings until its dependency is bumped.\n */\ntype RemotePatchesDocumentEvent = {\n type: 'remote-patches'\n documentId: string\n transactionId: string\n timestamp: string\n previousRev?: string\n patches: SanityPatchOperations[]\n origin: 'local' | 'remote'\n}\n\nfunction isRemotePatchesEvent(event: {\n type: string\n}): event is RemotePatchesDocumentEvent {\n return event.type === 'remote-patches'\n}\n\nfunction getPublishedDocumentId(id: string): string {\n if (id.startsWith('drafts.')) {\n return id.slice('drafts.'.length)\n }\n if (id.startsWith('versions.')) {\n return id.split('.').slice(2).join('.')\n }\n return id\n}\n\n/**\n * @public\n */\nexport function SDKValuePlugin(props: SDKValuePluginProps) {\n const normalizedProps = normalizeDocumentHandle(props)\n const {documentId, documentType, path} = normalizedProps\n const setSdkValue = useEditDocument(normalizedProps)\n const instance = useSanityInstance()\n const applyActions = useApplyDocumentActions()\n\n const handle = {documentId, documentType, path}\n const {getCurrent, subscribe} = getDocumentState<PortableTextBlock[]>(\n instance,\n handle,\n )\n\n const onRemotePatches = useCallback(\n (callback: (patches: PtePatch[]) => void) => {\n return subscribeDocumentEvents(instance, {\n eventHandler: (event) => {\n // widen before narrowing: `remote-patches` is not part of the\n // `DocumentEvent` union in older SDK typings\n const candidate: {type: string} = event\n if (!isRemotePatchesEvent(candidate)) {\n return\n }\n // our own transactions are already reflected in the editor\n if (candidate.origin !== 'remote') {\n return\n }\n if (\n getPublishedDocumentId(candidate.documentId) !==\n getPublishedDocumentId(documentId)\n ) {\n return\n }\n\n let patches: PtePatch[] | null\n try {\n patches = scopeRemotePatches(candidate.patches, path)\n } catch {\n // unconvertible patch shapes fall back to the whole-value sync\n // driven by `onRemoteValueChange`\n return\n }\n if (patches && patches.length > 0) {\n callback(patches)\n }\n },\n })\n },\n [instance, documentId, path],\n )\n\n const pushPatches = useCallback(\n (patches: PtePatch[]) => {\n const sanityPatches = convertPatchesToSanity(patches, {prefix: path})\n // `preserveOperations` ships in @sanity/sdk >= 2.17; the intersection\n // type keeps the plugin compatible with older SDK typings\n const action: EditDocumentAction & {preserveOperations?: boolean} = {\n ...editDocument(\n {documentId, documentType},\n sanityPatches as Parameters<typeof editDocument>[1],\n ),\n preserveOperations: true,\n }\n applyActions(action)\n },\n [applyActions, documentId, documentType, path],\n )\n\n return (\n <ValueSyncPlugin\n getRemoteValue={getCurrent}\n pushValue={setSdkValue}\n onRemoteValueChange={subscribe}\n onRemotePatches={onRemotePatches}\n pushPatches={pushPatches}\n />\n )\n}\n\n/**\n * @internal\n */\ntype ValueSyncConfig = {\n getRemoteValue: () => PortableTextBlock[] | null | undefined\n pushValue: (value: PortableTextBlock[]) => void\n onRemoteValueChange: (callback: () => void) => () => void\n /**\n * Optional patch channel. When provided, operational patches from other\n * clients are applied directly to the editor (in every state) instead of\n * waiting for a whole-value diff, and local editor patches are pushed\n * through `pushPatches`. The whole-value sync remains as a fallback for\n * anything the patch channel cannot express.\n */\n onRemotePatches?: (\n callback: (patches: PtePatch[]) => void,\n ) => (() => void) | undefined\n /**\n * Pushes the editor's own operational patches to the remote store. May\n * throw when a patch cannot be converted, in which case the plugin falls\n * back to pushing the whole value.\n */\n pushPatches?: (patches: PtePatch[]) => void\n}\n\n/**\n * NOTE: You are probably looking for SDKValuePlugin instead of this.\n * This is a lower-level plugin that only handles syncing the value\n * between the editor and a remote source. It does not know anything\n * about Sanity documents or how to fetch/update them.\n *\n * May be removed in the future, do not rely on this directly.\n *\n * @internal\n */\nexport function ValueSyncPlugin(props: ValueSyncConfig) {\n const {\n getRemoteValue,\n pushValue,\n onRemoteValueChange,\n onRemotePatches,\n pushPatches,\n } = props\n const editor = useEditor()\n\n useActorRef(\n valueSyncMachine.provide({\n actions: {\n 'push to remote': ({context, event}) => {\n if (event.type !== 'mutation flushed') {\n return\n }\n\n if (pushPatches && event.patches.length > 0) {\n try {\n const mergeable = toMergeableMarkDefsPatches(\n event.patches,\n context.getRemoteValue,\n )\n debug.push('pushing patches %o', mergeable)\n pushPatches(mergeable)\n return\n } catch {\n // fall back to pushing the whole value below\n }\n }\n\n if (debug.push.enabled) {\n debug.push(\n 'pushing whole value %s',\n debugTextOf(\n event.value ?? context.editor.getSnapshot().context.value,\n ),\n )\n }\n pushValue(event.value ?? context.editor.getSnapshot().context.value)\n },\n },\n }),\n {\n input: {\n editor,\n getRemoteValue,\n onRemoteValueChange,\n onRemotePatches,\n },\n },\n )\n\n return null\n}\n","import type {EditorSelection, RangeDecoration} from '@portabletext/editor'\nimport {\n usePresenceForDocument,\n useReportPresence,\n type DocumentHandle,\n type DocumentResource,\n type UseReportPresenceOptions,\n} from '@sanity/sdk-react'\nimport {useMemo, type PropsWithChildren, type ReactElement} from 'react'\nimport {useLocalSelection, useRemoteCursors} from './plugin.presence-sync'\nimport {arrayifyPath} from './plugin.sdk-value'\nimport {normalizeDocumentHandle} from './sdk-document-handle'\n\n/**\n * `@sanity/sdk-react` exports the presence hooks and their option types but not\n * the presence data types, so these are derived from what it does export.\n */\ntype DocumentPresence = ReturnType<\n typeof usePresenceForDocument\n>['presence'][number]\n\n/**\n * Draws one remote participant's caret. This package has no opinion about how a\n * caret looks, so it is the caller's to provide.\n *\n * @public\n */\nexport type RenderCursorFunction = (\n cursor: SDKRemoteCursor,\n) => (props: PropsWithChildren) => ReactElement\n\n/**\n * A remote participant's caret, together with who they are.\n *\n * Declared in full rather than extending the internal cursor type, so nothing in\n * the public API refers to a type consumers cannot import.\n *\n * @public\n */\nexport interface SDKRemoteCursor {\n /**\n * Identifies one editing session rather than one person. The same user in two\n * tabs reports two sessions and therefore draws two carets.\n */\n sessionId: string\n /**\n * Where the participant last reported their selection.\n */\n selection: EditorSelection\n user: DocumentPresence['user']\n}\n\n/**\n * Props for {@link SDKPresencePlugin}.\n *\n * @public\n */\nexport interface SDKPresencePluginProps extends DocumentHandle {\n /**\n * @deprecated Use `resource` instead.\n */\n source?: DocumentResource\n /**\n * The document path of the Portable Text field, for example `content`. The\n * same form `SDKValuePlugin` takes.\n */\n path: string\n}\n\n/**\n * Options for {@link useSDKPresenceCursors}.\n *\n * @public\n */\nexport interface UseSDKPresenceCursorsOptions extends DocumentHandle {\n /**\n * @deprecated Use `resource` instead.\n */\n source?: DocumentResource\n path: string\n renderCursor: RenderCursorFunction\n}\n\n/**\n * Reports the local user's caret in a Portable Text field, so other people in\n * the same document can see where they are.\n *\n * Place it inside `EditorProvider`. Prefer `SDKPortableTextEditable`, which\n * reports and draws in one component; reach for this when you render\n * `PortableTextEditable` yourself.\n *\n * Which document is reported follows the handle's perspective, or the ambient\n * one from `ResourceProvider`. Pass the plain document id and let the\n * perspective select the draft, the published document, or a release version.\n *\n * @public\n */\nexport function SDKPresencePlugin(props: SDKPresencePluginProps) {\n const {path, ...handle} = normalizeDocumentHandle(props)\n const selection = useLocalSelection()\n const fieldPath = useFieldPath(path)\n\n useReportPresence({...handle, path: fieldPath, selection})\n\n return null\n}\n\n/**\n * Other people's carets in a Portable Text field, as range decorations.\n *\n * Pass the result to `<PortableTextEditable rangeDecorations={...} />`, or use\n * `SDKPortableTextEditable` and skip the wiring. Each caret stays anchored as\n * the local user types, and disappears when the participant leaves or their\n * session expires.\n *\n * The local user is never included, so an app does not draw its own caret.\n * Participants are counted by session, so the same person in two tabs draws two\n * carets.\n *\n * @public\n */\nexport function useSDKPresenceCursors(\n options: UseSDKPresenceCursorsOptions,\n): RangeDecoration[] {\n const {path, renderCursor, ...handle} = normalizeDocumentHandle(options)\n const fieldPath = useFieldPath(path)\n // Exact id matching: a caret reported while editing a draft must not be drawn\n // in a release version of the same document, where the text differs.\n const {presence} = usePresenceForDocument({\n ...handle,\n path: fieldPath,\n excludeVersions: true,\n })\n\n const cursors = useMemo(\n () =>\n presence.flatMap((participant): SDKRemoteCursor[] =>\n participant.selection\n ? [\n {\n sessionId: participant.sessionId,\n selection: participant.selection,\n user: participant.user,\n },\n ]\n : [],\n ),\n [presence],\n )\n\n return useRemoteCursors({cursors, renderCursor})\n}\n\n/**\n * The SDK's presence hooks address fields by path array, because a field-level\n * path inside Portable Text needs keyed segments. This package takes the same\n * string expression as `SDKValuePlugin` everywhere and converts here.\n */\nfunction useFieldPath(\n path: string,\n): NonNullable<UseReportPresenceOptions['path']> {\n return useMemo(() => arrayifyPath(path), [path])\n}\n","import type {PropsWithChildren} from 'react'\nimport type {RenderCursorFunction, SDKRemoteCursor} from './plugin.sdk-presence'\n\n/**\n * Mid-tone hues, so a caret stays legible whether the app is light or dark. This\n * package cannot read the app's theme, so it does not try.\n */\nconst CARET_COLORS = [\n '#e0508a',\n '#c05fd8',\n '#7c66e8',\n '#2f8fdd',\n '#1f9c8f',\n '#4f9c2f',\n '#c98a1c',\n '#d4603a',\n]\n\nconst DOT_SIZE = 6\n\n/**\n * Picks a stable colour for a participant.\n *\n * Keyed on the user rather than the session, so one person in two tabs draws two\n * carets in the same colour. The Studio colours by user for the same reason.\n *\n * @public\n */\nexport function getCaretColor(userId: string): string {\n let hash = 0\n for (let index = 0; index < userId.length; index++) {\n hash = (hash * 31 + userId.charCodeAt(index)) % 1000003\n }\n return CARET_COLORS[hash % CARET_COLORS.length]\n}\n\n/**\n * Draws a remote caret when no `renderCursor` was given: a coloured line with a\n * dot above it, and the participant's name on hover.\n *\n * Deliberately plain, and styled inline so it needs no stylesheet. Pass your own\n * `renderCursor` to match your design.\n *\n * @public\n */\nexport const renderDefaultCursor: RenderCursorFunction =\n (cursor) => (props) => (\n <DefaultCaret cursor={cursor}>{props.children}</DefaultCaret>\n )\n\nfunction DefaultCaret(props: PropsWithChildren<{cursor: SDKRemoteCursor}>) {\n const {cursor, children} = props\n const color = getCaretColor(cursor.user.sanityUserId)\n const displayName = cursor.user.profile.displayName\n\n return (\n <>\n <span\n // Without this the caret becomes editable content and the local user can\n // put their own cursor inside it.\n contentEditable={false}\n data-testid={`presence-caret-${cursor.sessionId}`}\n style={{\n borderLeft: `2px solid ${color}`,\n marginLeft: -1,\n position: 'relative',\n // The line must not swallow clicks meant for the text under it.\n pointerEvents: 'none',\n }}\n >\n <span\n data-testid={`presence-caret-dot-${cursor.sessionId}`}\n style={{\n backgroundColor: color,\n borderRadius: '50%',\n height: DOT_SIZE,\n left: -1,\n pointerEvents: 'auto',\n position: 'absolute',\n top: -(DOT_SIZE - 1),\n transform: 'translateX(-50%)',\n width: DOT_SIZE,\n }}\n title={displayName}\n />\n </span>\n {children}\n </>\n )\n}\n","import {\n PortableTextEditable,\n type PortableTextEditableProps,\n type RangeDecoration,\n} from '@portabletext/editor'\nimport type {DocumentHandle, DocumentResource} from '@sanity/sdk-react'\nimport {useMemo} from 'react'\nimport {\n SDKPresencePlugin,\n useSDKPresenceCursors,\n type RenderCursorFunction,\n} from './plugin.sdk-presence'\nimport {SDKValuePlugin} from './plugin.sdk-value'\nimport {renderDefaultCursor} from './presence-caret'\nimport {normalizeDocumentHandle} from './sdk-document-handle'\n\n/**\n * Props for {@link SDKPortableTextEditable}.\n *\n * @public\n */\nexport interface SDKPortableTextEditableProps\n extends\n DocumentHandle,\n // `resource` is both a document handle field and an RDFa HTML attribute, so\n // the handle wins. Dropping the attribute costs nothing in an editor.\n Omit<PortableTextEditableProps, keyof DocumentHandle> {\n /**\n * @deprecated Use `resource` instead.\n */\n source?: DocumentResource\n /**\n * The document path of the Portable Text field, for example `content`.\n */\n path: string\n /**\n * Draws one remote participant's caret. Omit it for the built-in caret, which\n * needs no styling of your own. Pass `null` to draw no carets at all while\n * still reporting the local user's presence.\n */\n renderCursor?: RenderCursorFunction | null\n}\n\n/**\n * What {@link splitEditableProps} pulls apart.\n *\n * @internal\n */\nexport interface SplitEditableProps {\n handle: DocumentHandle\n path: string\n renderCursor: RenderCursorFunction | null | undefined\n rangeDecorations: RangeDecoration[] | undefined\n editableProps: Omit<\n PortableTextEditableProps,\n keyof DocumentHandle | 'rangeDecorations'\n >\n}\n\n/**\n * A `PortableTextEditable` wired to a Sanity document: the field's value syncs\n * both ways, the local user's caret is reported, and other people's carets are\n * drawn.\n *\n * Place it inside `EditorProvider` in place of `PortableTextEditable`. Every\n * other prop is forwarded untouched, and `rangeDecorations` you pass are kept\n * and merged with the presence carets rather than replaced.\n *\n * Nothing else is needed: this replaces a separate `SDKValuePlugin`.\n *\n * @example\n * ```tsx\n * <EditorProvider initialConfig={{schemaDefinition}}>\n * <SDKPortableTextEditable\n * {...documentHandle}\n * path=\"content\"\n * renderCursor={({user}) => (props) => (\n * <Caret user={user}>{props.children}</Caret>\n * )}\n * />\n * </EditorProvider>\n * ```\n *\n * @public\n */\nexport function SDKPortableTextEditable(props: SDKPortableTextEditableProps) {\n const {handle, path, renderCursor, rangeDecorations, editableProps} =\n splitEditableProps(props)\n\n const renderer = resolveCursorRenderer(renderCursor)\n\n const cursors = useSDKPresenceCursors({\n ...handle,\n path,\n renderCursor: renderer.renderCursor,\n })\n\n const decorations = useMemo(\n () =>\n mergePresenceDecorations(rangeDecorations, cursors, renderer.drawCursors),\n [cursors, rangeDecorations, renderer.drawCursors],\n )\n\n return (\n <>\n <PortableTextEditable {...editableProps} rangeDecorations={decorations} />\n <SDKValuePlugin {...handle} path={path} />\n <SDKPresencePlugin {...handle} path={path} />\n </>\n )\n}\n\n/**\n * Splits the props into the document handle, this component's own props, and\n * what is forwarded to `PortableTextEditable`. Keeping handle fields out of the\n * forwarded set is what stops them reaching the DOM as attributes.\n *\n * @internal\n */\nexport function splitEditableProps(\n props: SDKPortableTextEditableProps,\n): SplitEditableProps {\n const normalizedProps = normalizeDocumentHandle(props)\n const {\n documentId,\n documentType,\n projectId,\n dataset,\n resource,\n resourceName,\n liveEdit,\n perspective,\n path,\n renderCursor,\n rangeDecorations,\n ...editableProps\n } = normalizedProps\n\n return {\n // Only the fields the caller actually passed. The SDK resolves an ambient\n // perspective and resource from context with `Object.hasOwn`, so forwarding\n // `perspective: undefined` would override what `ResourceProvider` set rather\n // than defer to it, and the field would sync and report against the draft\n // instead of the release the app is showing.\n handle: {\n documentId,\n documentType,\n ...('projectId' in props && {projectId}),\n ...('dataset' in props && {dataset}),\n ...('resource' in normalizedProps && {resource}),\n ...('resourceName' in props && {resourceName}),\n ...('liveEdit' in props && {liveEdit}),\n ...('perspective' in props && {perspective}),\n },\n path,\n renderCursor,\n rangeDecorations,\n editableProps,\n }\n}\n\n/**\n * Decides which caret component to draw with, and whether to draw at all.\n *\n * Presence is subscribed to either way, because hooks cannot be called\n * conditionally, so switching carets off discards the decorations rather than\n * skipping the work.\n *\n * @internal\n */\nexport function resolveCursorRenderer(\n renderCursor: RenderCursorFunction | null | undefined,\n): {renderCursor: RenderCursorFunction; drawCursors: boolean} {\n return {\n renderCursor: renderCursor ?? renderDefaultCursor,\n drawCursors: renderCursor !== null,\n }\n}\n\n/**\n * Appends the presence carets to whatever decorations the caller passed, so\n * theirs survive. The Studio merges the same way. When carets are switched off\n * the caller's own decorations pass straight through.\n *\n * @internal\n */\nexport function mergePresenceDecorations(\n rangeDecorations: RangeDecoration[] | undefined,\n cursors: RangeDecoration[],\n drawCursors: boolean,\n): RangeDecoration[] | undefined {\n if (!drawCursors) {\n return rangeDecorations\n }\n return [...(rangeDecorations ?? []), ...cursors]\n}\n\n/**\n * Splitting the props by hand is what keeps document handle fields off the DOM,\n * so every field has to be listed above. `sdk-editable.test.ts` fails to compile\n * if `DocumentHandle` gains one. Note that `@sanity/sdk-react` adds fields to the\n * core handle, so it has to be read from there.\n */\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-sdk-value",
3
- "version": "8.0.5",
3
+ "version": "8.0.6",
4
4
  "description": "Connect a Portable Text Editor with a Sanity document using the SDK",
5
5
  "keywords": [
6
6
  "@sanity/sdk",
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@sanity/diff-match-patch": "^3.2.0",
45
45
  "@sanity/pkg-utils": "^12.3.0",
46
- "@sanity/sdk-react": "^2.19.0",
46
+ "@sanity/sdk-react": "^3.0.0",
47
47
  "@sanity/tsconfig": "^2.1.0",
48
48
  "@types/debug": "^4.1.13",
49
49
  "@types/react": "^19.2.17",
@@ -59,12 +59,12 @@
59
59
  "vite": "^8.2.0",
60
60
  "vitest": "^4.1.11",
61
61
  "vitest-browser-react": "^2.2.0",
62
- "@portabletext/editor": "^8.1.1",
63
62
  "@portabletext/schema": "^3.0.0",
64
- "@portabletext/test": "^2.0.0"
63
+ "@portabletext/editor": "^8.1.1",
64
+ "@portabletext/test": "^2.1.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@sanity/sdk-react": "^2.19.0",
67
+ "@sanity/sdk-react": "^2.19.0 || ^3",
68
68
  "react": "^19.2",
69
69
  "react-dom": "^19.2",
70
70
  "@portabletext/editor": "^8.1.1"