mesurer-solid 0.1.6 → 0.1.7-beta.1
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 +65 -462
- package/README.md +71 -406
- package/dist/arrange.js +61 -43
- package/dist/core.d.ts +1 -0
- package/dist/core.js +116 -99
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4613 -2851
- package/dist/inject-script.js +118 -14
- package/dist/inject.js +4382 -2637
- package/dist/screenshot.js +309 -292
- package/package.json +1 -1
- package/skills/mesurer-ui/SKILL.md +74 -532
package/AGENT_INTEGRATION.md
CHANGED
|
@@ -1,32 +1,10 @@
|
|
|
1
1
|
# Mesurer agent integration
|
|
2
2
|
|
|
3
|
-
Mesurer's agent integration is
|
|
3
|
+
Mesurer's agent integration is the rendered page itself. The coding agent reads `window.__MESURER__` through the browser control it already has, consumes human visual intent, edits normal application source, and verifies the real Live result.
|
|
4
4
|
|
|
5
|
-
There is no Mesurer MCP
|
|
5
|
+
There is no Mesurer MCP server, localhost daemon, Send-to-agent callback, or harness-specific transport.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```text
|
|
10
|
-
human visual intent
|
|
11
|
-
↓
|
|
12
|
-
Arrange / text-style Desired edits / annotation / selection
|
|
13
|
-
↓
|
|
14
|
-
window.__MESURER__
|
|
15
|
-
↓
|
|
16
|
-
structured rendered evidence + optional harness screenshot
|
|
17
|
-
↓
|
|
18
|
-
agent edits normal source
|
|
19
|
-
↓
|
|
20
|
-
real render / HMR
|
|
21
|
-
↓
|
|
22
|
-
fresh Live review/context
|
|
23
|
-
↓
|
|
24
|
-
validated result
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Install the portable Agent Skill
|
|
28
|
-
|
|
29
|
-
Use the stable package by default:
|
|
7
|
+
## Install the Agent Skill
|
|
30
8
|
|
|
31
9
|
```bash
|
|
32
10
|
npx --yes --package=mesurer-solid mesurer-skill install
|
|
@@ -34,7 +12,7 @@ npx --yes --package=mesurer-solid mesurer-skill install
|
|
|
34
12
|
|
|
35
13
|
Use `mesurer-solid@beta` only when intentionally validating a prerelease.
|
|
36
14
|
|
|
37
|
-
The
|
|
15
|
+
The installer writes a self-contained skill and injection artifact:
|
|
38
16
|
|
|
39
17
|
```text
|
|
40
18
|
.agents/skills/mesurer-ui/
|
|
@@ -43,11 +21,9 @@ The installed skill is self-contained:
|
|
|
43
21
|
└── inject-script.js
|
|
44
22
|
```
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
## Reuse a live human instance
|
|
24
|
+
## Reuse a live instance
|
|
49
25
|
|
|
50
|
-
Before injecting anything:
|
|
26
|
+
Before injecting anything, check whether Mesurer is already connected:
|
|
51
27
|
|
|
52
28
|
```js
|
|
53
29
|
const hasMesurer = Boolean(
|
|
@@ -60,107 +36,31 @@ if (hasMesurer) {
|
|
|
60
36
|
}
|
|
61
37
|
```
|
|
62
38
|
|
|
63
|
-
If
|
|
39
|
+
If it exists, use that instance. A person may already have selected targets, guides, measurements, annotations, Arrange intent, text/style intent, or screenshot review state. That state is part of the request.
|
|
64
40
|
|
|
65
|
-
The injector reuses a
|
|
41
|
+
The injector reuses a connected instance by default. Deliberate replacement requires:
|
|
66
42
|
|
|
67
43
|
```js
|
|
68
44
|
window.__MESURER_CONFIG__ = { reuseExisting: false }
|
|
69
45
|
```
|
|
70
46
|
|
|
71
|
-
Do not
|
|
47
|
+
Do not replace a live instance while consuming human review state.
|
|
72
48
|
|
|
73
49
|
## Inject only when absent
|
|
74
50
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
With the installed skill, evaluate `.agents/skills/mesurer-ui/assets/inject-script.js`. With the npm package installed, use `mesurer-solid/inject-script`.
|
|
78
|
-
|
|
79
|
-
```js
|
|
80
|
-
import { readFile } from "node:fs/promises"
|
|
81
|
-
import { fileURLToPath } from "node:url"
|
|
82
|
-
|
|
83
|
-
const source = await readFile(
|
|
84
|
-
fileURLToPath(import.meta.resolve("mesurer-solid/inject-script")),
|
|
85
|
-
"utf8",
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
const alreadyPresent = await browser.evaluate(() => Boolean(
|
|
89
|
-
window.__MESURER__ &&
|
|
90
|
-
window.__MESURER_INSTANCE__?.element?.isConnected
|
|
91
|
-
))
|
|
92
|
-
|
|
93
|
-
if (!alreadyPresent) {
|
|
94
|
-
await browser.evaluate(source)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
await browser.evaluate(() => window.__MESURER__.ready())
|
|
98
|
-
```
|
|
51
|
+
Use the browser, Electron, WebView, Playwright, CDP, or other evaluation channel the harness already owns. With the installed skill, evaluate `.agents/skills/mesurer-ui/assets/inject-script.js`. With the npm package installed, read `mesurer-solid/inject-script`.
|
|
99
52
|
|
|
100
|
-
Do not
|
|
53
|
+
Do not mutate application source or create another browser connection merely to get Mesurer into a page the harness can already evaluate.
|
|
101
54
|
|
|
102
|
-
|
|
55
|
+
The optional human Screenshot plugin remains disabled during normal agent injection unless configured before first injection:
|
|
103
56
|
|
|
104
57
|
```js
|
|
105
58
|
window.__MESURER_CONFIG__ = { screenshot: true }
|
|
106
59
|
```
|
|
107
60
|
|
|
108
|
-
##
|
|
61
|
+
## Inventory human intent
|
|
109
62
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```js
|
|
113
|
-
window.__MESURER__.capabilities()
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
The context-oriented surface is:
|
|
117
|
-
|
|
118
|
-
```text
|
|
119
|
-
context
|
|
120
|
-
select
|
|
121
|
-
annotations
|
|
122
|
-
review
|
|
123
|
-
capturePlan
|
|
124
|
-
textEdits
|
|
125
|
-
textEdit
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Direct text editing reports:
|
|
129
|
-
|
|
130
|
-
```text
|
|
131
|
-
textEdit
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
and exposes:
|
|
135
|
-
|
|
136
|
-
```text
|
|
137
|
-
textEdits()
|
|
138
|
-
textEdit(id)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
When `arrangePlugin()` is mounted, capabilities also reports:
|
|
142
|
-
|
|
143
|
-
```text
|
|
144
|
-
arrange
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
and exposes:
|
|
148
|
-
|
|
149
|
-
```text
|
|
150
|
-
arrangements()
|
|
151
|
-
arrange(id)
|
|
152
|
-
showArrange(id, state)
|
|
153
|
-
arrangeCapturePlan(id, state)
|
|
154
|
-
reviewArrange(id, tolerance?)
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
There is no `send`, `screenshots`, or `sendContext` delivery capability. Screenshot bytes stay with the outer browser harness.
|
|
158
|
-
|
|
159
|
-
## Broad Mesurer/context requests mean inspect all human intent
|
|
160
|
-
|
|
161
|
-
If the user says “check Mesurer,” “check Measure,” “look at Mesurer context,” “see what I highlighted/moved/annotated/edited,” or otherwise asks generally about Mesurer state, treat that as a request to inspect the **combined live review state**, not only the return value of `context()`.
|
|
162
|
-
|
|
163
|
-
Start with a non-destructive inventory:
|
|
63
|
+
For a broad request such as “check Mesurer,” collect all relevant state before editing source:
|
|
164
64
|
|
|
165
65
|
```js
|
|
166
66
|
const capabilities = window.__MESURER__.capabilities().capabilities
|
|
@@ -179,7 +79,7 @@ try {
|
|
|
179
79
|
} catch {}
|
|
180
80
|
```
|
|
181
81
|
|
|
182
|
-
|
|
82
|
+
Resolve relevant records before HMR can replace their DOM targets:
|
|
183
83
|
|
|
184
84
|
```js
|
|
185
85
|
const annotationContexts = await Promise.all(
|
|
@@ -197,56 +97,35 @@ const textEditIntents = await Promise.all(
|
|
|
197
97
|
)
|
|
198
98
|
```
|
|
199
99
|
|
|
200
|
-
|
|
100
|
+
Treat annotation notes, Arrange Desired geometry, and text/style Desired state as intent. Treat selection, measurements, distances, geometry, and computed styles as rendered evidence.
|
|
201
101
|
|
|
202
|
-
|
|
102
|
+
## Select exact targets
|
|
203
103
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Arrange lets a person reposition selected rendered elements into the layout they want without editing application source. If Arrange is available, consume relevant saved intents before changing human selection or editing source:
|
|
104
|
+
Do not overwrite a meaningful human selection until its context has been retained. When there is no relevant human selection and the exact targets are known:
|
|
207
105
|
|
|
208
106
|
```js
|
|
209
|
-
const
|
|
210
|
-
|
|
107
|
+
const context = await window.__MESURER__.select([
|
|
108
|
+
"#pricing-card",
|
|
109
|
+
"#pricing-cta",
|
|
110
|
+
])
|
|
211
111
|
```
|
|
212
112
|
|
|
213
|
-
Each
|
|
113
|
+
Each selector must resolve to exactly one target. Missing or ambiguous selectors throw rather than binding to a guess.
|
|
214
114
|
|
|
215
|
-
|
|
115
|
+
For multi-selection, inspect every selected target and the relevant pair relationships. Prefer `selection.visualContext.distances`; use `distance(a, b)` when a needed pair has no existing evidence.
|
|
216
116
|
|
|
217
|
-
|
|
117
|
+
## Arrange intent
|
|
218
118
|
|
|
219
|
-
|
|
119
|
+
Arrange expresses requested geometry, not source implementation.
|
|
220
120
|
|
|
221
121
|
```js
|
|
222
|
-
await window.__MESURER__.
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
await window.__MESURER__.prepareCapture()
|
|
226
|
-
try {
|
|
227
|
-
// outer harness captures Before with beforePlan
|
|
228
|
-
} finally {
|
|
229
|
-
await window.__MESURER__.finishCapture()
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
await window.__MESURER__.showArrange(arrangeId, "desired")
|
|
233
|
-
const desiredPlan = await window.__MESURER__.arrangeCapturePlan(arrangeId, "desired")
|
|
234
|
-
|
|
235
|
-
await window.__MESURER__.prepareCapture()
|
|
236
|
-
try {
|
|
237
|
-
// outer harness captures Desired with desiredPlan
|
|
238
|
-
} finally {
|
|
239
|
-
await window.__MESURER__.finishCapture()
|
|
240
|
-
}
|
|
122
|
+
const intents = await window.__MESURER__.arrangements()
|
|
123
|
+
const intent = await window.__MESURER__.arrange(arrangeId)
|
|
241
124
|
```
|
|
242
125
|
|
|
243
|
-
|
|
126
|
+
A 96px Desired offset does not mean production source should use `transform: translateX(96px)`. Implement the visual outcome through the application's real flex/grid, spacing, sizing, ordering, or component structure.
|
|
244
127
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
### Verify the source-produced result
|
|
248
|
-
|
|
249
|
-
After editing source:
|
|
128
|
+
After source changes:
|
|
250
129
|
|
|
251
130
|
```js
|
|
252
131
|
await window.__MESURER__.stable()
|
|
@@ -254,366 +133,90 @@ await window.__MESURER__.showArrange(arrangeId, "live")
|
|
|
254
133
|
const review = await window.__MESURER__.reviewArrange(arrangeId)
|
|
255
134
|
```
|
|
256
135
|
|
|
257
|
-
Live removes
|
|
258
|
-
|
|
259
|
-
```text
|
|
260
|
-
Before Desired Live
|
|
261
|
-
x 120 → x 284 x 276
|
|
262
|
-
remaining +8px
|
|
263
|
-
|
|
264
|
-
edit source
|
|
265
|
-
↓
|
|
266
|
-
Live x 284
|
|
267
|
-
↓
|
|
268
|
-
matched ✓
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
If review is still numerically wrong, continue editing. If it is `stale` or `partial`, do not silently bind the intent to a different element.
|
|
272
|
-
|
|
273
|
-
## Direct text editing: human UI and durable intent
|
|
274
|
-
|
|
275
|
-
Direct text editing records human copy and typography intent without pretending to edit application source. It is not a separate top-level text-edit tool/plugin.
|
|
276
|
-
|
|
277
|
-
The human-facing inspection tool is **Typography**. The existing internal compatibility id and stable command remain `text-inspector` / `builtin.text-inspector`; agents should not script the toolbar by label.
|
|
278
|
-
|
|
279
|
-
A person enters direct editing while **Select** or **Typography** is active:
|
|
280
|
-
|
|
281
|
-
```text
|
|
282
|
-
double-click ordinary direct text
|
|
283
|
-
(or double-tap with touch/pen)
|
|
284
|
-
↓
|
|
285
|
-
current text is selected in full
|
|
286
|
-
↓
|
|
287
|
-
in-place editor uses rendered typography
|
|
288
|
-
↓
|
|
289
|
-
Mesurer-style direct typography toolbar
|
|
290
|
-
B / I / U / Font / Size / Weight / Color
|
|
291
|
-
+ separate semantic Text/H1/H2/H3 preset popup
|
|
292
|
-
+ contextual Typography card for that exact field
|
|
293
|
-
↓
|
|
294
|
-
Enter keeps Desired / Shift+Enter newline
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Because Arrange keeps Select active, this same interaction works while Arrange remains selected. Starting the edit contextually activates Typography for the field while Select/Arrange remain active; it does not force the reviewer out of the layout workflow.
|
|
136
|
+
Live removes the temporary Arrange preview before measuring source output.
|
|
298
137
|
|
|
299
|
-
|
|
138
|
+
Arrange preview ownership is conservative. Mesurer restores an older transform only while the element still carries the exact preview value and priority Mesurer applied. Host-authored transform changes survive review, refresh, and disposal.
|
|
300
139
|
|
|
301
|
-
|
|
302
|
-
- the separate semantic popup contains only **Text** plus Heading 1/2/3 for semantic levels actually rendered by visible direct-text page elements;
|
|
303
|
-
- each semantic preset uses the page's **dominant rendered typography bundle** for that level: font family, size, weight, style, line height, tracking, text transform, and color;
|
|
304
|
-
- non-dominant page variants remain available through the direct Font/Size/Weight/color controls;
|
|
305
|
-
- heading levels absent from the rendered page are not invented.
|
|
140
|
+
See [Arrange](../../docs/ARRANGE.md).
|
|
306
141
|
|
|
307
|
-
|
|
142
|
+
## Text and Typography intent
|
|
308
143
|
|
|
309
|
-
|
|
144
|
+
Direct text editing records copy and typography intent without pretending to edit source. The human-facing tool is **Typography**; the internal compatibility id remains `text-inspector`.
|
|
310
145
|
|
|
311
|
-
|
|
146
|
+
Editing starts by double-click/double-tap while Select or Typography is active. Arrange keeps Select active, so editing works while Arrange remains selected.
|
|
312
147
|
|
|
313
|
-
|
|
148
|
+
Native editing stays native. Mesurer does not intercept form controls or descendants that inherit `contenteditable`. A nested `contenteditable="false"` boundary ends inherited editability and can become a Mesurer target when the direct-text rules otherwise pass.
|
|
314
149
|
|
|
315
|
-
The
|
|
150
|
+
The editing UI exposes direct B/I/U, Font, Size, Weight, rendered-page colors, custom color, and a separate Text/H1/H2/H3 semantic preset popup. Missing heading levels are not invented.
|
|
316
151
|
|
|
317
|
-
|
|
152
|
+
If Typography was already explicitly selected, the direct-edit session suppresses the older hover/pinned Typography surface so the field has one live card. The normal surface returns when editing ends.
|
|
318
153
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
If `capabilities.textEdit` is true, read saved edits before source changes:
|
|
154
|
+
Read durable intent through:
|
|
322
155
|
|
|
323
156
|
```js
|
|
324
157
|
const edits = await window.__MESURER__.textEdits()
|
|
325
158
|
const intent = await window.__MESURER__.textEdit(textEditId)
|
|
326
159
|
```
|
|
327
160
|
|
|
328
|
-
|
|
161
|
+
Treat `intent.desired` and style deltas as visual/source requirements, not inline CSS instructions. Look for the application's semantic props, classes, design tokens, CSS variables, theme values, or stylesheet rules that produce the requested render.
|
|
329
162
|
|
|
330
|
-
|
|
331
|
-
id / createdAt / pageUrl
|
|
332
|
-
selector / nodeIndex
|
|
333
|
-
before
|
|
334
|
-
original text
|
|
335
|
-
desired
|
|
336
|
-
requested text
|
|
337
|
-
styles[]
|
|
338
|
-
property
|
|
339
|
-
before
|
|
340
|
-
desired
|
|
341
|
-
```
|
|
163
|
+
Verification must use Live source with the Desired preview inactive. Text/style preview ownership follows the same conservative rule as Arrange: while the DOM still equals Mesurer's owned value, undo/redo can move it to the restored Desired value; once the application changes it, Mesurer preserves the host value instead of overwriting it during history or cleanup.
|
|
342
164
|
|
|
343
|
-
|
|
165
|
+
See [Direct text editing and Typography](../../docs/TEXT_EDITING.md).
|
|
344
166
|
|
|
345
|
-
|
|
167
|
+
## Annotation and context review
|
|
346
168
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
While Select or Typography is active, Mesurer may be rendering the saved Desired text/style as a reversible preview. Do not compare against that preview and declare the source implementation correct.
|
|
350
|
-
|
|
351
|
-
After editing source:
|
|
352
|
-
|
|
353
|
-
1. retain the text-edit intent;
|
|
354
|
-
2. wait for the real render;
|
|
355
|
-
3. deactivate the active Select/Typography preview without clearing the intent;
|
|
356
|
-
4. inspect the actual target text and computed typography;
|
|
357
|
-
5. compare those Live values with `intent.desired` and `intent.styles`;
|
|
358
|
-
6. reactivate Select only if continued review is useful.
|
|
359
|
-
|
|
360
|
-
Mesurer relinquishes ownership when the application itself changes the value, so correct source output remains correct with the temporary preview inactive.
|
|
361
|
-
|
|
362
|
-
## Context acquisition after intent preservation
|
|
363
|
-
|
|
364
|
-
Once any relevant Arrange and text-edit intent is retained, use this order for ordinary context.
|
|
365
|
-
|
|
366
|
-
### Existing human selection or annotation
|
|
169
|
+
A saved annotation carries target-bound intent and an immutable baseline:
|
|
367
170
|
|
|
368
171
|
```js
|
|
369
|
-
const
|
|
370
|
-
const annotations = await window.__MESURER__.annotations()
|
|
371
|
-
|
|
372
|
-
let selection = null
|
|
373
|
-
try {
|
|
374
|
-
selection = await window.__MESURER__.context({ scope: "selection" })
|
|
375
|
-
} catch {}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
For a relevant annotation:
|
|
379
|
-
|
|
380
|
-
```js
|
|
381
|
-
const context = await window.__MESURER__.context({
|
|
382
|
-
annotation: annotation.id,
|
|
383
|
-
})
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Do not overwrite a meaningful human selection until its context has been retained by the current task.
|
|
387
|
-
|
|
388
|
-
### Ambiguous target
|
|
389
|
-
|
|
390
|
-
If 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 region in Mesurer, then read:
|
|
391
|
-
|
|
392
|
-
```js
|
|
393
|
-
const context = await window.__MESURER__.context({ scope: "selection" })
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
Do not guess merely to avoid asking for a selection.
|
|
397
|
-
|
|
398
|
-
### Agent knows the exact target
|
|
399
|
-
|
|
400
|
-
Use `select()` and consume its return value:
|
|
401
|
-
|
|
402
|
-
```js
|
|
403
|
-
const context = await window.__MESURER__.select([
|
|
404
|
-
"#pricing-card",
|
|
405
|
-
"#pricing-cta",
|
|
406
|
-
])
|
|
172
|
+
const context = await window.__MESURER__.context({ annotation: annotationId })
|
|
407
173
|
```
|
|
408
174
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
Every selector must resolve to exactly one page target. Invalid, missing, or ambiguous selectors throw.
|
|
412
|
-
|
|
413
|
-
## What context contains
|
|
414
|
-
|
|
415
|
-
`MesurerContextV1` is JSON-safe and uses `viewport-css-px` coordinates:
|
|
416
|
-
|
|
417
|
-
```text
|
|
418
|
-
schema / id / createdAt
|
|
419
|
-
scope
|
|
420
|
-
page
|
|
421
|
-
viewport / DPR / scroll
|
|
422
|
-
coordinateSpace
|
|
423
|
-
regions
|
|
424
|
-
visualState
|
|
425
|
-
rulersVisible
|
|
426
|
-
xrayVisible
|
|
427
|
-
targets[]
|
|
428
|
-
ref
|
|
429
|
-
inspection.selector
|
|
430
|
-
inspection.rect
|
|
431
|
-
margin / padding / border
|
|
432
|
-
typography
|
|
433
|
-
appearance
|
|
434
|
-
layout
|
|
435
|
-
scroll / overflow
|
|
436
|
-
visualContext
|
|
437
|
-
guides[]
|
|
438
|
-
measurements[]
|
|
439
|
-
distances[]
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
Prefer rendered numbers over screenshot estimates or source assumptions.
|
|
443
|
-
|
|
444
|
-
## Multi-selection is relational
|
|
445
|
-
|
|
446
|
-
When several targets are selected, consume each target plus the useful relationships between them. Start with `visualContext.distances`; for a pair not represented there:
|
|
447
|
-
|
|
448
|
-
```js
|
|
449
|
-
const pair = window.__MESURER__.distance(selectorA, selectorB)
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
A useful result can be:
|
|
453
|
-
|
|
454
|
-
```text
|
|
455
|
-
Card A width: 320px
|
|
456
|
-
Card B width: 320px
|
|
457
|
-
A → B horizontal gap: 24px
|
|
458
|
-
A/B top-edge delta: 0px
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
For large repeated sets, focus on adjacent or user-relevant relationships rather than dumping all O(n²) pairs.
|
|
462
|
-
|
|
463
|
-
## Fresh evidence is required after visual edits
|
|
464
|
-
|
|
465
|
-
Wait for the actual rendered page:
|
|
175
|
+
After source changes:
|
|
466
176
|
|
|
467
177
|
```js
|
|
468
178
|
await window.__MESURER__.stable()
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
Then use the strongest applicable path.
|
|
472
|
-
|
|
473
|
-
Arrange:
|
|
474
|
-
|
|
475
|
-
```js
|
|
476
|
-
await window.__MESURER__.showArrange(arrangeId, "live")
|
|
477
|
-
const review = await window.__MESURER__.reviewArrange(arrangeId)
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
Text/style Desired intent, with its temporary preview inactive:
|
|
481
|
-
|
|
482
|
-
```js
|
|
483
|
-
const intent = await window.__MESURER__.textEdit(textEditId)
|
|
484
|
-
const live = window.__MESURER__.inspect(intent.selector)
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
Compare the actual rendered text and relevant typography values with the saved Desired intent.
|
|
488
|
-
|
|
489
|
-
Annotation:
|
|
490
|
-
|
|
491
|
-
```js
|
|
492
179
|
const review = await window.__MESURER__.review(annotationId)
|
|
493
180
|
```
|
|
494
181
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
```js
|
|
498
|
-
const after = await window.__MESURER__.context({ scope: "selection" })
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
Agent-known changed targets:
|
|
502
|
-
|
|
503
|
-
```js
|
|
504
|
-
const after = await window.__MESURER__.select([
|
|
505
|
-
changedSelectorA,
|
|
506
|
-
changedSelectorB,
|
|
507
|
-
])
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
For meaningful visual work, lint/typecheck/tests/build are not enough. They validate implementation mechanics, not the rendered result.
|
|
511
|
-
|
|
512
|
-
## Screenshot ownership
|
|
513
|
-
|
|
514
|
-
Mesurer has two different screenshot roles.
|
|
515
|
-
|
|
516
|
-
### Human camera tool
|
|
517
|
-
|
|
518
|
-
The optional `mesurer.screenshot` plugin gives the person a camera tool:
|
|
519
|
-
|
|
520
|
-
```ts
|
|
521
|
-
import { mountMesurer } from "mesurer-solid"
|
|
522
|
-
import { screenshotPlugin } from "mesurer-solid/screenshot"
|
|
523
|
-
|
|
524
|
-
const mesurer = mountMesurer({
|
|
525
|
-
plugins: [screenshotPlugin()],
|
|
526
|
-
})
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
It captures HiDPI-aware PNGs and can copy/download them or keep a draggable preview/viewer. Preserve an existing human screenshot preview unless the task explicitly requires manipulating it.
|
|
182
|
+
`review()` can report exact geometry/evidence changes without any external message transport.
|
|
530
183
|
|
|
531
|
-
|
|
184
|
+
## Screenshots
|
|
532
185
|
|
|
533
|
-
|
|
186
|
+
For ordinary coding-agent evidence, the outer harness owns screenshot bytes while Mesurer prepares capture presentation:
|
|
534
187
|
|
|
535
188
|
```js
|
|
536
|
-
const plan = await window.__MESURER__.capturePlan({
|
|
537
|
-
|
|
189
|
+
const plan = await window.__MESURER__.capturePlan({ annotation: annotationId })
|
|
538
190
|
await window.__MESURER__.prepareCapture()
|
|
539
191
|
try {
|
|
540
|
-
//
|
|
192
|
+
// harness screenshot
|
|
541
193
|
} finally {
|
|
542
194
|
await window.__MESURER__.finishCapture()
|
|
543
195
|
}
|
|
544
196
|
```
|
|
545
197
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
Use all three signals when relevant:
|
|
549
|
-
|
|
550
|
-
```text
|
|
551
|
-
Mesurer context/review → geometry, box model, styles, distances, overflow
|
|
552
|
-
saved Desired intent → requested Arrange geometry + copy/typography
|
|
553
|
-
real screenshot → composition, hierarchy, clipping, color, visual judgment
|
|
554
|
-
```
|
|
198
|
+
The optional `mesurer-solid/screenshot` plugin is a separate human camera workflow. It is not an agent delivery capability. Preserve an existing human preview unless the task is specifically about Screenshot behavior.
|
|
555
199
|
|
|
556
|
-
##
|
|
200
|
+
## Revalidate after source edits
|
|
557
201
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
```ts
|
|
561
|
-
import {
|
|
562
|
-
contextPlugin,
|
|
563
|
-
mountMesurer,
|
|
564
|
-
} from "mesurer-solid"
|
|
565
|
-
import { arrangePlugin } from "mesurer-solid/arrange"
|
|
566
|
-
|
|
567
|
-
const mesurer = mountMesurer({
|
|
568
|
-
agent: true,
|
|
569
|
-
plugins: [
|
|
570
|
-
contextPlugin(),
|
|
571
|
-
arrangePlugin(),
|
|
572
|
-
],
|
|
573
|
-
})
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
The same API is available on `mesurer.agent` and, when configured, `window.__MESURER__`.
|
|
577
|
-
|
|
578
|
-
Arrange remains optional. Direct text editing and text-edit intent are part of the base inspector runtime; applications do not need a separate text-edit plugin.
|
|
579
|
-
|
|
580
|
-
## Low-level inspection
|
|
581
|
-
|
|
582
|
-
These remain useful for focused queries:
|
|
202
|
+
After HMR or reload settles:
|
|
583
203
|
|
|
584
204
|
```js
|
|
585
|
-
window.__MESURER__.inspect(".selector")
|
|
586
|
-
window.__MESURER__.inspectAll(".selector")
|
|
587
|
-
window.__MESURER__.at(x, y)
|
|
588
|
-
window.__MESURER__.distance(".a", ".b")
|
|
589
|
-
window.__MESURER__.viewport()
|
|
590
|
-
await window.__MESURER__.feedback([".selector"])
|
|
591
|
-
await window.__MESURER__.state()
|
|
592
|
-
await window.__MESURER__.textEdits()
|
|
593
|
-
await window.__MESURER__.textEdit(textEditId)
|
|
594
205
|
await window.__MESURER__.stable()
|
|
595
206
|
```
|
|
596
207
|
|
|
597
|
-
|
|
208
|
+
Then compare the same evidence retained before editing:
|
|
598
209
|
|
|
599
|
-
|
|
210
|
+
- Arrange Desired against Live through `reviewArrange()`;
|
|
211
|
+
- text/style Desired against Live with the text preview inactive;
|
|
212
|
+
- saved annotations through `review()`;
|
|
213
|
+
- current selections and measurements through fresh `context()`;
|
|
214
|
+
- exact geometry through `inspect()`, `distance()`, and `viewport()`.
|
|
600
215
|
|
|
601
|
-
|
|
216
|
+
Do not clear human history merely to expose Live state, and do not reinject just to refresh context.
|
|
602
217
|
|
|
603
|
-
|
|
604
|
-
1. discover/reuse Mesurer and preserve human state
|
|
605
|
-
2. if the request is broad, inventory Arrange + text edits + annotations + selection + workspace context before narrowing
|
|
606
|
-
3. consume relevant Arrange and text/style Desired intent first
|
|
607
|
-
4. capture Arrange Before/Desired when useful, before source edits
|
|
608
|
-
5. consume existing annotation/selection context
|
|
609
|
-
6. resolve exact targets; ask only when genuinely ambiguous
|
|
610
|
-
7. edit normal source
|
|
611
|
-
8. wait for stable render
|
|
612
|
-
9. switch Arrange to Live and deactivate text Desired preview when applicable
|
|
613
|
-
10. get fresh source-rendered review/context and optional screenshot
|
|
614
|
-
11. iterate until rendered evidence supports completion
|
|
615
|
-
```
|
|
218
|
+
## Completion rule
|
|
616
219
|
|
|
617
|
-
|
|
220
|
+
A meaningful Mesurer call should affect the work. If exact geometry is available, use it instead of estimating pixels from a screenshot. If the user already encoded intent in Arrange, text editing, annotations, or selection, consume it before asking them to repeat it.
|
|
618
221
|
|
|
619
|
-
|
|
222
|
+
The repository's packaged [`mesurer-ui` skill](./skills/mesurer-ui/SKILL.md) carries the operational version of this contract for coding agents. [Browser and agent integration](../../docs/BROWSER_HARNESS.md) documents the outer harness boundary.
|