Skip to content

Commit 9eac33d

Browse files
committed
feat(ai): Implement Neural Link tool get_computed_styles (#8281)
1 parent 749234d commit 9eac33d

5 files changed

Lines changed: 118 additions & 0 deletions

File tree

ai/mcp/server/neural-link/openapi.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,46 @@ paths:
7272
schema:
7373
$ref: '#/components/schemas/ErrorResponse'
7474

75+
/component/styles/get:
76+
post:
77+
summary: Get Computed Styles
78+
operationId: get_computed_styles
79+
x-pass-as-object: true
80+
description: |
81+
Retrieves the computed styles for a component.
82+
83+
**When to Use:**
84+
To verify visual state and theming (The "Visual Gap").
85+
tags: [Component]
86+
requestBody:
87+
required: true
88+
content:
89+
application/json:
90+
schema:
91+
$ref: '#/components/schemas/GetComputedStylesRequest'
92+
responses:
93+
'200':
94+
description: Computed styles
95+
content:
96+
application/json:
97+
schema:
98+
type: object
99+
properties:
100+
styles:
101+
type: object
102+
'400':
103+
description: Invalid request body
104+
content:
105+
application/json:
106+
schema:
107+
$ref: '#/components/schemas/ErrorResponse'
108+
'500':
109+
description: Internal server error
110+
content:
111+
application/json:
112+
schema:
113+
$ref: '#/components/schemas/ErrorResponse'
114+
75115
/component/rect/get:
76116
post:
77117
summary: Get Component DOM Rects
@@ -805,6 +845,24 @@ components:
805845
type: string
806846
description: The target App Worker Session ID
807847

848+
GetComputedStylesRequest:
849+
type: object
850+
required:
851+
- componentId
852+
- variables
853+
properties:
854+
componentId:
855+
type: string
856+
description: The component ID
857+
variables:
858+
type: array
859+
items:
860+
type: string
861+
description: The list of style properties/variables to retrieve
862+
sessionId:
863+
type: string
864+
description: The target App Worker Session ID
865+
808866
GetDomRectRequest:
809867
type: object
810868
required:

ai/mcp/server/neural-link/services/ComponentService.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ class ComponentService extends Base {
4848
return await ConnectionService.call(sessionId, 'get_dom_rect', {componentIds})
4949
}
5050

51+
/**
52+
* Retrieves the computed styles for a component.
53+
* @param {Object} opts The options object.
54+
* @param {String} opts.componentId The component ID.
55+
* @param {String[]} opts.variables The list of style properties/variables to retrieve.
56+
* @param {String} [opts.sessionId] The target session ID.
57+
* @returns {Promise<Object>} The computed styles.
58+
*/
59+
async getComputedStyles({componentId, variables, sessionId}) {
60+
return await ConnectionService.call(sessionId, 'get_computed_styles', {componentId, variables});
61+
}
62+
5163
/**
5264
* Retrieves the full component tree of the application.
5365
* @param {Object} opts The options object.

ai/mcp/server/neural-link/services/toolService.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const openApiFilePath = path.join(__dirname, '../openapi.yaml');
1515
const serviceMapping = {
1616
get_component_property: ComponentService .getComponentProperty.bind(ComponentService),
1717
get_component_tree : ComponentService .getComponentTree .bind(ComponentService),
18+
get_computed_styles : ComponentService .getComputedStyles .bind(ComponentService),
1819
get_dom_rect : ComponentService .getDomRect .bind(ComponentService),
1920
get_drag_state : InteractionService.getDragState .bind(InteractionService),
2021
get_record : DataService .getRecord .bind(DataService),

src/ai/client/ComponentService.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@ class ComponentService extends Service {
2626
return {value: this.safeSerialize(component[property])};
2727
}
2828

29+
/**
30+
* @param {Object} params
31+
* @param {String} params.componentId
32+
* @param {String[]} params.variables
33+
* @returns {Object}
34+
*/
35+
async getComputedStyles({componentId, variables}) {
36+
const component = Neo.getComponent(componentId);
37+
38+
if (!component) {
39+
throw new Error(`Component not found: ${componentId}`)
40+
}
41+
42+
const styles = await Neo.main.DomAccess.getComputedStyle({
43+
id : component.id,
44+
style: variables
45+
});
46+
47+
return {styles}
48+
}
49+
2950
/**
3051
* @param {Object} params
3152
* @param {String[]} params.componentIds

src/main/DomAccess.mjs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class DomAccess extends Base {
6969
'focus',
7070
'getAttributes',
7171
'getBoundingClientRect',
72+
'getComputedStyle',
7273
'getOffscreenCanvas',
7374
'getScrollingDimensions',
7475
'measure',
@@ -403,6 +404,31 @@ class DomAccess extends Base {
403404
return rect
404405
}
405406

407+
/**
408+
* @param {Object} data
409+
* @param {String} data.id
410+
* @param {String|String[]} data.style
411+
* @returns {Object}
412+
*/
413+
getComputedStyle({id, style}) {
414+
let node = this.getElement(id),
415+
styles = {};
416+
417+
if (node) {
418+
let computedStyle = window.getComputedStyle(node);
419+
420+
if (!Array.isArray(style)) {
421+
style = [style]
422+
}
423+
424+
style.forEach(prop => {
425+
styles[prop] = computedStyle.getPropertyValue(prop)
426+
})
427+
}
428+
429+
return styles
430+
}
431+
406432
/**
407433
* @param {String|HTMLElement} nodeId
408434
* @returns {HTMLElement|null}

0 commit comments

Comments
 (0)