mesurer-solid 0.1.0-beta.12 → 0.1.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.
- package/AGENT_INTEGRATION.md +305 -88
- package/README.md +203 -106
- package/dist/agent.d.ts +7 -5
- package/dist/context-plugin.d.ts +3 -10
- package/dist/context.d.ts +1 -29
- package/dist/core.d.ts +22 -15
- package/dist/core.js +20 -8
- package/dist/index.d.ts +10 -10
- package/dist/index.js +3014 -2514
- package/dist/inject-script.d.ts +6 -1
- package/dist/inject-script.js +12 -6
- package/dist/inject.d.ts +12 -0
- package/dist/inject.js +2968 -2462
- package/package.json +1 -1
- package/skills/mesurer-ui/SKILL.md +331 -44
package/AGENT_INTEGRATION.md
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
# Mesurer agent integration
|
|
2
2
|
|
|
3
|
-
Mesurer
|
|
3
|
+
Mesurer's agent integration is deliberately direct: **the agent reads and manipulates Mesurer through the same rendered page it is already controlling**.
|
|
4
|
+
|
|
5
|
+
There is no Mesurer MCP, WebMCP, ACP, localhost feedback daemon, Send-to-agent callback, chat/session bridge, or harness-specific Mesurer adapter.
|
|
6
|
+
|
|
7
|
+
The central contract is stronger than “Mesurer is available”:
|
|
8
|
+
|
|
9
|
+
> **A Mesurer visual operation should return structured context to the harness.** The agent should consume existing human context before editing and obtain fresh Mesurer context/review for the affected rendered UI before claiming completion.
|
|
4
10
|
|
|
5
11
|
```text
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
human selection / annotation OR agent-known changed target
|
|
13
|
+
↓
|
|
14
|
+
real page + window.__MESURER__
|
|
15
|
+
↓
|
|
16
|
+
context() / select() / review()
|
|
17
|
+
↓ structured rendered evidence
|
|
18
|
+
agent reasoning + source edit
|
|
19
|
+
↓
|
|
20
|
+
normal render/HMR
|
|
21
|
+
↓
|
|
22
|
+
fresh context() / select() / review()
|
|
23
|
+
↓
|
|
24
|
+
validated result
|
|
12
25
|
```
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
The page is the shared state boundary. Mesurer never needs to know which chat, thread, model, or agent is using it.
|
|
15
28
|
|
|
16
29
|
## Install the portable Agent Skill
|
|
17
30
|
|
|
18
|
-
The npm package ships one canonical `mesurer-ui` Agent Skill:
|
|
19
|
-
|
|
20
31
|
```bash
|
|
21
32
|
npx --yes --package=mesurer-solid@beta mesurer-skill install
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
The installed skill is self-contained:
|
|
25
36
|
|
|
26
37
|
```text
|
|
27
38
|
.agents/skills/mesurer-ui/
|
|
@@ -30,144 +41,337 @@ Use `--force` only when intentionally replacing an existing local copy. The inst
|
|
|
30
41
|
└── inject-script.js
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
The skill
|
|
44
|
+
The skill defines the same context-first workflow described here.
|
|
45
|
+
|
|
46
|
+
## Reuse a live human instance first
|
|
47
|
+
|
|
48
|
+
Before injecting anything:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const hasMesurer = Boolean(
|
|
52
|
+
window.__MESURER__ &&
|
|
53
|
+
window.__MESURER_INSTANCE__?.element?.isConnected
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if (hasMesurer) {
|
|
57
|
+
await window.__MESURER__.ready()
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If Mesurer exists, use that exact instance. The person may already have selected elements, placed guides, measured gaps, held distances, enabled rulers/X-ray, or saved annotations. Read that state before changing it.
|
|
62
|
+
|
|
63
|
+
The injector also reuses a live injected instance by default. Deliberate destructive replacement requires:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
window.__MESURER_CONFIG__ = { reuseExisting: false }
|
|
67
|
+
```
|
|
34
68
|
|
|
35
|
-
|
|
69
|
+
Do not use that while consuming human review state.
|
|
36
70
|
|
|
37
|
-
|
|
71
|
+
## Inject only when absent
|
|
38
72
|
|
|
39
|
-
|
|
73
|
+
Default host-project mutation budget is zero. Reuse the browser, Electron, WebView, Playwright, CDP, or other evaluation channel the harness already owns.
|
|
40
74
|
|
|
41
|
-
|
|
75
|
+
With the installed skill, evaluate `.agents/skills/mesurer-ui/assets/inject-script.js`. With the npm package installed, use `mesurer-solid/inject-script`.
|
|
42
76
|
|
|
43
77
|
```js
|
|
44
|
-
import { readFile } from "node:fs/promises"
|
|
45
|
-
import { fileURLToPath } from "node:url"
|
|
78
|
+
import { readFile } from "node:fs/promises"
|
|
79
|
+
import { fileURLToPath } from "node:url"
|
|
46
80
|
|
|
47
81
|
const source = await readFile(
|
|
48
82
|
fileURLToPath(import.meta.resolve("mesurer-solid/inject-script")),
|
|
49
83
|
"utf8",
|
|
50
|
-
)
|
|
84
|
+
)
|
|
51
85
|
|
|
52
|
-
await browser.evaluate(
|
|
53
|
-
|
|
86
|
+
const alreadyPresent = await browser.evaluate(() => Boolean(
|
|
87
|
+
window.__MESURER__ &&
|
|
88
|
+
window.__MESURER_INSTANCE__?.element?.isConnected
|
|
89
|
+
))
|
|
90
|
+
|
|
91
|
+
if (!alreadyPresent) {
|
|
92
|
+
await browser.evaluate(source)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await browser.evaluate(() => window.__MESURER__.ready())
|
|
54
96
|
```
|
|
55
97
|
|
|
56
|
-
|
|
98
|
+
Do not create a second browser/CDP connection, Mesurer server, special app build, or source mutation merely to inspect a page the harness already controls.
|
|
99
|
+
|
|
100
|
+
## Capability contract
|
|
101
|
+
|
|
102
|
+
After `ready()`:
|
|
57
103
|
|
|
58
104
|
```js
|
|
59
|
-
window.
|
|
105
|
+
window.__MESURER__.capabilities()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The context-oriented capability surface is:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
context
|
|
112
|
+
select
|
|
113
|
+
annotations
|
|
114
|
+
review
|
|
115
|
+
capturePlan
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`select` is an agent/harness operation; it does not add a human context-toolbar button. Human context controls remain:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
Copy Context
|
|
122
|
+
Copy Selection
|
|
123
|
+
Add Note
|
|
60
124
|
```
|
|
61
125
|
|
|
62
|
-
|
|
126
|
+
There is no `send`, `screenshots`, or `sendContext` delivery capability.
|
|
127
|
+
|
|
128
|
+
## Context acquisition precedence
|
|
63
129
|
|
|
64
|
-
|
|
130
|
+
Harnesses should follow this order.
|
|
65
131
|
|
|
66
|
-
|
|
132
|
+
### 1. Existing human evidence exists → read it first
|
|
67
133
|
|
|
68
134
|
```js
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
135
|
+
const workspace = await window.__MESURER__.context()
|
|
136
|
+
const annotations = await window.__MESURER__.annotations()
|
|
137
|
+
|
|
138
|
+
let selection = null
|
|
139
|
+
try {
|
|
140
|
+
selection = await window.__MESURER__.context({ scope: "selection" })
|
|
141
|
+
} catch {}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
For relevant annotations:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
const context = await window.__MESURER__.context({
|
|
148
|
+
annotation: annotation.id,
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Do not overwrite a meaningful live human selection until its context has been consumed and retained by the current agent task.
|
|
153
|
+
|
|
154
|
+
### 2. No relevant selection and intended target is ambiguous → ask the user
|
|
155
|
+
|
|
156
|
+
When the user's visual reference cannot be mapped confidently to exact rendered elements or a region, ask the person to select the intended element(s) or drag the intended region in Mesurer.
|
|
157
|
+
|
|
158
|
+
Then read:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const context = await window.__MESURER__.context({ scope: "selection" })
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Do not guess merely to avoid asking for a selection.
|
|
165
|
+
|
|
166
|
+
### 3. No relevant selection and agent knows exact target(s) → use `select()`
|
|
167
|
+
|
|
168
|
+
If the harness knows exactly which rendered elements correspond to the change, it should select them itself:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
const context = await window.__MESURER__.select("#pricing-card")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
or:
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
const context = await window.__MESURER__.select([
|
|
178
|
+
"#pricing-card",
|
|
179
|
+
"#pricing-cta",
|
|
180
|
+
])
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`select()` is deliberately context-returning. In one operation it:
|
|
184
|
+
|
|
185
|
+
1. switches Mesurer to Select;
|
|
186
|
+
2. visibly highlights the exact rendered targets;
|
|
187
|
+
3. makes them the live selection;
|
|
188
|
+
4. waits for the selection to settle;
|
|
189
|
+
5. returns selection-scoped `MesurerContextV1`.
|
|
190
|
+
|
|
191
|
+
The return value is the point. A harness should not call `select()` only for visual highlighting and then ignore the context.
|
|
192
|
+
|
|
193
|
+
Every supplied selector must resolve to exactly one target inside Mesurer's page target. Invalid, missing, or ambiguous selectors throw. Refine the selector or ask the user to select the intended target rather than guessing.
|
|
194
|
+
|
|
195
|
+
This makes a useful post-edit pattern trivial:
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
await window.__MESURER__.stable()
|
|
199
|
+
|
|
200
|
+
const evidence = await window.__MESURER__.select([
|
|
201
|
+
changedSelectorA,
|
|
202
|
+
changedSelectorB,
|
|
203
|
+
])
|
|
204
|
+
|
|
205
|
+
// `evidence` is the exact rendered result the agent should reason from.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## What context contains
|
|
209
|
+
|
|
210
|
+
`MesurerContextV1` is JSON-safe and uses `viewport-css-px` coordinates:
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
schema / id / createdAt
|
|
214
|
+
scope
|
|
215
|
+
page
|
|
216
|
+
viewport / DPR / scroll
|
|
217
|
+
coordinateSpace
|
|
218
|
+
regions
|
|
219
|
+
visualState
|
|
220
|
+
rulersVisible
|
|
221
|
+
xrayVisible
|
|
222
|
+
targets[]
|
|
223
|
+
ref
|
|
224
|
+
inspection.selector
|
|
225
|
+
inspection.rect
|
|
226
|
+
margin / padding / border
|
|
227
|
+
typography
|
|
228
|
+
appearance
|
|
229
|
+
layout
|
|
230
|
+
scroll / overflow
|
|
231
|
+
visualContext
|
|
232
|
+
guides[]
|
|
233
|
+
measurements[]
|
|
234
|
+
distances[]
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Prefer these rendered numbers over screenshot estimates or source-level assumptions.
|
|
238
|
+
|
|
239
|
+
## Multi-selection is relational
|
|
240
|
+
|
|
241
|
+
When several targets are selected, consume every target's complete inspection and the relevant relationships between them.
|
|
242
|
+
|
|
243
|
+
Use existing `visualContext.distances` first. For a needed pair not represented there:
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
const pair = window.__MESURER__.distance(selectorA, selectorB)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
A small selection can produce evidence such as:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
Card A width: 320px
|
|
253
|
+
Card B width: 320px
|
|
254
|
+
A → B horizontal gap: 24px
|
|
255
|
+
A/B top-edge delta: 0px
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
For large repeated sets, focus on adjacent/repeated/user-relevant relationships instead of dumping all O(n²) pairs.
|
|
259
|
+
|
|
260
|
+
## Context is required before and after meaningful visual edits
|
|
261
|
+
|
|
262
|
+
### Before editing
|
|
263
|
+
|
|
264
|
+
If the user supplied visual evidence, retain it before HMR can replace nodes:
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
const before = await window.__MESURER__.context({ scope: "selection" })
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
or:
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
const before = await window.__MESURER__.context({ annotation: annotationId })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### After editing
|
|
277
|
+
|
|
278
|
+
First wait for the actual rendered page:
|
|
279
|
+
|
|
280
|
+
```js
|
|
281
|
+
await window.__MESURER__.stable()
|
|
73
282
|
```
|
|
74
283
|
|
|
75
|
-
|
|
284
|
+
Then obtain fresh evidence using the strongest path.
|
|
76
285
|
|
|
77
|
-
|
|
286
|
+
Human annotation:
|
|
78
287
|
|
|
79
|
-
|
|
288
|
+
```js
|
|
289
|
+
const review = await window.__MESURER__.review(annotationId)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Still-relevant human selection:
|
|
80
293
|
|
|
81
294
|
```js
|
|
82
|
-
await window.__MESURER__.
|
|
83
|
-
await window.__MESURER__.context({ annotation: annotationId })
|
|
84
|
-
await window.__MESURER__.context({ scope: "selection" })
|
|
85
|
-
await window.__MESURER__.context()
|
|
86
|
-
await window.__MESURER__.contextText({ annotation: annotationId })
|
|
295
|
+
const after = await window.__MESURER__.context({ scope: "selection" })
|
|
87
296
|
```
|
|
88
297
|
|
|
89
|
-
|
|
298
|
+
Agent knows exact changed rendered targets:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
const after = await window.__MESURER__.select([
|
|
302
|
+
changedSelectorA,
|
|
303
|
+
changedSelectorB,
|
|
304
|
+
])
|
|
305
|
+
```
|
|
90
306
|
|
|
91
|
-
|
|
307
|
+
Target identity is ambiguous after the change: ask the user to select the intended result and then read selection context.
|
|
308
|
+
|
|
309
|
+
For meaningful visual work, lint/typecheck/tests/build are not enough. If Mesurer is available and the changed UI can be identified, a harness should not report completion without fresh Mesurer evidence for that UI.
|
|
310
|
+
|
|
311
|
+
## Annotation review
|
|
92
312
|
|
|
93
313
|
```js
|
|
94
314
|
await window.__MESURER__.stable()
|
|
95
315
|
const review = await window.__MESURER__.review(annotationId)
|
|
96
316
|
```
|
|
97
317
|
|
|
98
|
-
|
|
318
|
+
`review()` compares the human baseline against fresh context and reports exact pixel changes/missing evidence:
|
|
319
|
+
|
|
320
|
+
```text
|
|
321
|
+
gap: 37px → 24px
|
|
322
|
+
left-edge mismatch: 4px → 0px
|
|
323
|
+
width: 318px → 320px
|
|
324
|
+
expected target/guide/measurement missing
|
|
325
|
+
```
|
|
99
326
|
|
|
100
|
-
|
|
327
|
+
If the requested result remains numerically wrong, continue editing.
|
|
101
328
|
|
|
102
|
-
|
|
329
|
+
## Screenshots complement context
|
|
103
330
|
|
|
104
|
-
|
|
331
|
+
Mesurer supplies capture scope; the outer harness supplies real pixels:
|
|
105
332
|
|
|
106
333
|
```js
|
|
107
|
-
const plan = await window.__MESURER__.capturePlan({
|
|
334
|
+
const plan = await window.__MESURER__.capturePlan({ scope: "selection" })
|
|
335
|
+
|
|
108
336
|
await window.__MESURER__.prepareCapture()
|
|
109
337
|
try {
|
|
110
|
-
// harness screenshot
|
|
111
|
-
// close-up when present: plan.captures.find(c => c.id === "focus")
|
|
338
|
+
// use the harness's real screenshot primitive
|
|
112
339
|
} finally {
|
|
113
340
|
await window.__MESURER__.finishCapture()
|
|
114
341
|
}
|
|
115
342
|
```
|
|
116
343
|
|
|
117
|
-
|
|
344
|
+
Use the signals together:
|
|
118
345
|
|
|
119
|
-
|
|
346
|
+
```text
|
|
347
|
+
Mesurer context → exact geometry, box model, styles, distances, overflow
|
|
348
|
+
real screenshot → composition, hierarchy, clipping, color, visual judgment
|
|
349
|
+
```
|
|
120
350
|
|
|
121
|
-
## Source-mounted
|
|
351
|
+
## Source-mounted usage
|
|
122
352
|
|
|
123
|
-
When Mesurer is mounted from application code
|
|
353
|
+
When Mesurer is intentionally mounted from application code:
|
|
124
354
|
|
|
125
355
|
```ts
|
|
126
|
-
import {
|
|
127
|
-
contextPlugin,
|
|
128
|
-
mountMeasurer,
|
|
129
|
-
} from "mesurer-solid";
|
|
356
|
+
import { contextPlugin, mountMeasurer } from "mesurer-solid"
|
|
130
357
|
|
|
131
358
|
const mesurer = mountMeasurer({
|
|
132
359
|
agent: true,
|
|
133
360
|
plugins: [contextPlugin()],
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Browser/harness delivery capabilities are plugin options:
|
|
138
|
-
|
|
139
|
-
```ts
|
|
140
|
-
contextPlugin({
|
|
141
|
-
evidenceProvider: async ({ context, plan }) => [],
|
|
142
|
-
sendContext: async ({ context, text, images }) => {
|
|
143
|
-
// Send using the ACP session already owned by the host.
|
|
144
|
-
},
|
|
145
361
|
})
|
|
146
362
|
```
|
|
147
363
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```ts
|
|
151
|
-
mesurer.pluginHost?.remove("mesurer.context");
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
The context UI, annotation runtime, shortcuts, service, and listeners are disposed together.
|
|
364
|
+
The same API is available on `mesurer.agent` and, when configured, `window.__MESURER__`:
|
|
155
365
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Mesurer does not own an ACP process or session. The ACP client/harness that already owns the session sends Mesurer output.
|
|
159
|
-
|
|
160
|
-
```ts
|
|
161
|
-
import { toAcpContentBlocks } from "mesurer-solid";
|
|
162
|
-
|
|
163
|
-
const blocks = toAcpContentBlocks(context, images);
|
|
366
|
+
```js
|
|
367
|
+
const context = await mesurer.agent.select("#target")
|
|
164
368
|
```
|
|
165
369
|
|
|
166
|
-
|
|
370
|
+
No transport callback is involved.
|
|
167
371
|
|
|
168
|
-
##
|
|
372
|
+
## Low-level inspection
|
|
169
373
|
|
|
170
|
-
These
|
|
374
|
+
These remain useful for focused queries:
|
|
171
375
|
|
|
172
376
|
```js
|
|
173
377
|
window.__MESURER__.inspect(".selector")
|
|
@@ -180,10 +384,23 @@ await window.__MESURER__.state()
|
|
|
180
384
|
await window.__MESURER__.stable()
|
|
181
385
|
```
|
|
182
386
|
|
|
183
|
-
|
|
387
|
+
Prefer `context()`, `select()`, and `review()` for visual development because they provide context that directly carries human intent or agent-selected affected UI into reasoning.
|
|
388
|
+
|
|
389
|
+
## Harness completion rule
|
|
184
390
|
|
|
185
|
-
|
|
391
|
+
A good harness-level visual completion loop is:
|
|
186
392
|
|
|
187
|
-
|
|
393
|
+
```text
|
|
394
|
+
1. discover/reuse Mesurer
|
|
395
|
+
2. consume existing human context
|
|
396
|
+
3. if target ambiguous, ask user to select
|
|
397
|
+
4. otherwise select known affected rendered target(s) when needed
|
|
398
|
+
5. consume returned context
|
|
399
|
+
6. edit normal source
|
|
400
|
+
7. wait for stable render
|
|
401
|
+
8. get fresh review/context; use select() for known changed targets
|
|
402
|
+
9. optionally capture real screenshot
|
|
403
|
+
10. iterate until rendered evidence supports the claim
|
|
404
|
+
```
|
|
188
405
|
|
|
189
|
-
|
|
406
|
+
**Context is the output of the Mesurer step, not an optional side effect.**
|