Skip to content

Commit 4a33e3c

Browse files
OrKoNDevtools-frontend LUCI CQ
authored andcommitted
AI Assistance: dedicated getComputedStyles and getAuthoredStyles functions
Not enabled by default, pending evals. Fixed: 436140497, 436140498 Change-Id: Iff69afdb74849d88b1fc8931dde0efa9aee8b2f2 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6861973 Commit-Queue: Alex Rudenko <[email protected]> Reviewed-by: Ergün Erdoğmuş <[email protected]>
1 parent 3668543 commit 4a33e3c

File tree

1 file changed

+181
-1
lines changed

1 file changed

+181
-1
lines changed

front_end/models/ai_assistance/agents/StylingAgent.ts

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)