@@ -239,6 +239,10 @@ export class NodeContext extends ConversationContext<SDK.DOMModel.DOMNode> {
239239 }
240240}
241241
242+ type Relation = 'currentElement' | 'parentElement' ;
243+
244+ const enableDedicatedStyleFunctions = false ;
245+
242246/**
243247 * One agent instance handles one conversation. Create a new agent
244248 * instance for a new conversation.
@@ -326,6 +330,108 @@ export class StylingAgent extends AiAgent<SDK.DOMModel.DOMNode> {
326330 this ,
327331 ) ;
328332
333+ if ( enableDedicatedStyleFunctions ) {
334+ this . declareFunction < {
335+ relations : Relation [ ] ,
336+ properties : string [ ] ,
337+ thought : string ,
338+ } > ( 'getComputedStyles' , {
339+ description :
340+ 'Call this function to get the computed styles for the current or the parent element. Use executeJavaScript for more complex queries.' ,
341+ parameters : {
342+ type : Host . AidaClient . ParametersTypes . OBJECT ,
343+ description : '' ,
344+ nullable : false ,
345+ properties : {
346+ thought : {
347+ type : Host . AidaClient . ParametersTypes . STRING ,
348+ description : 'Explain why you want to get computed styles' ,
349+ } ,
350+ relations : {
351+ type : Host . AidaClient . ParametersTypes . ARRAY ,
352+ description : 'A list of relations describing which elements to query.' ,
353+ items : {
354+ type : Host . AidaClient . ParametersTypes . STRING ,
355+ description : 'Which element to query. Either \'currentElement\' or \'parentElement\'' ,
356+ }
357+ } ,
358+ properties : {
359+ type : Host . AidaClient . ParametersTypes . ARRAY ,
360+ description : 'One or more style property names to fetch' ,
361+ nullable : false ,
362+ items : {
363+ type : Host . AidaClient . ParametersTypes . STRING ,
364+ description : 'A computed style property name to retrieve. For example, \'background-color\'.'
365+ }
366+ } ,
367+ }
368+ } ,
369+ displayInfoFromArgs : params => {
370+ return {
371+ title : 'Reading computed styles' ,
372+ thought : params . thought ,
373+ action : `getComputedStyles(${ JSON . stringify ( params . relations ) } , ${ JSON . stringify ( params . properties ) } )` ,
374+ } ;
375+ } ,
376+ handler : async (
377+ params ,
378+ options ,
379+ ) => {
380+ return await this . getComputedStyles ( params . relations , params . properties , options ) ;
381+ } ,
382+ } ) ;
383+
384+ this . declareFunction < {
385+ relations : Relation [ ] ,
386+ properties : string [ ] ,
387+ thought : string ,
388+ } > ( 'getAuthoredStyles' , {
389+ description : 'Call this function to get the styles as specified by the page author.' ,
390+ parameters : {
391+ type : Host . AidaClient . ParametersTypes . OBJECT ,
392+ description : '' ,
393+ nullable : false ,
394+ properties : {
395+ thought : {
396+ type : Host . AidaClient . ParametersTypes . STRING ,
397+ description : 'Explain why you want to get computed styles' ,
398+ } ,
399+ relations : {
400+ type : Host . AidaClient . ParametersTypes . ARRAY ,
401+ description :
402+ 'A list of relations describing which elements to query. Possible values: \'currentElement\', \'parentElement\'' ,
403+ items : {
404+ type : Host . AidaClient . ParametersTypes . STRING ,
405+ description : 'Which element to query. Either \'currentElement\' or \'parentElement\'' ,
406+ }
407+ } ,
408+ properties : {
409+ type : Host . AidaClient . ParametersTypes . ARRAY ,
410+ description : 'One or more style property names to fetch' ,
411+ nullable : false ,
412+ items : {
413+ type : Host . AidaClient . ParametersTypes . STRING ,
414+ description : 'A computed style property name to retrieve. For example, \'background-color\'.'
415+ }
416+ } ,
417+ }
418+ } ,
419+ displayInfoFromArgs : params => {
420+ return {
421+ title : 'Reading authored styles' ,
422+ thought : params . thought ,
423+ action : `getAuthoredStyles(${ JSON . stringify ( params . relations ) } , ${ JSON . stringify ( params . properties ) } )` ,
424+ } ;
425+ } ,
426+ handler : async (
427+ params ,
428+ options ,
429+ ) => {
430+ return await this . getAuthoredStyles ( params . relations , params . properties , options ) ;
431+ } ,
432+ } ) ;
433+ }
434+
329435 this . declareFunction < {
330436 title : string ,
331437 thought : string ,
@@ -630,6 +736,80 @@ const data = {
630736 return output . trim ( ) ;
631737 }
632738
739+ #getSelectedNode( ) : SDK . DOMModel . DOMNode | null {
740+ return UI . Context . Context . instance ( ) . flavor ( SDK . DOMModel . DOMNode ) ;
741+ }
742+
743+ async getComputedStyles ( relations : Relation [ ] , properties : string [ ] , _options ?: {
744+ signal ?: AbortSignal ,
745+ approved ?: boolean ,
746+ } ) : Promise < FunctionCallHandlerResult < unknown > > {
747+ const result : Record < string , Record < string , string | undefined > | undefined > = { } ;
748+ for ( const relation of relations ) {
749+ result [ relation ] = { } ;
750+ debugLog ( `Action to execute: ${ relation } ` ) ;
751+ let selectedNode = this . #getSelectedNode( ) ;
752+ if ( ! selectedNode ) {
753+ return { error : 'Error: Could not find the currently selected element.' } ;
754+ }
755+ if ( relation === 'parentElement' ) {
756+ selectedNode = selectedNode . parentNode ;
757+ }
758+ if ( ! selectedNode ) {
759+ return { error : 'Error: Could not find the parent element.' } ;
760+ }
761+ const styles = await selectedNode . domModel ( ) . cssModel ( ) . getComputedStyle ( selectedNode . id ) ;
762+ if ( ! styles ) {
763+ return { error : 'Error: Could not get computed styles.' } ;
764+ }
765+ for ( const prop of properties ) {
766+ result [ relation ] [ prop ] = styles . get ( prop ) ;
767+ }
768+ }
769+ return {
770+ result : JSON . stringify ( result , null , 2 ) ,
771+ } ;
772+ }
773+
774+ async getAuthoredStyles ( relations : Relation [ ] , properties : string [ ] , _options ?: {
775+ signal ?: AbortSignal ,
776+ approved ?: boolean ,
777+ } ) : Promise < FunctionCallHandlerResult < unknown > > {
778+ const result : Record < string , Record < string , string | undefined > | undefined > = { } ;
779+ for ( const relation of relations ) {
780+ result [ relation ] = { } ;
781+ debugLog ( `Action to execute: ${ relation } ` ) ;
782+ let selectedNode = this . #getSelectedNode( ) ;
783+ if ( ! selectedNode ) {
784+ return { error : 'Error: Could not find the currently selected element.' } ;
785+ }
786+ if ( relation === 'parentElement' ) {
787+ selectedNode = selectedNode . parentNode ;
788+ }
789+ if ( ! selectedNode ) {
790+ return { error : 'Error: Could not find the parent element.' } ;
791+ }
792+ const matchedStyles = await selectedNode . domModel ( ) . cssModel ( ) . getMatchedStyles ( selectedNode . id ) ;
793+ if ( ! matchedStyles ) {
794+ return { error : 'Error: Could not get computed styles.' } ;
795+ }
796+ for ( const style of matchedStyles . nodeStyles ( ) ) {
797+ for ( const property of style . allProperties ( ) ) {
798+ if ( ! properties . includes ( property . name ) ) {
799+ continue ;
800+ }
801+ const state = matchedStyles . propertyState ( property ) ;
802+ if ( state === SDK . CSSMatchedStyles . PropertyState . ACTIVE ) {
803+ result [ relation ] [ property . name ] = property . value ;
804+ }
805+ }
806+ }
807+ }
808+ return {
809+ result : JSON . stringify ( result , null , 2 ) ,
810+ } ;
811+ }
812+
633813 async executeAction ( action : string , options ?: { signal ?: AbortSignal , approved ?: boolean } ) :
634814 Promise < FunctionCallHandlerResult < unknown > > {
635815 debugLog ( `Action to execute: ${ action } ` ) ;
@@ -646,7 +826,7 @@ const data = {
646826 } ;
647827 }
648828
649- const selectedNode = UI . Context . Context . instance ( ) . flavor ( SDK . DOMModel . DOMNode ) ;
829+ const selectedNode = this . #getSelectedNode ( ) ;
650830 const target = selectedNode ?. domModel ( ) . target ( ) ?? UI . Context . Context . instance ( ) . flavor ( SDK . Target . Target ) ;
651831 if ( target ?. model ( SDK . DebuggerModel . DebuggerModel ) ?. selectedCallFrame ( ) ) {
652832 return {
0 commit comments