@@ -3,9 +3,10 @@ import { UmbPropertyActionBase, type UmbPropertyActionArgs } from "@umbraco-cms/
33import { UMB_PROPERTY_CONTEXT } from "@umbraco-cms/backoffice/property" ;
44import { UMB_CONTENT_WORKSPACE_CONTEXT } from "@umbraco-cms/backoffice/content" ;
55import { UMB_BLOCK_WORKSPACE_CONTEXT } from "@umbraco-cms/backoffice/block" ;
6+ import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from "@umbraco-cms/backoffice/document" ;
67import { UMB_PROPERTY_STRUCTURE_WORKSPACE_CONTEXT } from "@umbraco-cms/backoffice/content-type" ;
78import { umbOpenModal } from "@umbraco-cms/backoffice/modal" ;
8- import { createEntityContextItem , resolveEntityAdapterByType , type UaiEntityAdapterApi } from "@umbraco-ai/core" ;
9+ import { createEntityContextItem , createElementContextItem , resolveEntityAdapterByType , type UaiEntityAdapterApi } from "@umbraco-ai/core" ;
910import { UAI_PROMPT_PREVIEW_MODAL , UAI_PROMPT_PREVIEW_SIDEBAR } from "./prompt-preview-modal.token.js" ;
1011import type { UaiPromptPropertyActionMeta , UaiPromptContextItem , UaiPromptPreviewModalData } from "./types.js" ;
1112
@@ -30,6 +31,8 @@ interface WorkspaceContextLike {
3031export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase < UaiPromptPropertyActionMeta > {
3132 #propertyContext?: typeof UMB_PROPERTY_CONTEXT . TYPE ;
3233 #workspaceContext?: WorkspaceContextLike ;
34+ #parentDocumentContext?: WorkspaceContextLike ;
35+ #isBlockWorkspace = false ;
3336 #contentTypeAlias?: string ;
3437 #init: Promise < unknown > ;
3538 #workspaceAdapter?: UaiEntityAdapterApi ;
@@ -49,6 +52,7 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
4952 this . consumeContext ( UMB_BLOCK_WORKSPACE_CONTEXT , ( ctx ) => {
5053 if ( ! this . #workspaceContext) {
5154 this . #workspaceContext = ctx ;
55+ this . #isBlockWorkspace = true ;
5256 // For blocks, get content type alias from the content element manager's structure
5357 if ( ctx ) {
5458 this . observe (
@@ -63,6 +67,13 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
6367 } ) ;
6468 } ) ;
6569
70+ // Observe the parent document context for blocks.
71+ // Uses passContextAliasMatches() to skip the block workspace alias match
72+ // and find the document workspace higher in the DOM tree.
73+ this . consumeContext ( UMB_DOCUMENT_WORKSPACE_CONTEXT , ( ctx ) => {
74+ this . #parentDocumentContext = ctx as unknown as WorkspaceContextLike ;
75+ } ) . passContextAliasMatches ( ) ;
76+
6677 this . #init = Promise . all ( [
6778 this . consumeContext ( UMB_PROPERTY_CONTEXT , ( context ) => {
6879 this . #propertyContext = context ;
@@ -100,10 +111,39 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
100111 throw new Error ( "Property action meta is not available" ) ;
101112 }
102113
103- // Resolve required entity context for prompt execution
104- const entityId = this . #workspaceContext. getUnique ( ) ;
105- const entityType = this . #workspaceContext. getEntityType ( ) ;
114+ // Resolve required context for prompt execution
106115 const propertyAlias = this . #propertyContext. getAlias ( ) ;
116+ if ( ! propertyAlias ) {
117+ throw new Error ( "Property alias is not available" ) ;
118+ }
119+
120+ // For blocks: entity = parent document, element = block
121+ // For documents: entity = document, no element
122+ let entityId : string | null | undefined ;
123+ let entityType : string ;
124+ let elementId : string | undefined ;
125+ let elementType : string | undefined ;
126+
127+ if ( this . #isBlockWorkspace) {
128+ try {
129+ elementId = this . #workspaceContext. getUnique ( ) ?? undefined ;
130+ } catch {
131+ // getUnique() can throw for blocks if contentKey is not yet available
132+ }
133+ elementType = this . #workspaceContext. getEntityType ( ) ;
134+
135+ if ( this . #parentDocumentContext) {
136+ entityId = this . #parentDocumentContext. getUnique ( ) ;
137+ entityType = this . #parentDocumentContext. getEntityType ( ) ;
138+ } else {
139+ // Fallback: if parent document couldn't be resolved, use block as entity
140+ entityId = elementId ;
141+ entityType = elementType ;
142+ }
143+ } else {
144+ entityId = this . #workspaceContext. getUnique ( ) ;
145+ entityType = this . #workspaceContext. getEntityType ( ) ;
146+ }
107147
108148 if ( ! entityId ) {
109149 throw new Error ( "Entity ID is not available" ) ;
@@ -113,11 +153,7 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
113153 throw new Error ( "Entity type is not available" ) ;
114154 }
115155
116- if ( ! propertyAlias ) {
117- throw new Error ( "Property alias is not available" ) ;
118- }
119-
120- // Serialize document context for AI operations
156+ // Serialize entity and element context for AI operations
121157 const context = await this . #serializeEntityContext( ) ;
122158
123159 // Get maxChars from property editor config (if available)
@@ -134,6 +170,8 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
134170 entityType,
135171 propertyAlias,
136172 contentTypeAlias : this . #contentTypeAlias ?? "" ,
173+ elementId,
174+ elementType,
137175 culture : this . #propertyContext. getVariantId ?.( ) ?. culture ?? undefined ,
138176 segment : this . #propertyContext. getVariantId ?.( ) ?. segment ?? undefined ,
139177 context,
@@ -187,8 +225,9 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
187225 }
188226
189227 /**
190- * Serialize the current entity for AI context injection.
191- * Resolves the appropriate adapter based on the workspace entity type.
228+ * Serialize the current entity (and element, if editing a block) for AI context injection.
229+ * For blocks: sends both the parent document (entity context) and the block (element context).
230+ * For documents: sends only the document (entity context).
192231 */
193232 async #serializeEntityContext( ) : Promise < UaiPromptContextItem [ ] | undefined > {
194233 if ( ! this . #workspaceContext) {
@@ -200,13 +239,33 @@ export class UaiPromptInsertPropertyAction extends UmbPropertyActionBase<UaiProm
200239 return undefined ;
201240 }
202241
242+ const contextItems : UaiPromptContextItem [ ] = [ ] ;
243+
203244 try {
204- const serializedEntity = await adapter . serializeForLlm ( this . #workspaceContext) ;
205- return [ createEntityContextItem ( serializedEntity ) ] ;
245+ if ( this . #isBlockWorkspace) {
246+ // Block: serialize block as element context
247+ const serializedElement = await adapter . serializeForLlm ( this . #workspaceContext) ;
248+ contextItems . push ( createElementContextItem ( serializedElement ) ) ;
249+
250+ // Serialize parent document as entity context
251+ if ( this . #parentDocumentContext) {
252+ const docAdapter = await resolveEntityAdapterByType ( "document" ) ;
253+ if ( docAdapter ?. canHandle ( this . #parentDocumentContext) ) {
254+ const serializedEntity = await docAdapter . serializeForLlm ( this . #parentDocumentContext) ;
255+ contextItems . push ( createEntityContextItem ( serializedEntity ) ) ;
256+ }
257+ }
258+ } else {
259+ // Document/media: serialize as entity context (as before)
260+ const serializedEntity = await adapter . serializeForLlm ( this . #workspaceContext) ;
261+ contextItems . push ( createEntityContextItem ( serializedEntity ) ) ;
262+ }
206263 } catch {
207264 // Serialization failed - continue without context
208265 return undefined ;
209266 }
267+
268+ return contextItems . length > 0 ? contextItems : undefined ;
210269 }
211270}
212271
0 commit comments