diff --git a/dev/test-studio/preview/SimpleBlockPortableText.tsx b/dev/test-studio/preview/SimpleBlockPortableText.tsx
index 4cd27549f65..2bb43aea409 100644
--- a/dev/test-studio/preview/SimpleBlockPortableText.tsx
+++ b/dev/test-studio/preview/SimpleBlockPortableText.tsx
@@ -24,10 +24,11 @@ export function SimpleBlockPortableText(): React.JSX.Element {
{
_id: string
title: string | null
+ bodyString: string
body: PortableTextBlock[]
notes: {_key: string; title?: string; minutes?: number; notes?: PortableTextBlock[]}[]
}[]
- >(/* groq */ `*[_type == "simpleBlock"]{_id,title,body,notes}`)
+ >(/* groq */ `*[_type == "simpleBlock"]{_id,title,"bodyString":pt::text(body),body,notes}`)
if (error) {
throw error
@@ -52,6 +53,7 @@ export function SimpleBlockPortableText(): React.JSX.Element {
}}
>
{item.title || 'Untitled'}
+ {item.bodyString}
{item.notes?.map((note) => (
diff --git a/packages/sanity/package.json b/packages/sanity/package.json
index 7fccc22dc30..eb5663be129 100644
--- a/packages/sanity/package.json
+++ b/packages/sanity/package.json
@@ -156,6 +156,7 @@
"@portabletext/block-tools": "^1.1.2",
"@portabletext/editor": "^1.25.0",
"@portabletext/react": "^3.0.0",
+ "@portabletext/toolkit": "^2.0.16",
"@rexxars/react-json-inspector": "^9.0.1",
"@sanity/asset-utils": "^2.0.6",
"@sanity/bifur-client": "^0.4.1",
diff --git a/packages/sanity/src/presentation/loader/LiveQueries.tsx b/packages/sanity/src/presentation/loader/LiveQueries.tsx
index e94e872a0ae..60e1025442c 100644
--- a/packages/sanity/src/presentation/loader/LiveQueries.tsx
+++ b/packages/sanity/src/presentation/loader/LiveQueries.tsx
@@ -41,7 +41,7 @@ import {
type PresentationPerspective,
} from '../types'
import {type DocumentOnPage} from '../useDocumentsOnPage'
-import {useQueryParams, useRevalidate} from './utils'
+import {mapChangedValue, useQueryParams, useRevalidate} from './utils'
export interface LoaderQueriesProps {
liveDocument: Partial | null | undefined
@@ -197,8 +197,6 @@ export default function LoaderQueries(props: LoaderQueriesProps): React.JSX.Elem
if (comlink) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const {projectId, dataset} = clientConfig
- // @todo - Can this be migrated/deprecated in favour of emitting
- // `presentation/perspective` at a higher level?
comlink.post('loader/perspective', {
projectId: projectId!,
dataset: dataset!,
@@ -482,14 +480,7 @@ export function turboChargeResultIfSourceMap(
}
return null
},
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (changedValue: any, {previousValue}) => {
- if (typeof changedValue === 'number' && typeof previousValue === 'string') {
- // If the string() function was used in the query, we need to convert the source value to a string as well
- return `${changedValue}`
- }
- return changedValue
- },
+ mapChangedValue,
perspective,
)
}
diff --git a/packages/sanity/src/presentation/loader/LoaderQueries.tsx b/packages/sanity/src/presentation/loader/LoaderQueries.tsx
index 54a641f2859..b02de31950e 100644
--- a/packages/sanity/src/presentation/loader/LoaderQueries.tsx
+++ b/packages/sanity/src/presentation/loader/LoaderQueries.tsx
@@ -41,7 +41,7 @@ import {
type PresentationPerspective,
} from '../types'
import {type DocumentOnPage} from '../useDocumentsOnPage'
-import {useQueryParams, useRevalidate} from './utils'
+import {mapChangedValue, useQueryParams, useRevalidate} from './utils'
export interface LoaderQueriesProps {
liveDocument: Partial | null | undefined
@@ -174,8 +174,6 @@ export default function LoaderQueries(props: LoaderQueriesProps): React.JSX.Elem
if (comlink) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const {projectId, dataset} = clientConfig
- // @todo - Can this be migrated/deprecated in favour of emitting
- // `presentation/perspective` at a higher level?
comlink.post('loader/perspective', {
projectId: projectId!,
dataset: dataset!,
@@ -556,14 +554,7 @@ export function turboChargeResultIfSourceMap(
// Fallback to general documents cache
return cache.get(sourceDocument._id)
},
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (changedValue: any, {previousValue}) => {
- if (typeof changedValue === 'number' && typeof previousValue === 'string') {
- // If the string() function was used in the query, we need to convert the source value to a string as well
- return `${changedValue}`
- }
- return changedValue
- },
+ mapChangedValue,
perspective,
)
}
diff --git a/packages/sanity/src/presentation/loader/utils.ts b/packages/sanity/src/presentation/loader/utils.ts
index 1092203b48d..6112d316332 100644
--- a/packages/sanity/src/presentation/loader/utils.ts
+++ b/packages/sanity/src/presentation/loader/utils.ts
@@ -1,5 +1,38 @@
+import {toPlainText} from '@portabletext/react'
+import {isPortableTextBlock} from '@portabletext/toolkit'
import {type ClientPerspective, type QueryParams} from '@sanity/client'
+import {type ApplySourceDocumentsUpdateFunction} from '@sanity/client/csm'
import {useCallback, useEffect, useMemo, useState, useSyncExternalStore} from 'react'
+import {type FIXME} from 'sanity'
+
+/**
+ * Used by `applySourceDocuments`
+ * @internal
+ */
+export const mapChangedValue: ApplySourceDocumentsUpdateFunction = (
+ changedValue: FIXME,
+ {previousValue},
+) => {
+ if (typeof previousValue === 'string') {
+ if (typeof changedValue === 'number') {
+ // If the string() function was used in the query, we need to convert the source value to a string as well
+ return `${changedValue}`
+ }
+ // If it's an array in the source, but a string in the query response, it could be pt::text
+ if (Array.isArray(changedValue)) {
+ if (changedValue.length === 0) {
+ // If it's empty assume it's PT and return an empty string
+ return ''
+ }
+ // If the array contains any valid block type, assume the GROQ initially used pt::text on it and do the same conversion
+ if (changedValue.some((node) => typeof node === 'object' && isPortableTextBlock(node))) {
+ return toPlainText(changedValue)
+ }
+ }
+ }
+
+ return changedValue
+}
/**
* @internal
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f5569de3c07..a1e1cd74f5d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1349,6 +1349,9 @@ importers:
'@portabletext/react':
specifier: ^3.0.0
version: 3.2.0(react@18.3.1)
+ '@portabletext/toolkit':
+ specifier: ^2.0.16
+ version: 2.0.16
'@rexxars/react-json-inspector':
specifier: ^9.0.1
version: 9.0.1(react@18.3.1)