@sanity/sdk-react 2.6.0 → 2.8.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.
@@ -39,11 +39,69 @@ interface Subscribable<T> {
39
39
  /**
40
40
  * @alpha
41
41
  * Generates content for a document (or specific fields) via Sanity Agent Actions.
42
+ *
43
+ * @remarks
44
+ * This hook provides a stable callback to trigger AI-powered content generation for documents.
45
+ *
46
+ * Features:
42
47
  * - Uses instruction templates with `$variables` and supports `instructionParams` (constants, fields, documents, GROQ queries).
43
48
  * - Can target specific paths/fields; supports image generation when targeting image fields.
44
49
  * - Supports optional `temperature`, `async`, `noWrite`, and `conditionalPaths`.
50
+ * - Returns a Subscribable stream for tracking generation progress.
51
+ *
52
+ * @returns A stable callback that triggers the action and yields a Subscribable stream.
53
+ *
54
+ * @example Basic content generation
55
+ * ```tsx
56
+ * import {useAgentGenerate} from '@sanity/sdk-react'
57
+ *
58
+ * function GenerateDescription({documentId}: {documentId: string}) {
59
+ * const generate = useAgentGenerate()
60
+ *
61
+ * const handleGenerate = () => {
62
+ * generate({
63
+ * documentId,
64
+ * instruction: 'Write a compelling product description based on the title: $title',
65
+ * instructionParams: {
66
+ * title: {type: 'field', path: 'title'},
67
+ * },
68
+ * targetPaths: ['description'],
69
+ * }).subscribe({
70
+ * next: (result) => console.log('Generation progress:', result),
71
+ * complete: () => console.log('Generation complete'),
72
+ * error: (err) => console.error('Generation failed:', err),
73
+ * })
74
+ * }
75
+ *
76
+ * return <button onClick={handleGenerate}>Generate Description</button>
77
+ * }
78
+ * ```
79
+ *
80
+ * @example Image generation
81
+ * ```tsx
82
+ * import {useAgentGenerate} from '@sanity/sdk-react'
83
+ *
84
+ * function GenerateProductImage({documentId}: {documentId: string}) {
85
+ * const generate = useAgentGenerate()
86
+ *
87
+ * const handleGenerateImage = () => {
88
+ * generate({
89
+ * documentId,
90
+ * instruction: 'Generate a product photo for: $productName',
91
+ * instructionParams: {
92
+ * productName: {type: 'field', path: 'name'},
93
+ * },
94
+ * targetPaths: ['mainImage'],
95
+ * }).subscribe({
96
+ * complete: () => console.log('Image generated'),
97
+ * })
98
+ * }
45
99
  *
46
- * Returns a stable callback that triggers the action and yields a Subscribable stream.
100
+ * return <button onClick={handleGenerateImage}>Generate Image</button>
101
+ * }
102
+ * ```
103
+ *
104
+ * @category Agent Actions
47
105
  */
48
106
  export const useAgentGenerate: () => (options: AgentGenerateOptions) => Subscribable<unknown> =
49
107
  createCallbackHook(agentGenerate) as unknown as () => (
@@ -53,12 +111,73 @@ export const useAgentGenerate: () => (options: AgentGenerateOptions) => Subscrib
53
111
  /**
54
112
  * @alpha
55
113
  * Transforms an existing document or selected fields using Sanity Agent Actions.
114
+ *
115
+ * @remarks
116
+ * This hook provides a stable callback to apply AI-powered transformations to document content.
117
+ *
118
+ * Features:
56
119
  * - Accepts `instruction` and `instructionParams` (constants, fields, documents, GROQ queries).
57
120
  * - Can write to the same or a different `targetDocument` (create/edit), and target specific paths.
58
121
  * - Supports per-path image transform instructions and image description operations.
59
122
  * - Optional `temperature`, `async`, `noWrite`, `conditionalPaths`.
60
123
  *
61
- * Returns a stable callback that triggers the action and yields a Subscribable stream.
124
+ * @returns A stable callback that triggers the action and yields a Subscribable stream.
125
+ *
126
+ * @example Transform text content
127
+ * ```tsx
128
+ * import {useAgentTransform} from '@sanity/sdk-react'
129
+ *
130
+ * function SummarizeArticle({documentId}: {documentId: string}) {
131
+ * const transform = useAgentTransform()
132
+ *
133
+ * const handleSummarize = () => {
134
+ * transform({
135
+ * documentId,
136
+ * instruction: 'Summarize the following content into 2-3 sentences: $body',
137
+ * instructionParams: {
138
+ * body: {type: 'field', path: 'body'},
139
+ * },
140
+ * targetPaths: ['summary'],
141
+ * }).subscribe({
142
+ * complete: () => console.log('Summary generated'),
143
+ * error: (err) => console.error('Transform failed:', err),
144
+ * })
145
+ * }
146
+ *
147
+ * return <button onClick={handleSummarize}>Generate Summary</button>
148
+ * }
149
+ * ```
150
+ *
151
+ * @example Transform and write to a different document
152
+ * ```tsx
153
+ * import {useAgentTransform} from '@sanity/sdk-react'
154
+ *
155
+ * function CreateVariant({sourceDocumentId}: {sourceDocumentId: string}) {
156
+ * const transform = useAgentTransform()
157
+ *
158
+ * const handleCreateVariant = () => {
159
+ * transform({
160
+ * documentId: sourceDocumentId,
161
+ * instruction: 'Rewrite this product description for a younger audience: $description',
162
+ * instructionParams: {
163
+ * description: {type: 'field', path: 'description'},
164
+ * },
165
+ * targetDocument: {
166
+ * operation: 'create',
167
+ * _type: 'product',
168
+ * },
169
+ * targetPaths: ['description'],
170
+ * }).subscribe({
171
+ * next: (result) => console.log('New document:', result),
172
+ * complete: () => console.log('Variant created'),
173
+ * })
174
+ * }
175
+ *
176
+ * return <button onClick={handleCreateVariant}>Create Youth Variant</button>
177
+ * }
178
+ * ```
179
+ *
180
+ * @category Agent Actions
62
181
  */
63
182
  export const useAgentTransform: () => (options: AgentTransformOptions) => Subscribable<unknown> =
64
183
  createCallbackHook(agentTransform) as unknown as () => (
@@ -68,11 +187,92 @@ export const useAgentTransform: () => (options: AgentTransformOptions) => Subscr
68
187
  /**
69
188
  * @alpha
70
189
  * Translates documents or fields using Sanity Agent Actions.
190
+ *
191
+ * @remarks
192
+ * This hook provides a stable callback to translate document content between languages using AI.
193
+ *
194
+ * Features:
71
195
  * - Configure `fromLanguage`/`toLanguage`, optional `styleGuide`, and `protectedPhrases`.
72
196
  * - Can write into a different `targetDocument`, and/or store language in a field.
73
197
  * - Optional `temperature`, `async`, `noWrite`, `conditionalPaths`.
74
198
  *
75
- * Returns a stable callback that triggers the action and yields a Subscribable stream.
199
+ * @returns A stable callback that triggers the action and yields a Subscribable stream.
200
+ *
201
+ * @example Basic translation
202
+ * ```tsx
203
+ * import {useAgentTranslate} from '@sanity/sdk-react'
204
+ *
205
+ * function TranslateArticle({documentId}: {documentId: string}) {
206
+ * const translate = useAgentTranslate()
207
+ *
208
+ * const handleTranslate = () => {
209
+ * translate({
210
+ * documentId,
211
+ * fromLanguage: 'en',
212
+ * toLanguage: 'es',
213
+ * targetPaths: ['title', 'body'],
214
+ * }).subscribe({
215
+ * complete: () => console.log('Translation complete'),
216
+ * error: (err) => console.error('Translation failed:', err),
217
+ * })
218
+ * }
219
+ *
220
+ * return <button onClick={handleTranslate}>Translate to Spanish</button>
221
+ * }
222
+ * ```
223
+ *
224
+ * @example Translation with style guide and protected phrases
225
+ * ```tsx
226
+ * import {useAgentTranslate} from '@sanity/sdk-react'
227
+ *
228
+ * function TranslateWithBrandTerms({documentId}: {documentId: string}) {
229
+ * const translate = useAgentTranslate()
230
+ *
231
+ * const handleTranslate = () => {
232
+ * translate({
233
+ * documentId,
234
+ * fromLanguage: 'en',
235
+ * toLanguage: 'fr',
236
+ * styleGuide: 'Use formal French appropriate for business communication.',
237
+ * protectedPhrases: ['Acme Corp', 'PowerWidget Pro'],
238
+ * targetPaths: ['title', 'description'],
239
+ * }).subscribe({
240
+ * complete: () => console.log('Translation complete'),
241
+ * })
242
+ * }
243
+ *
244
+ * return <button onClick={handleTranslate}>Translate to French</button>
245
+ * }
246
+ * ```
247
+ *
248
+ * @example Translate to a new document
249
+ * ```tsx
250
+ * import {useAgentTranslate} from '@sanity/sdk-react'
251
+ *
252
+ * function CreateTranslatedCopy({documentId}: {documentId: string}) {
253
+ * const translate = useAgentTranslate()
254
+ *
255
+ * const handleCreateTranslation = () => {
256
+ * translate({
257
+ * documentId,
258
+ * fromLanguage: 'en',
259
+ * toLanguage: 'de',
260
+ * targetDocument: {
261
+ * operation: 'create',
262
+ * _type: 'article',
263
+ * },
264
+ * languageFieldPath: 'language',
265
+ * }).subscribe({
266
+ * next: (result) => console.log('New translated document:', result),
267
+ * complete: () => console.log('Translated copy created'),
268
+ * })
269
+ * }
270
+ *
271
+ * return <button onClick={handleCreateTranslation}>Create German Copy</button>
272
+ * }
273
+ * ```
274
+ *
275
+ * @category Agent Actions
76
276
  */
77
277
  export const useAgentTranslate: () => (options: AgentTranslateOptions) => Subscribable<unknown> =
78
278
  createCallbackHook(agentTranslate) as unknown as () => (
@@ -80,12 +280,8 @@ export const useAgentTranslate: () => (options: AgentTranslateOptions) => Subscr
80
280
  ) => Subscribable<unknown>
81
281
 
82
282
  /**
83
- * @alpha
84
- * Prompts the LLM using the same instruction template format as other actions.
85
- * - `format`: 'string' or 'json' (instruction must contain the word "json" for JSON responses).
86
- * - Optional `temperature`.
87
- *
88
- * Returns a stable callback that triggers the action and resolves a Promise with the prompt result.
283
+ * @internal
284
+ * Adapter to convert the agentPrompt observable to a Promise.
89
285
  */
90
286
  function promptAdapter(
91
287
  instance: SanityInstance,
@@ -96,24 +292,104 @@ function promptAdapter(
96
292
 
97
293
  /**
98
294
  * @alpha
99
- * Prompts the LLM using the same instruction template format as other actions.
295
+ * Prompts the Content Agent using the same instruction template format as other agent actions.
296
+ *
297
+ * @remarks
298
+ * This hook provides a stable callback to send prompts to the Content Agent and receive responses.
299
+ * Unlike the other agent action hooks, this one does not modify documents—it simply
300
+ * returns the AI's response.
301
+ *
302
+ * Features:
303
+ * - Uses the same instruction template format with `$variables` as other actions.
100
304
  * - `format`: 'string' or 'json' (instruction must contain the word "json" for JSON responses).
101
- * - Optional `temperature`.
305
+ * - Supports `instructionParams` for dynamic content (constants, fields, documents, GROQ queries).
306
+ * - Optional `temperature` for controlling response creativity.
307
+ *
308
+ * @returns A stable callback that triggers the action and resolves a Promise with the prompt result.
309
+ *
310
+ * @example Basic string prompt
311
+ * ```tsx
312
+ * import {useState} from 'react'
313
+ * import {useAgentPrompt} from '@sanity/sdk-react'
314
+ *
315
+ * function AskQuestion() {
316
+ * const prompt = useAgentPrompt()
317
+ * const [answer, setAnswer] = useState<string>('')
318
+ *
319
+ * const handleAsk = async () => {
320
+ * const result = await prompt({
321
+ * instruction: 'What are the top 3 benefits of content modeling?',
322
+ * format: 'string',
323
+ * })
324
+ * setAnswer(result.output)
325
+ * }
326
+ *
327
+ * return (
328
+ * <div>
329
+ * <button onClick={handleAsk}>Ask AI</button>
330
+ * {answer && <p>{answer}</p>}
331
+ * </div>
332
+ * )
333
+ * }
334
+ * ```
335
+ *
336
+ * @example JSON response with instruction params
337
+ * ```tsx
338
+ * import {useState} from 'react'
339
+ * import {useAgentPrompt} from '@sanity/sdk-react'
340
+ *
341
+ * interface TagSuggestions {
342
+ * tags: string[]
343
+ * reasoning: string
344
+ * }
345
+ *
346
+ * function SuggestTags({documentId}: {documentId: string}) {
347
+ * const prompt = useAgentPrompt()
348
+ * const [suggestions, setSuggestions] = useState<TagSuggestions | null>(null)
102
349
  *
103
- * Returns a stable callback that triggers the action and resolves a Promise with the prompt result.
350
+ * const handleSuggest = async () => {
351
+ * const result = await prompt({
352
+ * instruction: `
353
+ * Based on the following article title and content, suggest relevant tags.
354
+ * Return as json with "tags" (array of strings) and "reasoning" (string).
355
+ * Title: $title
356
+ * Content: $body
357
+ * `,
358
+ * instructionParams: {
359
+ * title: {type: 'field', path: 'title', documentId},
360
+ * body: {type: 'field', path: 'body', documentId},
361
+ * },
362
+ * format: 'json',
363
+ * })
364
+ * setSuggestions(result.output as TagSuggestions)
365
+ * }
366
+ *
367
+ * return (
368
+ * <div>
369
+ * <button onClick={handleSuggest}>Suggest Tags</button>
370
+ * {suggestions && (
371
+ * <div>
372
+ * <p>Reasoning: {suggestions.reasoning}</p>
373
+ * <ul>
374
+ * {suggestions.tags.map((tag) => (
375
+ * <li key={tag}>{tag}</li>
376
+ * ))}
377
+ * </ul>
378
+ * </div>
379
+ * )}
380
+ * </div>
381
+ * )
382
+ * }
383
+ * ```
384
+ *
385
+ * @category Agent Actions
104
386
  */
105
387
  export const useAgentPrompt: () => (options: AgentPromptOptions) => Promise<AgentPromptResult> =
106
388
  createCallbackHook(promptAdapter)
107
389
 
108
390
  /**
109
- * @alpha
110
- * Schema-aware patching with Sanity Agent Actions.
111
- * - Validates provided paths/values against the document schema and merges object values safely.
112
- * - Prevents duplicate keys and supports array appends (including after a specific keyed item).
113
- * - Accepts `documentId` or `targetDocument` (mutually exclusive).
114
- * - Optional `async`, `noWrite`, `conditionalPaths`.
115
- *
116
- * Returns a stable callback that triggers the action and resolves a Promise with the patch result.
391
+ * @internal
392
+ * Adapter to convert the agentPatch observable to a Promise.
117
393
  */
118
394
  function patchAdapter(
119
395
  instance: SanityInstance,
@@ -125,12 +401,151 @@ function patchAdapter(
125
401
  /**
126
402
  * @alpha
127
403
  * Schema-aware patching with Sanity Agent Actions.
404
+ *
405
+ * @remarks
406
+ * This hook provides a stable callback to apply schema-validated patches to documents.
407
+ * Unlike {@link useEditDocument}, this uses the Agent Actions API which provides
408
+ * additional schema validation and safe merging capabilities.
409
+ *
410
+ * Features:
128
411
  * - Validates provided paths/values against the document schema and merges object values safely.
129
412
  * - Prevents duplicate keys and supports array appends (including after a specific keyed item).
130
413
  * - Accepts `documentId` or `targetDocument` (mutually exclusive).
414
+ * - Requires `schemaId` (e.g., `'_.schemas.default'`) and `target` to specify patch operations.
131
415
  * - Optional `async`, `noWrite`, `conditionalPaths`.
132
416
  *
133
- * Returns a stable callback that triggers the action and resolves a Promise with the patch result.
417
+ * Each entry in `target` specifies a `path`, an `operation` (`'set'`, `'append'`, `'mixed'`, or `'unset'`),
418
+ * and a `value` (required for all operations except `'unset'`).
419
+ *
420
+ * @returns A stable callback that triggers the action and resolves a Promise with the patch result.
421
+ *
422
+ * @example Basic field update
423
+ * ```tsx
424
+ * import {useAgentPatch} from '@sanity/sdk-react'
425
+ *
426
+ * function UpdateTitle({documentId}: {documentId: string}) {
427
+ * const patch = useAgentPatch()
428
+ *
429
+ * const handleUpdate = async () => {
430
+ * const result = await patch({
431
+ * documentId,
432
+ * schemaId: '_.schemas.default',
433
+ * target: [
434
+ * {
435
+ * path: 'title',
436
+ * operation: 'set',
437
+ * value: 'Updated Title',
438
+ * },
439
+ * {
440
+ * path: 'lastModified',
441
+ * operation: 'set',
442
+ * value: new Date().toISOString(),
443
+ * },
444
+ * ],
445
+ * })
446
+ * console.log('Patch result:', result)
447
+ * }
448
+ *
449
+ * return <button onClick={handleUpdate}>Update Title</button>
450
+ * }
451
+ * ```
452
+ *
453
+ * @example Append items to an array
454
+ * ```tsx
455
+ * import {useAgentPatch} from '@sanity/sdk-react'
456
+ *
457
+ * function AddTag({documentId}: {documentId: string}) {
458
+ * const patch = useAgentPatch()
459
+ *
460
+ * const handleAddTag = async (newTag: string) => {
461
+ * await patch({
462
+ * documentId,
463
+ * schemaId: '_.schemas.default',
464
+ * target: {
465
+ * path: 'tags',
466
+ * operation: 'append',
467
+ * value: [newTag],
468
+ * },
469
+ * })
470
+ * }
471
+ *
472
+ * return (
473
+ * <button onClick={() => handleAddTag('featured')}>
474
+ * Add Featured Tag
475
+ * </button>
476
+ * )
477
+ * }
478
+ * ```
479
+ *
480
+ * @example Insert array item after a specific key
481
+ * ```tsx
482
+ * import {useAgentPatch} from '@sanity/sdk-react'
483
+ *
484
+ * function InsertContentBlock({
485
+ * documentId,
486
+ * afterKey,
487
+ * }: {
488
+ * documentId: string
489
+ * afterKey: string
490
+ * }) {
491
+ * const patch = useAgentPatch()
492
+ *
493
+ * const handleInsert = async () => {
494
+ * await patch({
495
+ * documentId,
496
+ * schemaId: '_.schemas.default',
497
+ * target: {
498
+ * path: ['content', {_key: afterKey}],
499
+ * operation: 'append',
500
+ * value: [{_type: 'block', text: 'New paragraph inserted here.'}],
501
+ * },
502
+ * })
503
+ * }
504
+ *
505
+ * return <button onClick={handleInsert}>Insert Block</button>
506
+ * }
507
+ * ```
508
+ *
509
+ * @example Create a new document with targetDocument
510
+ * ```tsx
511
+ * import {useAgentPatch} from '@sanity/sdk-react'
512
+ *
513
+ * function CreateProduct() {
514
+ * const patch = useAgentPatch()
515
+ *
516
+ * const handleCreate = async () => {
517
+ * const result = await patch({
518
+ * targetDocument: {
519
+ * operation: 'create',
520
+ * _type: 'product',
521
+ * },
522
+ * schemaId: '_.schemas.default',
523
+ * target: [
524
+ * {
525
+ * path: 'title',
526
+ * operation: 'set',
527
+ * value: 'New Product',
528
+ * },
529
+ * {
530
+ * path: 'price',
531
+ * operation: 'set',
532
+ * value: 29.99,
533
+ * },
534
+ * {
535
+ * path: 'inStock',
536
+ * operation: 'set',
537
+ * value: true,
538
+ * },
539
+ * ],
540
+ * })
541
+ * console.log('Created document:', result.documentId)
542
+ * }
543
+ *
544
+ * return <button onClick={handleCreate}>Create Product</button>
545
+ * }
546
+ * ```
547
+ *
548
+ * @category Agent Actions
134
549
  */
135
550
  export const useAgentPatch: () => (options: AgentPatchOptions) => Promise<AgentPatchResult> =
136
551
  createCallbackHook(patchAdapter)
@@ -166,11 +166,11 @@ describe('useDispatchIntent', () => {
166
166
  })
167
167
 
168
168
  it('should send intent message with media library source', () => {
169
- const mockMediaLibraryHandle: DocumentHandle = {
169
+ const mockMediaLibraryHandle = {
170
170
  documentId: 'test-asset-id',
171
171
  documentType: 'sanity.asset',
172
172
  sourceName: 'media-library',
173
- }
173
+ } as const
174
174
 
175
175
  const {result} = renderHook(() =>
176
176
  useDispatchIntent({
@@ -195,11 +195,11 @@ describe('useDispatchIntent', () => {
195
195
  })
196
196
 
197
197
  it('should send intent message with canvas source', () => {
198
- const mockCanvasHandle: DocumentHandle = {
198
+ const mockCanvasHandle = {
199
199
  documentId: 'test-canvas-document-id',
200
200
  documentType: 'sanity.canvas.document',
201
201
  sourceName: 'canvas',
202
- }
202
+ } as const
203
203
 
204
204
  const {result} = renderHook(() =>
205
205
  useDispatchIntent({
@@ -238,7 +238,7 @@ describe('useDispatchIntent', () => {
238
238
  )
239
239
 
240
240
  expect(() => result.current.dispatchIntent()).toThrow(
241
- 'useDispatchIntent: Either `sourceName` or both `projectId` and `dataset` must be provided in documentHandle.',
241
+ 'useDispatchIntent: Unable to determine resource. Either `source`, `sourceName`, or both `projectId` and `dataset` must be provided in documentHandle.',
242
242
  )
243
243
  })
244
244
  })
@@ -3,6 +3,7 @@ import {type DocumentHandle, type FrameMessage} from '@sanity/sdk'
3
3
  import {useCallback} from 'react'
4
4
 
5
5
  import {useWindowConnection} from '../comlink/useWindowConnection'
6
+ import {type WithSourceNameSupport} from '../helpers/useNormalizedSourceOptions'
6
7
  import {useResourceIdFromDocumentHandle} from './utils/useResourceIdFromDocumentHandle'
7
8
 
8
9
  /**
@@ -41,7 +42,7 @@ interface DispatchIntent {
41
42
  interface UseDispatchIntentParams {
42
43
  action?: 'edit'
43
44
  intentId?: string
44
- documentHandle: DocumentHandle
45
+ documentHandle: WithSourceNameSupport<DocumentHandle>
45
46
  parameters?: Record<string, unknown>
46
47
  }
47
48
 
@@ -111,8 +112,6 @@ export function useDispatchIntent(params: UseDispatchIntentParams): DispatchInte
111
112
  throw new Error('useDispatchIntent: Either `action` or `intentId` must be provided.')
112
113
  }
113
114
 
114
- const {projectId, dataset, sourceName} = documentHandle
115
-
116
115
  if (action && intentId) {
117
116
  // eslint-disable-next-line no-console -- warn if both action and intentId are provided
118
117
  console.warn(
@@ -120,9 +119,10 @@ export function useDispatchIntent(params: UseDispatchIntentParams): DispatchInte
120
119
  )
121
120
  }
122
121
 
123
- if (!sourceName && (!projectId || !dataset)) {
122
+ // Validate that we have a resource ID (which is computed from source/sourceName or projectId+dataset)
123
+ if (!resource.id) {
124
124
  throw new Error(
125
- 'useDispatchIntent: Either `sourceName` or both `projectId` and `dataset` must be provided in documentHandle.',
125
+ 'useDispatchIntent: Unable to determine resource. Either `source`, `sourceName`, or both `projectId` and `dataset` must be provided in documentHandle.',
126
126
  )
127
127
  }
128
128
 
@@ -1,4 +1,3 @@
1
- import {type DocumentHandle} from '@sanity/sdk'
2
1
  import {describe, expect, it} from 'vitest'
3
2
 
4
3
  import {renderHook} from '../../../../test/test-utils'
@@ -25,11 +24,11 @@ describe('getResourceIdFromDocumentHandle', () => {
25
24
 
26
25
  describe('with DocumentHandleWithSource - media library', () => {
27
26
  it('should return media library ID and resourceType when media library source is provided', () => {
28
- const documentHandle: DocumentHandle = {
27
+ const documentHandle = {
29
28
  documentId: 'test-asset-id',
30
29
  documentType: 'sanity.asset',
31
30
  sourceName: 'media-library',
32
- }
31
+ } as const
33
32
 
34
33
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
35
34
 
@@ -5,7 +5,7 @@ import {
5
5
  isMediaLibrarySource,
6
6
  } from '@sanity/sdk'
7
7
 
8
- import {useSource} from '../../context/useSource'
8
+ import {useNormalizedSourceOptions} from '../../helpers/useNormalizedSourceOptions'
9
9
 
10
10
  interface DashboardMessageResource {
11
11
  id: string
@@ -18,8 +18,8 @@ interface DashboardMessageResource {
18
18
  export function useResourceIdFromDocumentHandle(
19
19
  documentHandle: DocumentHandle,
20
20
  ): DashboardMessageResource {
21
- const source = useSource(documentHandle)
22
- const {projectId, dataset} = documentHandle
21
+ const options = useNormalizedSourceOptions(documentHandle)
22
+ const {projectId, dataset, source} = options
23
23
  let resourceId: string = ''
24
24
  let resourceType: 'media-library' | 'canvas' | undefined
25
25
  if (projectId && dataset) {