{{actualType}}
", expected "{{expectedType}}
"',
@@ -271,6 +279,8 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
'Edit the document or select an older version in the timeline to see a list of changes appear in this panel.',
/** No Changes title in the Review Changes pane */
'changes.no-changes-title': 'There are no changes',
+ /* Label for the tooltip that shows when an action is not selectable*/
+ 'changes.not-selectable': 'It is not possible to select this event',
/** Portable Text diff: An annotation was added */
'changes.portable-text.annotation_added': 'Added annotation',
/** Portable Text diff: An annotation was changed */
@@ -314,6 +324,8 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
'changes.removed-label': 'Removed',
/** Title for the Review Changes pane */
'changes.title': 'History',
+ /**The title that will be shown in the badge inside the events when the item is a draft */
+ 'changes.versions.draft': 'Draft',
/** --- Common components --- */
/** Tooltip text for context menu buttons */
@@ -353,6 +365,11 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
/** Title for the default ordering/SortOrder if no orderings are provided and the title field is found */
'default-orderings.title': 'Sort by Title',
+ /** Label to show in the document footer indicating the creation date of the document */
+ 'document-status.created': 'Created {{date}}',
+
+ /** Label to show in the document status indicating the date of the status */
+ 'document-status.date': '{{date}}',
/** Label to show in the document footer indicating the last edited date of the document */
'document-status.edited': 'Edited {{date}}',
/** Label to show in the document footer indicating the document is not published*/
@@ -1095,6 +1112,8 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
* when there are templates/types available for creation
*/
'new-document.create-new-document-label': 'New document…',
+ /** Tooltip message for add document button when the selected perspective is for published or inactive release */
+ 'new-document.disabled-release.tooltip': 'You cannot add documents to this release',
/** Placeholder for the "filter" input within the new document menu */
'new-document.filter-placeholder': 'Search document types',
/** Loading indicator text within the new document menu */
@@ -1141,6 +1160,96 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
/* Relative time, just now */
'relative-time.just-now': 'just now',
+ /** Action message to add document to release */
+ 'release.action.add-to-release': 'Add to {{title}}',
+ /** Action message for when document is already in release */
+ 'release.action.already-in-release': 'Already in release {{title}}',
+ /** Action message for when you click to view all versions you can copy the current document to */
+ 'release.action.copy-to': 'Copy version to',
+ /** Action message for creating new releases */
+ 'release.action.create-new': 'New release',
+ /** Action message for when document is already in release */
+ 'release.action.discard-version': 'Discard version',
+ /** Description for toast when version discarding failed */
+ 'release.action.discard-version.failure': 'Failed to discard version',
+ /** Description for toast when version deletion is successfully discarded */
+ 'release.action.discard-version.success':
+ '{{title}} version was successfully discarded',
+ /** Action message for when a new release is created off an existing version, draft or published document */
+ 'release.action.new-release': 'New Release',
+ /** Error message for when a version is set to be unpublished */
+ 'release.action.unpublish-version.failure': 'Failed to set version to be unpublished on release',
+ /** Action message for when a version is set to be unpublished successfully */
+ 'release.action.unpublish-version.success':
+ 'Successfully set {{title}} to be unpublished on release',
+ /** Action message for when the view release is pressed */
+ 'release.action.view-release': 'View release',
+ /** Label for banner when release is scheduled */
+ 'release.banner.scheduled-for-publishing-on': 'Scheduled for publishing on {{date}}',
+ /** Label for Draft chip in document header */
+ 'release.chip.draft': 'Draft',
+ /** Label for Draft chip in global header */
+ 'release.chip.global.drafts': 'Drafts',
+ /** Label for Published chip in document header */
+ 'release.chip.published': 'Published',
+ /** Label for tooltip in chip with the created date */
+ 'release.chip.tooltip.created-date': 'Created {{date}}',
+ /** Label for tooltip in chip with the lasted edited date */
+ 'release.chip.tooltip.edited-date': 'Edited {{date}}',
+ /** Label for tooltip in chip when document is intended for a future release that hasn't been scheduled */
+ 'release.chip.tooltip.intended-for-date': 'Intended for {{date}}',
+ /** Label for tooltip in chip when there is no recent draft edits */
+ 'release.chip.tooltip.no-edits': 'No edits',
+ /** Label for tooltip in chip when document isn't published */
+ 'release.chip.tooltip.not-published': 'Not published',
+ /** Label for tooltip in chip with the published date */
+ 'release.chip.tooltip.published-date': 'Published {{date}}',
+ /** Label for tooltip in chip when document is in a release that has been scheduled */
+ 'release.chip.tooltip.scheduled-for-date': 'Scheduled for {{date}}',
+ /** Label for tooltip in scheduled chip without a known date */
+ 'release.chip.tooltip.unknown-date': 'Unknown date',
+ /** Label for tooltip on deleted release */
+ 'release.deleted-tooltip': 'This release has been deleted',
+ /** Title for creating releases dialog */
+ 'release.dialog.create.title': 'Create release',
+ /** Label for description in tooltip to explain release types */
+ 'release.dialog.tooltip.description':
+ 'This makes it possible to show whether documents are in conflict when working on multiple versions.',
+ /** Label for noting that a release time is not final */
+ 'release.dialog.tooltip.note':
+ 'NOTE: You may change the time of release and set an exact time for scheduled publishing later.',
+ /** Title for tooltip to explain release time */
+ 'release.dialog.tooltip.title': 'Approximate time of release',
+ /** The placeholder text when the release doesn't have a description */
+ 'release.form.placeholer-describe-release': 'Describe the release…',
+ /** Tooltip for button to hide release visibility */
+ 'release.layer.hide': 'Hide release',
+ /** Label for draft perspective in navbar */
+ 'release.navbar.drafts': 'Drafts',
+ /** Label for published releases in navbar */
+ 'release.navbar.published': 'Published',
+ /** Tooltip for releases navigation in navbar */
+ 'release.navbar.tooltip': 'Releases',
+ /** The placeholder text when the release doesn't have a title */
+ 'release.placeholder-untitled-release': 'Untitled release',
+ /**The toast title that will be shown when the user has a release perspective which is now archived */
+ 'release.toast.archived-release.title': "The '{{title}}' release was archived",
+ /**The toast title that will be shown when the user has a release perspective which is now deleted */
+ 'release.toast.not-found-release.title': "The '{{title}}' release could not be found",
+ /** Label for when a version of a document has already been added to the release */
+ 'release.tooltip.already-added': 'A version of this document has already been added',
+ /** Label for when a release is scheduled / scheduling and a user can't add a document version to it */
+ 'release.tooltip.locked':
+ 'This release has been scheduled. Unsechedule it to add more documents.',
+ /** Label for the release type 'as soon as possible' */
+ 'release.type.asap': 'ASAP',
+ /** Label for the release type 'at time', meaning it's a release with a scheduled date */
+ 'release.type.scheduled': 'At time',
+ /** Label for the release type 'undecided' */
+ 'release.type.undecided': 'Undecided',
+ /** Tooltip for the dropdown to show all versions of document */
+ 'release.version-list.tooltip': 'See all document versions',
+
/** Accessibility label to open search action when the search would go fullscreen (eg on narrower screens) */
'search.action-open-aria-label': 'Open search',
/** Action label for adding a search filter */
@@ -1609,6 +1718,12 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
/** Title for error when the timeline for the given document can't be loaded */
'timeline.error.load-document-changes-title':
'An error occurred whilst retrieving document changes.',
+ /** Description for error when the timeline for the given document can't be loaded */
+ 'timeline.error.load-document-changes-version-description':
+ 'Enable the events API through the Studio config to view document history.',
+ /** Title for error when the timeline for the given version document can't be loaded */
+ 'timeline.error.load-document-changes-version-title':
+ 'Version documents history is only available through the Events API.',
/** Error description for when the document doesn't have history */
'timeline.error.no-document-history-description':
'When changing the content of the document, the document versions will appear in this menu.',
@@ -1630,6 +1745,8 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
'timeline.list.aria-label': 'Document revisions',
/** Label for loading history */
'timeline.loading-history': 'Loading history…',
+ /* Label for when no previous since events are available*/
+ 'timeline.no-previous-events': 'No previous events',
/** Label shown in review changes timeline when a document has been created */
'timeline.operation.created': 'Created',
/** Label shown in review changes timeline when a document was initially created */
diff --git a/packages/sanity/src/core/index.ts b/packages/sanity/src/core/index.ts
index 945e7796488..08616edebe0 100644
--- a/packages/sanity/src/core/index.ts
+++ b/packages/sanity/src/core/index.ts
@@ -11,12 +11,44 @@ export * from './FIXME'
export * from './form'
export * from './hooks'
export * from './i18n'
+export {PerspectiveProvider} from './perspective/PerspectiveProvider'
+export {
+ type PerspectiveContextValue,
+ type PerspectiveStack,
+ type SelectedPerspective,
+} from './perspective/types'
+export {useExcludedPerspective} from './perspective/useExcludedPerspective'
+export {usePerspective} from './perspective/usePerspective'
+export {useSetPerspective} from './perspective/useSetPerspective'
export * from './presence'
export * from './preview'
+export {
+ AddedVersion,
+ DiscardVersionDialog,
+ formatRelativeLocalePublishDate,
+ getPublishDateFromRelease,
+ getReleaseIdFromReleaseDocumentId,
+ getReleaseTone,
+ isDraftPerspective,
+ isPublishedPerspective,
+ isReleaseDocument,
+ isReleaseScheduledOrScheduling,
+ LATEST,
+ type ReleaseDocument,
+ RELEASES_INTENT,
+ useActiveReleases,
+ useArchivedReleases,
+ useDocumentVersions,
+ useIsReleaseActive,
+ useReleasesIds,
+ useVersionOperations,
+ VersionChip,
+ VersionInlineBadge,
+} from './releases'
export * from './scheduledPublishing'
export * from './schema'
export type {SearchFactoryOptions, SearchOptions, SearchSort, SearchTerms} from './search'
-export {createSearch, getSearchableTypes} from './search'
+export {createSearch, getSearchableTypes, isPerspectiveRaw} from './search'
export * from './store'
export * from './studio'
export * from './studioClient'
diff --git a/packages/sanity/src/core/perspective/GlobalPerspectiveProvider.tsx b/packages/sanity/src/core/perspective/GlobalPerspectiveProvider.tsx
new file mode 100644
index 00000000000..d2efb0f2c7b
--- /dev/null
+++ b/packages/sanity/src/core/perspective/GlobalPerspectiveProvider.tsx
@@ -0,0 +1,102 @@
+import {type ReleaseId} from '@sanity/client'
+import {Text, useToast} from '@sanity/ui'
+import {type ReactNode, useEffect, useMemo} from 'react'
+import {useRouter} from 'sanity/router'
+
+import {useTranslation} from '../i18n/hooks/useTranslation'
+import {Translate} from '../i18n/Translate'
+import {useActiveReleases} from '../releases/store/useActiveReleases'
+import {useArchivedReleases} from '../releases/store/useArchivedReleases'
+import {LATEST} from '../releases/util/const'
+import {getReleaseIdFromReleaseDocumentId} from '../releases/util/getReleaseIdFromReleaseDocumentId'
+import {isPublishedPerspective} from '../releases/util/util'
+import {EMPTY_ARRAY} from '../util/empty'
+import {PerspectiveProvider} from './PerspectiveProvider'
+import {usePerspective} from './usePerspective'
+import {useSetPerspective} from './useSetPerspective'
+
+const ResetPerspectiveHandler = () => {
+ const toast = useToast()
+ const {t} = useTranslation()
+ const {data: releases, loading: releasesLoading} = useActiveReleases()
+ const {data: archivedReleases} = useArchivedReleases()
+ const {selectedPerspectiveName} = usePerspective()
+ const setPerspective = useSetPerspective()
+
+ useEffect(() => {
+ // clear the perspective param when it is not an active release
+ if (
+ releasesLoading ||
+ !selectedPerspectiveName ||
+ isPublishedPerspective(selectedPerspectiveName)
+ )
+ return
+ const isCurrentPerspectiveValid = releases.some(
+ (release) => getReleaseIdFromReleaseDocumentId(release._id) === selectedPerspectiveName,
+ )
+ if (!isCurrentPerspectiveValid) {
+ setPerspective(LATEST)
+ const archived = archivedReleases.find(
+ (r) => getReleaseIdFromReleaseDocumentId(r._id) === selectedPerspectiveName,
+ )
+
+ toast.push({
+ id: `bundle-deleted-toast-${selectedPerspectiveName}`,
+ status: 'warning',
+ title: (
+