@sanity/sdk-react 3.0.0-rc.2 → 3.1.0
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/_exports/dashboard.d.ts +250 -0
- package/dist/_exports/dashboard.d.ts.map +1 -0
- package/dist/_exports/dashboard.js +278 -0
- package/dist/_exports/dashboard.js.map +1 -0
- package/dist/index.d.ts +86 -261
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +114 -616
- package/dist/index.js.map +1 -1
- package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js +317 -0
- package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js.map +1 -0
- package/package.json +12 -11
- package/src/_exports/dashboard.ts +12 -0
- package/src/_exports/sdk-react.ts +2 -12
- package/src/components/SDKProvider.test.tsx +5 -5
- package/src/components/auth/AuthBoundary.tsx +5 -5
- package/src/context/{WorkbenchTokenRefresh.test.tsx → DashboardTokenRefresh.test.tsx} +26 -26
- package/src/context/DashboardTokenRefresh.tsx +95 -0
- package/src/context/OrganizationResourcesProvider.test.tsx +9 -9
- package/src/context/OrganizationResourcesProvider.tsx +2 -2
- package/src/context/dashboardToken.ts +63 -0
- package/src/hooks/comlink/useWindowConnection.ts +1 -1
- package/src/hooks/{agent → dashboard}/useAgentResourceContext.ts +1 -1
- package/src/hooks/dashboard/useFavorite.test.tsx +101 -0
- package/src/hooks/dashboard/useFavorite.ts +34 -0
- package/src/hooks/dashboard/useFavoriteContext.ts +61 -0
- package/src/hooks/dashboard/{useDashboardNavigate.test.ts → useNavigate.test.ts} +3 -3
- package/src/hooks/dashboard/{useDashboardNavigate.ts → useNavigate.ts} +5 -5
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -1
- package/src/hooks/{auth/useDashboardOrganizationId.test.tsx → dashboard/useOrganizationId.test.tsx} +4 -4
- package/src/hooks/{auth/useDashboardOrganizationId.tsx → dashboard/useOrganizationId.tsx} +2 -2
- package/src/hooks/dashboard/useUpdateFavorite.test.tsx +146 -0
- package/src/hooks/dashboard/useUpdateFavorite.ts +74 -0
- package/src/hooks/dashboard/useWindowTitle.ts +1 -1
- package/src/hooks/datasets/useDatasets.test.tsx +29 -22
- package/src/hooks/datasets/useDatasets.ts +31 -53
- package/src/hooks/document/useCreateDocument.ts +2 -1
- package/src/utils/resolveOrgResources.test.ts +2 -2
- package/src/utils/resolveOrgResources.ts +2 -2
- package/src/context/WorkbenchTokenRefresh.tsx +0 -61
- package/src/context/workbenchToken.ts +0 -63
- package/src/hooks/dashboard/useManageFavorite.test.tsx +0 -379
- package/src/hooks/dashboard/useManageFavorite.ts +0 -173
- /package/src/hooks/{agent → dashboard}/useAgentResourceContext.test.tsx +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { DocumentHandle } from "@sanity/sdk";
|
|
2
|
+
import React, { PropsWithChildren } from "react";
|
|
3
|
+
import { PathChangeMessage } from "@sanity/message-protocol";
|
|
4
|
+
/**
|
|
5
|
+
* Authenticates the SDK with the Sanity Dashboard's session when the app runs
|
|
6
|
+
* inside the dashboard.
|
|
7
|
+
*
|
|
8
|
+
* The dashboard owns the session there: this provider subscribes to the token
|
|
9
|
+
* the dashboard issues, writes each new value into the SDK's auth store (where
|
|
10
|
+
* SDK hooks read it from), and asks the dashboard for a fresh token when a
|
|
11
|
+
* request fails with a 401. Outside the dashboard it renders children
|
|
12
|
+
* unchanged and the app's normal auth flow applies.
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* `AuthBoundary` mounts this automatically, so most apps never need it
|
|
16
|
+
* directly. Mount it yourself only when your app runs inside the dashboard
|
|
17
|
+
* without `AuthBoundary` — that is, the app renders its own loading and error
|
|
18
|
+
* UI instead of the SDK's login flow — but still uses SDK hooks such as
|
|
19
|
+
* `useQuery`, which need the dashboard's token in the auth store to
|
|
20
|
+
* authenticate their requests.
|
|
21
|
+
*
|
|
22
|
+
* Mount it once, inside the provider that creates the Sanity instance whose
|
|
23
|
+
* store should receive the token.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* import {ResourceProvider} from '@sanity/sdk-react'
|
|
28
|
+
* import {TokenRefreshProvider} from '@sanity/sdk-react/dashboard'
|
|
29
|
+
*
|
|
30
|
+
* function EmbeddedApp() {
|
|
31
|
+
* return (
|
|
32
|
+
* <ResourceProvider fallback={<Loading />}>
|
|
33
|
+
* <TokenRefreshProvider>
|
|
34
|
+
* <App />
|
|
35
|
+
* </TokenRefreshProvider>
|
|
36
|
+
* </ResourceProvider>
|
|
37
|
+
* )
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
declare const DashboardTokenRefreshProvider: React.FC<PropsWithChildren>;
|
|
44
|
+
/**
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
interface AgentResourceContextOptions {
|
|
48
|
+
/**
|
|
49
|
+
* The project ID of the current context
|
|
50
|
+
*/
|
|
51
|
+
projectId: string;
|
|
52
|
+
/**
|
|
53
|
+
* The dataset of the current context
|
|
54
|
+
*/
|
|
55
|
+
dataset: string;
|
|
56
|
+
/**
|
|
57
|
+
* Optional document ID if the user is viewing/editing a specific document
|
|
58
|
+
*/
|
|
59
|
+
documentId?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @public
|
|
63
|
+
* Hook for emitting agent resource context updates to the Dashboard.
|
|
64
|
+
* This allows the Agent to understand what resource the user is currently
|
|
65
|
+
* interacting with (e.g., which document they're editing).
|
|
66
|
+
*
|
|
67
|
+
* The hook will automatically emit the context when it changes, and also
|
|
68
|
+
* emit the initial context when the hook is first mounted.
|
|
69
|
+
*
|
|
70
|
+
* @category Agent
|
|
71
|
+
* @param options - The resource context options containing projectId, dataset, and optional documentId
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* import {useAgentResourceContext} from '@sanity/sdk-react/dashboard'
|
|
76
|
+
*
|
|
77
|
+
* function MyComponent() {
|
|
78
|
+
* const documentId = 'my-document-id'
|
|
79
|
+
*
|
|
80
|
+
* // Automatically updates the Agent's context whenever the document changes
|
|
81
|
+
* useAgentResourceContext({
|
|
82
|
+
* projectId: 'my-project',
|
|
83
|
+
* dataset: 'production',
|
|
84
|
+
* documentId,
|
|
85
|
+
* })
|
|
86
|
+
*
|
|
87
|
+
* return <div>Editing document: {documentId}</div>
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function useAgentResourceContext(options: AgentResourceContextOptions): void;
|
|
92
|
+
/**
|
|
93
|
+
* @public
|
|
94
|
+
*
|
|
95
|
+
* A helper hook designed to be injected into routing components for apps within the Dashboard.
|
|
96
|
+
* While the Dashboard can usually handle navigation, there are special cases when you
|
|
97
|
+
* are already within a target app, and need to navigate to another route inside of that app.
|
|
98
|
+
*
|
|
99
|
+
* For example, your user might "favorite" a document inside of your application.
|
|
100
|
+
* If they click on the Dashboard favorites item in the sidebar, and are already within your application,
|
|
101
|
+
* there needs to be some way for the dashboard to signal to your application to reroute to where that document was favorited.
|
|
102
|
+
*
|
|
103
|
+
* This hook is intended to receive those messages, and takes a function to route to the correct path.
|
|
104
|
+
*
|
|
105
|
+
* @param navigateFn - Function to handle navigation; should accept:
|
|
106
|
+
* - `path`: a string, which will be a relative path (for example, 'my-route')
|
|
107
|
+
* - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* import {useNavigate} from '@sanity/sdk-react/dashboard'
|
|
112
|
+
* import {BrowserRouter, useNavigate as useRouterNavigate} from 'react-router'
|
|
113
|
+
* import {Suspense} from 'react'
|
|
114
|
+
*
|
|
115
|
+
* function DashboardNavigationHandler() {
|
|
116
|
+
* const navigate = useRouterNavigate()
|
|
117
|
+
* useNavigate(({path, type}) => {
|
|
118
|
+
* navigate(path, {replace: type === 'replace'})
|
|
119
|
+
* })
|
|
120
|
+
* return null
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* // Wrap the component with Suspense since the hook may suspend
|
|
124
|
+
* function MyApp() {
|
|
125
|
+
* return (
|
|
126
|
+
* <BrowserRouter>
|
|
127
|
+
* <Suspense>
|
|
128
|
+
* <DashboardNavigationHandler />
|
|
129
|
+
* </Suspense>
|
|
130
|
+
* </BrowserRouter>
|
|
131
|
+
* )
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
declare function useNavigate(navigateFn: (options: PathChangeMessage['data']) => void): void;
|
|
136
|
+
/**
|
|
137
|
+
* @public
|
|
138
|
+
* @category Types
|
|
139
|
+
*/
|
|
140
|
+
interface NavigateToStudioResult {
|
|
141
|
+
navigateToStudioDocument: () => void;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @public
|
|
145
|
+
*
|
|
146
|
+
* Hook that provides a function to navigate to a given document in its parent Studio.
|
|
147
|
+
*
|
|
148
|
+
* Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.
|
|
149
|
+
* This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.
|
|
150
|
+
*
|
|
151
|
+
* @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),
|
|
152
|
+
* it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.
|
|
153
|
+
*
|
|
154
|
+
* @category Documents
|
|
155
|
+
* @param documentHandle - The document handle for the document to navigate to
|
|
156
|
+
* @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple
|
|
157
|
+
* studios with the same projectId and dataset
|
|
158
|
+
* @returns An object containing:
|
|
159
|
+
* - `navigateToStudioDocument` - Function that when called will navigate to the studio document
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```tsx
|
|
163
|
+
* import {type DocumentHandle} from '@sanity/sdk-react'
|
|
164
|
+
* import {useNavigateToStudioDocument} from '@sanity/sdk-react/dashboard'
|
|
165
|
+
* import {Button} from '@sanity/ui'
|
|
166
|
+
* import {Suspense} from 'react'
|
|
167
|
+
*
|
|
168
|
+
* function NavigateButton({documentHandle}: {documentHandle: DocumentHandle}) {
|
|
169
|
+
* const {navigateToStudioDocument} = useNavigateToStudioDocument(documentHandle)
|
|
170
|
+
* return (
|
|
171
|
+
* <Button
|
|
172
|
+
* onClick={navigateToStudioDocument}
|
|
173
|
+
* text="Navigate to Studio Document"
|
|
174
|
+
* />
|
|
175
|
+
* )
|
|
176
|
+
* }
|
|
177
|
+
*
|
|
178
|
+
* // Wrap the component with Suspense since the hook may suspend
|
|
179
|
+
* function MyDocumentAction({documentHandle}: {documentHandle: DocumentHandle}) {
|
|
180
|
+
* return (
|
|
181
|
+
* <Suspense fallback={<Button text="Loading..." disabled />}>
|
|
182
|
+
* <NavigateButton documentHandle={documentHandle} />
|
|
183
|
+
* </Suspense>
|
|
184
|
+
* )
|
|
185
|
+
* }
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
declare function useNavigateToStudioDocument(documentHandle: DocumentHandle, preferredStudioUrl?: string): NavigateToStudioResult;
|
|
189
|
+
/**
|
|
190
|
+
* @public
|
|
191
|
+
*
|
|
192
|
+
* A React hook that retrieves the dashboard organization ID that is currently selected in the Sanity Dashboard.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```tsx
|
|
196
|
+
* function DashboardComponent() {
|
|
197
|
+
* const orgId = useOrganizationId()
|
|
198
|
+
*
|
|
199
|
+
* if (!orgId) return null
|
|
200
|
+
*
|
|
201
|
+
* return <div>Organization ID: {String(orgId)}</div>
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* @category Dashboard
|
|
206
|
+
* @returns The dashboard organization ID (string | undefined)
|
|
207
|
+
*/
|
|
208
|
+
declare function useOrganizationId(): string | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Sets the browser's document title, automatically including the app's name
|
|
211
|
+
* from the manifest.
|
|
212
|
+
*
|
|
213
|
+
* This follows the same convention as Sanity Studio workspaces, where the
|
|
214
|
+
* workspace name is always present in the title:
|
|
215
|
+
*
|
|
216
|
+
* - With a view title: `<viewTitle> | <appTitle>`
|
|
217
|
+
* - Without a view title: `<appTitle>`
|
|
218
|
+
*
|
|
219
|
+
* The Sanity dashboard appends `| Sanity` to produce the final browser tab title.
|
|
220
|
+
*
|
|
221
|
+
* @param viewTitle - An optional view-specific title to prepend to the app title.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```tsx
|
|
225
|
+
* import {useWindowTitle} from '@sanity/sdk-react/dashboard'
|
|
226
|
+
*
|
|
227
|
+
* function MoviesList() {
|
|
228
|
+
* useWindowTitle('Movies')
|
|
229
|
+
* return <div>...</div>
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* // Browser tab: "Movies | My App | Sanity"
|
|
233
|
+
* ```
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```tsx
|
|
237
|
+
* // Call without arguments to show just the app title
|
|
238
|
+
* function AppRoot() {
|
|
239
|
+
* useWindowTitle()
|
|
240
|
+
* return <Outlet />
|
|
241
|
+
* }
|
|
242
|
+
*
|
|
243
|
+
* // Browser tab: "My App | Sanity"
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
declare function useWindowTitle(viewTitle?: string): void;
|
|
249
|
+
export { type AgentResourceContextOptions, type NavigateToStudioResult, DashboardTokenRefreshProvider as TokenRefreshProvider, useAgentResourceContext, useNavigate, useNavigateToStudioDocument, useOrganizationId, useWindowTitle };
|
|
250
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","names":[],"sources":["../../src/context/DashboardTokenRefresh.tsx","../../src/hooks/dashboard/useAgentResourceContext.ts","../../src/hooks/dashboard/useNavigate.ts","../../src/hooks/dashboard/useNavigateToStudioDocument.ts","../../src/hooks/dashboard/useOrganizationId.tsx","../../src/hooks/dashboard/useWindowTitle.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwFa,+BAA+B,MAAM,GAAG;;;;UC/EpC;;;;EAIf;;;;EAIA;;;;EAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCc,wBAAwB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCPjC,YACd,aAAa,SAAS;;;;;UClCP;EACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDc,4BACd,gBAAgB,gBAChB,8BACC;;;;;;;;;;;;;;;;;;;;iBC1Ca;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC2CA,eAAe"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { a as useWindowConnection, n as useOrganizationId, r as DashboardTokenRefreshProvider, t as useStudioWorkspacesByProjectIdDataset } from "../useStudioWorkspacesByProjectIdDataset-DxUlukmF.js";
|
|
2
|
+
import { c } from "react-compiler-runtime";
|
|
3
|
+
import "@sanity/sdk";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
|
|
6
|
+
import "@sanity/sdk/comlink";
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
* Hook for emitting agent resource context updates to the Dashboard.
|
|
10
|
+
* This allows the Agent to understand what resource the user is currently
|
|
11
|
+
* interacting with (e.g., which document they're editing).
|
|
12
|
+
*
|
|
13
|
+
* The hook will automatically emit the context when it changes, and also
|
|
14
|
+
* emit the initial context when the hook is first mounted.
|
|
15
|
+
*
|
|
16
|
+
* @category Agent
|
|
17
|
+
* @param options - The resource context options containing projectId, dataset, and optional documentId
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import {useAgentResourceContext} from '@sanity/sdk-react/dashboard'
|
|
22
|
+
*
|
|
23
|
+
* function MyComponent() {
|
|
24
|
+
* const documentId = 'my-document-id'
|
|
25
|
+
*
|
|
26
|
+
* // Automatically updates the Agent's context whenever the document changes
|
|
27
|
+
* useAgentResourceContext({
|
|
28
|
+
* projectId: 'my-project',
|
|
29
|
+
* dataset: 'production',
|
|
30
|
+
* documentId,
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* return <div>Editing document: {documentId}</div>
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function useAgentResourceContext(options) {
|
|
38
|
+
let $ = c(9), { projectId, dataset, documentId } = options, t0;
|
|
39
|
+
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {
|
|
40
|
+
name: SDK_NODE_NAME,
|
|
41
|
+
connectTo: SDK_CHANNEL_NAME
|
|
42
|
+
}, $[0] = t0) : t0 = $[0];
|
|
43
|
+
let { sendMessage } = useWindowConnection(t0), lastContextRef = useRef(null), t1;
|
|
44
|
+
$[1] !== dataset || $[2] !== documentId || $[3] !== projectId || $[4] !== sendMessage ? (t1 = () => {
|
|
45
|
+
if (!projectId || !dataset) {
|
|
46
|
+
console.warn("[useAgentResourceContext] projectId and dataset are required", {
|
|
47
|
+
projectId,
|
|
48
|
+
dataset
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let contextKey = `${projectId}:${dataset}:${documentId || ""}`;
|
|
53
|
+
if (lastContextRef.current !== contextKey) try {
|
|
54
|
+
let message = {
|
|
55
|
+
type: "dashboard/v1/events/agent/resource/update",
|
|
56
|
+
data: {
|
|
57
|
+
projectId,
|
|
58
|
+
dataset,
|
|
59
|
+
documentId
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
sendMessage(message.type, message.data), lastContextRef.current = contextKey;
|
|
63
|
+
} catch (t2) {
|
|
64
|
+
console.error("[useAgentResourceContext] Failed to update context:", t2);
|
|
65
|
+
}
|
|
66
|
+
}, $[1] = dataset, $[2] = documentId, $[3] = projectId, $[4] = sendMessage, $[5] = t1) : t1 = $[5];
|
|
67
|
+
let updateContext = t1, t2, t3;
|
|
68
|
+
$[6] === updateContext ? (t2 = $[7], t3 = $[8]) : (t2 = () => {
|
|
69
|
+
updateContext();
|
|
70
|
+
}, t3 = [updateContext], $[6] = updateContext, $[7] = t2, $[8] = t3), useEffect(t2, t3);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* @public
|
|
74
|
+
*
|
|
75
|
+
* A helper hook designed to be injected into routing components for apps within the Dashboard.
|
|
76
|
+
* While the Dashboard can usually handle navigation, there are special cases when you
|
|
77
|
+
* are already within a target app, and need to navigate to another route inside of that app.
|
|
78
|
+
*
|
|
79
|
+
* For example, your user might "favorite" a document inside of your application.
|
|
80
|
+
* If they click on the Dashboard favorites item in the sidebar, and are already within your application,
|
|
81
|
+
* there needs to be some way for the dashboard to signal to your application to reroute to where that document was favorited.
|
|
82
|
+
*
|
|
83
|
+
* This hook is intended to receive those messages, and takes a function to route to the correct path.
|
|
84
|
+
*
|
|
85
|
+
* @param navigateFn - Function to handle navigation; should accept:
|
|
86
|
+
* - `path`: a string, which will be a relative path (for example, 'my-route')
|
|
87
|
+
* - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* import {useNavigate} from '@sanity/sdk-react/dashboard'
|
|
92
|
+
* import {BrowserRouter, useNavigate as useRouterNavigate} from 'react-router'
|
|
93
|
+
* import {Suspense} from 'react'
|
|
94
|
+
*
|
|
95
|
+
* function DashboardNavigationHandler() {
|
|
96
|
+
* const navigate = useRouterNavigate()
|
|
97
|
+
* useNavigate(({path, type}) => {
|
|
98
|
+
* navigate(path, {replace: type === 'replace'})
|
|
99
|
+
* })
|
|
100
|
+
* return null
|
|
101
|
+
* }
|
|
102
|
+
*
|
|
103
|
+
* // Wrap the component with Suspense since the hook may suspend
|
|
104
|
+
* function MyApp() {
|
|
105
|
+
* return (
|
|
106
|
+
* <BrowserRouter>
|
|
107
|
+
* <Suspense>
|
|
108
|
+
* <DashboardNavigationHandler />
|
|
109
|
+
* </Suspense>
|
|
110
|
+
* </BrowserRouter>
|
|
111
|
+
* )
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
function useNavigate(navigateFn) {
|
|
116
|
+
let $ = c(2), t0;
|
|
117
|
+
$[0] === navigateFn ? t0 = $[1] : (t0 = {
|
|
118
|
+
name: SDK_NODE_NAME,
|
|
119
|
+
connectTo: SDK_CHANNEL_NAME,
|
|
120
|
+
onMessage: { "dashboard/v1/history/change-path": (data) => {
|
|
121
|
+
navigateFn(data);
|
|
122
|
+
} }
|
|
123
|
+
}, $[0] = navigateFn, $[1] = t0), useWindowConnection(t0);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @public
|
|
127
|
+
*
|
|
128
|
+
* Hook that provides a function to navigate to a given document in its parent Studio.
|
|
129
|
+
*
|
|
130
|
+
* Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.
|
|
131
|
+
* This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.
|
|
132
|
+
*
|
|
133
|
+
* @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),
|
|
134
|
+
* it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.
|
|
135
|
+
*
|
|
136
|
+
* @category Documents
|
|
137
|
+
* @param documentHandle - The document handle for the document to navigate to
|
|
138
|
+
* @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple
|
|
139
|
+
* studios with the same projectId and dataset
|
|
140
|
+
* @returns An object containing:
|
|
141
|
+
* - `navigateToStudioDocument` - Function that when called will navigate to the studio document
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```tsx
|
|
145
|
+
* import {type DocumentHandle} from '@sanity/sdk-react'
|
|
146
|
+
* import {useNavigateToStudioDocument} from '@sanity/sdk-react/dashboard'
|
|
147
|
+
* import {Button} from '@sanity/ui'
|
|
148
|
+
* import {Suspense} from 'react'
|
|
149
|
+
*
|
|
150
|
+
* function NavigateButton({documentHandle}: {documentHandle: DocumentHandle}) {
|
|
151
|
+
* const {navigateToStudioDocument} = useNavigateToStudioDocument(documentHandle)
|
|
152
|
+
* return (
|
|
153
|
+
* <Button
|
|
154
|
+
* onClick={navigateToStudioDocument}
|
|
155
|
+
* text="Navigate to Studio Document"
|
|
156
|
+
* />
|
|
157
|
+
* )
|
|
158
|
+
* }
|
|
159
|
+
*
|
|
160
|
+
* // Wrap the component with Suspense since the hook may suspend
|
|
161
|
+
* function MyDocumentAction({documentHandle}: {documentHandle: DocumentHandle}) {
|
|
162
|
+
* return (
|
|
163
|
+
* <Suspense fallback={<Button text="Loading..." disabled />}>
|
|
164
|
+
* <NavigateButton documentHandle={documentHandle} />
|
|
165
|
+
* </Suspense>
|
|
166
|
+
* )
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
function useNavigateToStudioDocument(documentHandle, preferredStudioUrl) {
|
|
171
|
+
let $ = c(8), { workspacesByProjectIdAndDataset } = useStudioWorkspacesByProjectIdDataset(), t0;
|
|
172
|
+
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = {
|
|
173
|
+
name: SDK_NODE_NAME,
|
|
174
|
+
connectTo: SDK_CHANNEL_NAME
|
|
175
|
+
}, $[0] = t0) : t0 = $[0];
|
|
176
|
+
let { sendMessage } = useWindowConnection(t0), t1;
|
|
177
|
+
$[1] !== documentHandle || $[2] !== preferredStudioUrl || $[3] !== sendMessage || $[4] !== workspacesByProjectIdAndDataset ? (t1 = () => {
|
|
178
|
+
let { projectId, dataset } = documentHandle;
|
|
179
|
+
if (!projectId || !dataset) {
|
|
180
|
+
console.warn("Project ID and dataset are required to navigate to a studio document");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let workspace;
|
|
184
|
+
if (preferredStudioUrl) workspace = [...workspacesByProjectIdAndDataset[`${projectId}:${dataset}`] || [], ...workspacesByProjectIdAndDataset["NO_PROJECT_ID:NO_DATASET"] || []].find((w) => w.url === preferredStudioUrl);
|
|
185
|
+
else {
|
|
186
|
+
let workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`];
|
|
187
|
+
workspaces?.length > 1 && (console.warn("Multiple workspaces found for document and no preferred studio url", documentHandle), console.warn("Using the first one", workspaces[0])), workspace = workspaces?.[0];
|
|
188
|
+
}
|
|
189
|
+
if (!workspace) {
|
|
190
|
+
console.warn(`No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ""}`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
let message = {
|
|
194
|
+
type: "dashboard/v1/bridge/navigate-to-resource",
|
|
195
|
+
data: {
|
|
196
|
+
resourceId: workspace.id,
|
|
197
|
+
resourceType: "studio",
|
|
198
|
+
path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
sendMessage(message.type, message.data);
|
|
202
|
+
}, $[1] = documentHandle, $[2] = preferredStudioUrl, $[3] = sendMessage, $[4] = workspacesByProjectIdAndDataset, $[5] = t1) : t1 = $[5];
|
|
203
|
+
let navigateToStudioDocument = t1, t2;
|
|
204
|
+
return $[6] === navigateToStudioDocument ? t2 = $[7] : (t2 = { navigateToStudioDocument }, $[6] = navigateToStudioDocument, $[7] = t2), t2;
|
|
205
|
+
}
|
|
206
|
+
function resolveAppTitle(resource) {
|
|
207
|
+
return resource.manifest?.title || resource.activeDeployment?.manifest?.title || resource.title;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Sets the browser's document title, automatically including the app's name
|
|
211
|
+
* from the manifest.
|
|
212
|
+
*
|
|
213
|
+
* This follows the same convention as Sanity Studio workspaces, where the
|
|
214
|
+
* workspace name is always present in the title:
|
|
215
|
+
*
|
|
216
|
+
* - With a view title: `<viewTitle> | <appTitle>`
|
|
217
|
+
* - Without a view title: `<appTitle>`
|
|
218
|
+
*
|
|
219
|
+
* The Sanity dashboard appends `| Sanity` to produce the final browser tab title.
|
|
220
|
+
*
|
|
221
|
+
* @param viewTitle - An optional view-specific title to prepend to the app title.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```tsx
|
|
225
|
+
* import {useWindowTitle} from '@sanity/sdk-react/dashboard'
|
|
226
|
+
*
|
|
227
|
+
* function MoviesList() {
|
|
228
|
+
* useWindowTitle('Movies')
|
|
229
|
+
* return <div>...</div>
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* // Browser tab: "Movies | My App | Sanity"
|
|
233
|
+
* ```
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```tsx
|
|
237
|
+
* // Call without arguments to show just the app title
|
|
238
|
+
* function AppRoot() {
|
|
239
|
+
* useWindowTitle()
|
|
240
|
+
* return <Outlet />
|
|
241
|
+
* }
|
|
242
|
+
*
|
|
243
|
+
* // Browser tab: "My App | Sanity"
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
function useWindowTitle(viewTitle) {
|
|
249
|
+
let [appTitle, setAppTitle] = useState(null), { fetch } = useWindowConnection({
|
|
250
|
+
name: SDK_NODE_NAME,
|
|
251
|
+
connectTo: SDK_CHANNEL_NAME
|
|
252
|
+
});
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (!fetch) return;
|
|
255
|
+
let controller = new AbortController();
|
|
256
|
+
async function fetchAppTitle(signal) {
|
|
257
|
+
try {
|
|
258
|
+
let title = resolveAppTitle((await fetch("dashboard/v1/context", void 0, { signal })).context.resource) || document.title;
|
|
259
|
+
title && setAppTitle(title);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
262
|
+
console.error("Failed to fetch app title from dashboard context:", err);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return fetchAppTitle(controller.signal), () => {
|
|
266
|
+
controller.abort();
|
|
267
|
+
};
|
|
268
|
+
}, [fetch]), useEffect(() => {
|
|
269
|
+
if (!appTitle) return;
|
|
270
|
+
let previous = document.title;
|
|
271
|
+
return document.title = viewTitle ? `${viewTitle} | ${appTitle}` : appTitle, () => {
|
|
272
|
+
document.title = previous;
|
|
273
|
+
};
|
|
274
|
+
}, [viewTitle, appTitle]);
|
|
275
|
+
}
|
|
276
|
+
export { DashboardTokenRefreshProvider as TokenRefreshProvider, useAgentResourceContext, useNavigate, useNavigateToStudioDocument, useOrganizationId, useWindowTitle };
|
|
277
|
+
|
|
278
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","names":["Events","SDK_CHANNEL_NAME","SDK_NODE_NAME","FrameMessage","useCallback","useEffect","useRef","useWindowConnection","AgentResourceContextOptions","projectId","dataset","documentId","useAgentResourceContext","options","$","_c","t0","Symbol","for","name","connectTo","sendMessage","lastContextRef","t1","console","warn","contextKey","current","message","type","data","t2","error","updateContext","t3","PathChangeMessage","SDK_CHANNEL_NAME","SDK_NODE_NAME","useWindowConnection","useNavigate","navigateFn","$","_c","t0","name","connectTo","onMessage","data","Bridge","SDK_CHANNEL_NAME","SDK_NODE_NAME","DocumentHandle","useCallback","useWindowConnection","DashboardResource","useStudioWorkspacesByProjectIdDataset","NavigateToStudioResult","navigateToStudioDocument","useNavigateToStudioDocument","documentHandle","preferredStudioUrl","$","_c","workspacesByProjectIdAndDataset","t0","Symbol","for","name","connectTo","sendMessage","t1","projectId","dataset","console","warn","workspace","allWorkspaces","find","w","url","workspaces","length","message","type","data","resourceId","id","resourceType","path","documentId","documentType","t2","SDK_CHANNEL_NAME","SDK_NODE_NAME","useEffect","useState","useWindowConnection","ContextResource","type","title","manifest","activeDeployment","ContextResponse","context","resource","resolveAppTitle","useWindowTitle","viewTitle","appTitle","setAppTitle","fetch","name","connectTo","controller","AbortController","fetchAppTitle","signal","AbortSignal","data","undefined","document","err","Error","console","error","abort","previous"],"sources":["../../src/hooks/dashboard/useAgentResourceContext.ts","../../src/hooks/dashboard/useNavigate.ts","../../src/hooks/dashboard/useNavigateToStudioDocument.ts","../../src/hooks/dashboard/useWindowTitle.ts"],"sourcesContent":["import {type Events, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {type FrameMessage} from '@sanity/sdk/comlink'\nimport {useCallback, useEffect, useRef} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\n/**\n * @public\n */\nexport interface AgentResourceContextOptions {\n /**\n * The project ID of the current context\n */\n projectId: string\n /**\n * The dataset of the current context\n */\n dataset: string\n /**\n * Optional document ID if the user is viewing/editing a specific document\n */\n documentId?: string\n}\n\n/**\n * @public\n * Hook for emitting agent resource context updates to the Dashboard.\n * This allows the Agent to understand what resource the user is currently\n * interacting with (e.g., which document they're editing).\n *\n * The hook will automatically emit the context when it changes, and also\n * emit the initial context when the hook is first mounted.\n *\n * @category Agent\n * @param options - The resource context options containing projectId, dataset, and optional documentId\n *\n * @example\n * ```tsx\n * import {useAgentResourceContext} from '@sanity/sdk-react/dashboard'\n *\n * function MyComponent() {\n * const documentId = 'my-document-id'\n *\n * // Automatically updates the Agent's context whenever the document changes\n * useAgentResourceContext({\n * projectId: 'my-project',\n * dataset: 'production',\n * documentId,\n * })\n *\n * return <div>Editing document: {documentId}</div>\n * }\n * ```\n */\nexport function useAgentResourceContext(options: AgentResourceContextOptions): void {\n const {projectId, dataset, documentId} = options\n const {sendMessage} = useWindowConnection<Events.AgentResourceUpdateMessage, FrameMessage>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n // Track the last sent context to avoid duplicate updates\n const lastContextRef = useRef<string | null>(null)\n\n const updateContext = useCallback(() => {\n // Validate required fields\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('[useAgentResourceContext] projectId and dataset are required', {\n projectId,\n dataset,\n })\n return\n }\n\n // Create a stable key for the current context\n const contextKey = `${projectId}:${dataset}:${documentId || ''}`\n\n // Skip if context hasn't changed\n if (lastContextRef.current === contextKey) {\n return\n }\n\n try {\n const message: Events.AgentResourceUpdateMessage = {\n type: 'dashboard/v1/events/agent/resource/update',\n data: {\n projectId,\n dataset,\n documentId,\n },\n }\n\n sendMessage(message.type, message.data)\n lastContextRef.current = contextKey\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[useAgentResourceContext] Failed to update context:', error)\n }\n }, [projectId, dataset, documentId, sendMessage])\n\n // Update context whenever it changes\n useEffect(() => {\n updateContext()\n }, [updateContext])\n}\n","import {type PathChangeMessage, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\n/**\n * @public\n *\n * A helper hook designed to be injected into routing components for apps within the Dashboard.\n * While the Dashboard can usually handle navigation, there are special cases when you\n * are already within a target app, and need to navigate to another route inside of that app.\n *\n * For example, your user might \"favorite\" a document inside of your application.\n * If they click on the Dashboard favorites item in the sidebar, and are already within your application,\n * there needs to be some way for the dashboard to signal to your application to reroute to where that document was favorited.\n *\n * This hook is intended to receive those messages, and takes a function to route to the correct path.\n *\n * @param navigateFn - Function to handle navigation; should accept:\n * - `path`: a string, which will be a relative path (for example, 'my-route')\n * - `type`: 'push', 'replace', or 'pop', which will be the type of navigation to perform\n *\n * @example\n * ```tsx\n * import {useNavigate} from '@sanity/sdk-react/dashboard'\n * import {BrowserRouter, useNavigate as useRouterNavigate} from 'react-router'\n * import {Suspense} from 'react'\n *\n * function DashboardNavigationHandler() {\n * const navigate = useRouterNavigate()\n * useNavigate(({path, type}) => {\n * navigate(path, {replace: type === 'replace'})\n * })\n * return null\n * }\n *\n * // Wrap the component with Suspense since the hook may suspend\n * function MyApp() {\n * return (\n * <BrowserRouter>\n * <Suspense>\n * <DashboardNavigationHandler />\n * </Suspense>\n * </BrowserRouter>\n * )\n * }\n * ```\n */\nexport function useNavigate(\n navigateFn: (options: PathChangeMessage['data']) => void,\n): void {\n useWindowConnection<PathChangeMessage, never>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n onMessage: {\n 'dashboard/v1/history/change-path': (data: PathChangeMessage['data']) => {\n navigateFn(data)\n },\n },\n })\n}\n","import {type Bridge, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {type DocumentHandle} from '@sanity/sdk'\nimport {useCallback} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\nimport {\n type DashboardResource,\n useStudioWorkspacesByProjectIdDataset,\n} from './useStudioWorkspacesByProjectIdDataset'\n\n/**\n * @public\n * @category Types\n */\nexport interface NavigateToStudioResult {\n navigateToStudioDocument: () => void\n}\n\n/**\n * @public\n *\n * Hook that provides a function to navigate to a given document in its parent Studio.\n *\n * Uses the `projectId` and `dataset` properties of the {@link DocumentHandle} you provide to resolve the correct Studio.\n * This will only work if you have deployed a studio with a workspace with this `projectId` / `dataset` combination.\n *\n * @remarks If you write your own Document Handle to pass to this hook (as opposed to a Document Handle generated by another hook),\n * it must include values for `documentId`, `documentType`, `projectId`, and `dataset`.\n *\n * @category Documents\n * @param documentHandle - The document handle for the document to navigate to\n * @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple\n * studios with the same projectId and dataset\n * @returns An object containing:\n * - `navigateToStudioDocument` - Function that when called will navigate to the studio document\n *\n * @example\n * ```tsx\n * import {type DocumentHandle} from '@sanity/sdk-react'\n * import {useNavigateToStudioDocument} from '@sanity/sdk-react/dashboard'\n * import {Button} from '@sanity/ui'\n * import {Suspense} from 'react'\n *\n * function NavigateButton({documentHandle}: {documentHandle: DocumentHandle}) {\n * const {navigateToStudioDocument} = useNavigateToStudioDocument(documentHandle)\n * return (\n * <Button\n * onClick={navigateToStudioDocument}\n * text=\"Navigate to Studio Document\"\n * />\n * )\n * }\n *\n * // Wrap the component with Suspense since the hook may suspend\n * function MyDocumentAction({documentHandle}: {documentHandle: DocumentHandle}) {\n * return (\n * <Suspense fallback={<Button text=\"Loading...\" disabled />}>\n * <NavigateButton documentHandle={documentHandle} />\n * </Suspense>\n * )\n * }\n * ```\n */\nexport function useNavigateToStudioDocument(\n documentHandle: DocumentHandle,\n preferredStudioUrl?: string,\n): NavigateToStudioResult {\n const {workspacesByProjectIdAndDataset} = useStudioWorkspacesByProjectIdDataset()\n const {sendMessage} = useWindowConnection<Bridge.Navigation.NavigateToResourceMessage, never>({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n const navigateToStudioDocument = useCallback(() => {\n const {projectId, dataset} = documentHandle\n\n if (!projectId || !dataset) {\n // eslint-disable-next-line no-console\n console.warn('Project ID and dataset are required to navigate to a studio document')\n return\n }\n\n let workspace: DashboardResource | undefined\n\n if (preferredStudioUrl) {\n // Get workspaces matching the projectId:dataset and any workspaces without projectId/dataset,\n // in case there hasn't been a manifest loaded yet\n const allWorkspaces = [\n ...(workspacesByProjectIdAndDataset[`${projectId}:${dataset}`] || []),\n ...(workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'] || []),\n ]\n workspace = allWorkspaces.find((w) => w.url === preferredStudioUrl)\n } else {\n const workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`]\n if (workspaces?.length > 1) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Multiple workspaces found for document and no preferred studio url',\n documentHandle,\n )\n // eslint-disable-next-line no-console\n console.warn('Using the first one', workspaces[0])\n }\n\n workspace = workspaces?.[0]\n }\n\n if (!workspace) {\n // eslint-disable-next-line no-console\n console.warn(\n `No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ''}`,\n )\n return\n }\n\n const message: Bridge.Navigation.NavigateToResourceMessage = {\n type: 'dashboard/v1/bridge/navigate-to-resource',\n data: {\n resourceId: workspace.id,\n resourceType: 'studio',\n path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`,\n },\n }\n\n sendMessage(message.type, message.data)\n }, [documentHandle, workspacesByProjectIdAndDataset, sendMessage, preferredStudioUrl])\n\n return {\n navigateToStudioDocument,\n }\n}\n","import {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'\nimport {useEffect, useState} from 'react'\n\nimport {useWindowConnection} from '../comlink/useWindowConnection'\n\ninterface ContextResource {\n type: string\n title?: string\n manifest?: {\n title?: string\n } | null\n activeDeployment?: {\n manifest?: {\n title?: string\n } | null\n } | null\n}\n\ninterface ContextResponse {\n context: {\n resource: ContextResource\n }\n}\n\nfunction resolveAppTitle(resource: ContextResource): string | undefined {\n return resource.manifest?.title || resource.activeDeployment?.manifest?.title || resource.title\n}\n\n/**\n * Sets the browser's document title, automatically including the app's name\n * from the manifest.\n *\n * This follows the same convention as Sanity Studio workspaces, where the\n * workspace name is always present in the title:\n *\n * - With a view title: `<viewTitle> | <appTitle>`\n * - Without a view title: `<appTitle>`\n *\n * The Sanity dashboard appends `| Sanity` to produce the final browser tab title.\n *\n * @param viewTitle - An optional view-specific title to prepend to the app title.\n *\n * @example\n * ```tsx\n * import {useWindowTitle} from '@sanity/sdk-react/dashboard'\n *\n * function MoviesList() {\n * useWindowTitle('Movies')\n * return <div>...</div>\n * }\n *\n * // Browser tab: \"Movies | My App | Sanity\"\n * ```\n *\n * @example\n * ```tsx\n * // Call without arguments to show just the app title\n * function AppRoot() {\n * useWindowTitle()\n * return <Outlet />\n * }\n *\n * // Browser tab: \"My App | Sanity\"\n * ```\n *\n * @public\n */\nexport function useWindowTitle(viewTitle?: string): void {\n const [appTitle, setAppTitle] = useState<string | null>(null)\n\n const {fetch} = useWindowConnection({\n name: SDK_NODE_NAME,\n connectTo: SDK_CHANNEL_NAME,\n })\n\n useEffect(() => {\n if (!fetch) return\n\n const controller = new AbortController()\n\n async function fetchAppTitle(signal: AbortSignal) {\n try {\n const data = await fetch<ContextResponse>('dashboard/v1/context', undefined, {signal})\n // Local development resources do not have a registered title or deployment manifest.\n // In that case, use the title rendered into the iframe HTML by the Sanity CLI.\n const title = resolveAppTitle(data.context.resource) || document.title\n if (title) {\n setAppTitle(title)\n }\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') return\n // eslint-disable-next-line no-console\n console.error('Failed to fetch app title from dashboard context:', err)\n }\n }\n\n fetchAppTitle(controller.signal)\n\n return () => {\n controller.abort()\n }\n }, [fetch])\n\n useEffect(() => {\n if (!appTitle) return\n\n const previous = document.title\n document.title = viewTitle ? `${viewTitle} | ${appTitle}` : appTitle\n\n return () => {\n document.title = previous\n }\n }, [viewTitle, appTitle])\n}\n"],"mappings":"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"}
|