@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.
Files changed (43) hide show
  1. package/dist/_exports/dashboard.d.ts +250 -0
  2. package/dist/_exports/dashboard.d.ts.map +1 -0
  3. package/dist/_exports/dashboard.js +278 -0
  4. package/dist/_exports/dashboard.js.map +1 -0
  5. package/dist/index.d.ts +86 -261
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +114 -616
  8. package/dist/index.js.map +1 -1
  9. package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js +317 -0
  10. package/dist/useStudioWorkspacesByProjectIdDataset-DxUlukmF.js.map +1 -0
  11. package/package.json +12 -11
  12. package/src/_exports/dashboard.ts +12 -0
  13. package/src/_exports/sdk-react.ts +2 -12
  14. package/src/components/SDKProvider.test.tsx +5 -5
  15. package/src/components/auth/AuthBoundary.tsx +5 -5
  16. package/src/context/{WorkbenchTokenRefresh.test.tsx → DashboardTokenRefresh.test.tsx} +26 -26
  17. package/src/context/DashboardTokenRefresh.tsx +95 -0
  18. package/src/context/OrganizationResourcesProvider.test.tsx +9 -9
  19. package/src/context/OrganizationResourcesProvider.tsx +2 -2
  20. package/src/context/dashboardToken.ts +63 -0
  21. package/src/hooks/comlink/useWindowConnection.ts +1 -1
  22. package/src/hooks/{agent → dashboard}/useAgentResourceContext.ts +1 -1
  23. package/src/hooks/dashboard/useFavorite.test.tsx +101 -0
  24. package/src/hooks/dashboard/useFavorite.ts +34 -0
  25. package/src/hooks/dashboard/useFavoriteContext.ts +61 -0
  26. package/src/hooks/dashboard/{useDashboardNavigate.test.ts → useNavigate.test.ts} +3 -3
  27. package/src/hooks/dashboard/{useDashboardNavigate.ts → useNavigate.ts} +5 -5
  28. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -1
  29. package/src/hooks/{auth/useDashboardOrganizationId.test.tsx → dashboard/useOrganizationId.test.tsx} +4 -4
  30. package/src/hooks/{auth/useDashboardOrganizationId.tsx → dashboard/useOrganizationId.tsx} +2 -2
  31. package/src/hooks/dashboard/useUpdateFavorite.test.tsx +146 -0
  32. package/src/hooks/dashboard/useUpdateFavorite.ts +74 -0
  33. package/src/hooks/dashboard/useWindowTitle.ts +1 -1
  34. package/src/hooks/datasets/useDatasets.test.tsx +29 -22
  35. package/src/hooks/datasets/useDatasets.ts +31 -53
  36. package/src/hooks/document/useCreateDocument.ts +2 -1
  37. package/src/utils/resolveOrgResources.test.ts +2 -2
  38. package/src/utils/resolveOrgResources.ts +2 -2
  39. package/src/context/WorkbenchTokenRefresh.tsx +0 -61
  40. package/src/context/workbenchToken.ts +0 -63
  41. package/src/hooks/dashboard/useManageFavorite.test.tsx +0 -379
  42. package/src/hooks/dashboard/useManageFavorite.ts +0 -173
  43. /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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"}