mesurer-solid 0.1.5 → 0.1.7-beta.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.
@@ -1,32 +1,10 @@
1
1
  # Mesurer agent integration
2
2
 
3
- Mesurer's agent integration is direct: **the agent reads Mesurer through the same rendered page it is already controlling**.
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, WebMCP, ACP, localhost feedback daemon, Send-to-agent callback, chat/session bridge, or harness-specific Mesurer adapter.
5
+ There is no Mesurer MCP server, localhost daemon, Send-to-agent callback, or harness-specific transport.
6
6
 
7
- The page is the shared state boundary. A meaningful Mesurer operation should return evidence that the coding agent actually consumes before it edits source or claims completion.
8
-
9
- ```text
10
- human visual intent
11
-
12
- Arrange / 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 installed skill is self-contained:
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
- The skill defines the same state-preservation, Arrange, context, screenshot, HMR, and completion rules described here.
24
+ ## Reuse a live instance
47
25
 
48
- ## Reuse a live human instance
49
-
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,92 +36,31 @@ if (hasMesurer) {
60
36
  }
61
37
  ```
62
38
 
63
- If Mesurer exists, use that exact instance. The person may already have arranged elements, selected targets, placed guides, measured gaps, held distances, enabled rulers/X-ray, saved annotations, or kept a screenshot preview open. That state is part of the user's visual message.
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 live injected instance by default. Deliberate destructive replacement requires:
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 use that while consuming human review state.
47
+ Do not replace a live instance while consuming human review state.
72
48
 
73
49
  ## Inject only when absent
74
50
 
75
- Default host-project mutation budget is zero. Reuse the browser, Electron, WebView, Playwright, CDP, or other evaluation channel the harness already owns.
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 create a second browser/CDP connection, Mesurer server, special app build, or source mutation merely to inspect a page the harness already controls.
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
- Normal injection keeps the optional human screenshot plugin disabled unless requested:
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
- ## Capabilities
61
+ ## Inventory human intent
109
62
 
110
- After `ready()`:
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
- ```
125
-
126
- When `arrangePlugin()` is mounted, it also reports:
127
-
128
- ```text
129
- arrange
130
- ```
131
-
132
- and exposes:
133
-
134
- ```text
135
- arrangements()
136
- arrange(id)
137
- showArrange(id, state)
138
- arrangeCapturePlan(id, state)
139
- reviewArrange(id, tolerance?)
140
- ```
141
-
142
- There is no `send`, `screenshots`, or `sendContext` delivery capability. Screenshot bytes stay with the outer browser harness.
143
-
144
- ## Broad Mesurer/context requests mean inspect all human intent
145
-
146
- If the user says “check Mesurer,” “check Measure,” “look at Mesurer context,” “see what I highlighted/moved/annotated,” 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()`.
147
-
148
- Start with a non-destructive inventory:
63
+ For a broad request such as “check Mesurer,” collect all relevant state before editing source:
149
64
 
150
65
  ```js
151
66
  const capabilities = window.__MESURER__.capabilities().capabilities
@@ -154,6 +69,9 @@ const annotations = await window.__MESURER__.annotations()
154
69
  const arrangements = capabilities.arrange
155
70
  ? await window.__MESURER__.arrangements()
156
71
  : []
72
+ const textEdits = capabilities.textEdit
73
+ ? await window.__MESURER__.textEdits()
74
+ : []
157
75
 
158
76
  let selection = null
159
77
  try {
@@ -161,7 +79,7 @@ try {
161
79
  } catch {}
162
80
  ```
163
81
 
164
- Then resolve the saved human intent that is relevant to the task:
82
+ Resolve relevant records before HMR can replace their DOM targets:
165
83
 
166
84
  ```js
167
85
  const annotationContexts = await Promise.all(
@@ -173,58 +91,41 @@ const annotationContexts = await Promise.all(
173
91
  const arrangeIntents = await Promise.all(
174
92
  arrangements.map((intent) => window.__MESURER__.arrange(intent.id)),
175
93
  )
176
- ```
177
94
 
178
- Bring the useful pieces together before editing: target-bound notes, current selection, Arrange Before/Desired geometry, guides, measurements, held distances, exact target inspection, layout/style data, rulers/X-ray state, and any existing human screenshot preview that must be preserved.
95
+ const textEditIntents = await Promise.all(
96
+ textEdits.map((intent) => window.__MESURER__.textEdit(intent.id)),
97
+ )
98
+ ```
179
99
 
180
- The user may have selected one element, annotated another, moved a group with Arrange, and left measurements/guides that explain the relationship. Those are not separate conversations. They are one encoded visual request. Do not ask the person to repeat information Mesurer already contains, and do not overwrite one channel before reading the others.
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.
181
101
 
182
- ## Arrange has highest human-intent precedence
102
+ ## Select exact targets
183
103
 
184
- 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:
185
105
 
186
106
  ```js
187
- const arrangements = await window.__MESURER__.arrangements()
188
- const intent = await window.__MESURER__.arrange(arrangeId)
107
+ const context = await window.__MESURER__.select([
108
+ "#pricing-card",
109
+ "#pricing-cta",
110
+ ])
189
111
  ```
190
112
 
191
- Each intent contains exact target identity plus Before and Desired geometry.
113
+ Each selector must resolve to exactly one target. Missing or ambiguous selectors throw rather than binding to a guess.
192
114
 
193
- Desired is a **visual specification**, not an implementation prescription. If a target moved `96px` right, do not blindly write `transform: translateX(96px)` into production CSS. Inspect the rendered layout and implement the appropriate flex/grid alignment, gap, margin, sizing, ordering, component structure, or other semantic source change.
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.
194
116
 
195
- ### Capture Before and Desired before source edits
117
+ ## Arrange intent
196
118
 
197
- When screenshot comparison will help, reconstruct and capture both states before HMR can change the baseline:
119
+ Arrange expresses requested geometry, not source implementation.
198
120
 
199
121
  ```js
200
- await window.__MESURER__.showArrange(arrangeId, "before")
201
- const beforePlan = await window.__MESURER__.arrangeCapturePlan(arrangeId, "before")
202
-
203
- await window.__MESURER__.prepareCapture()
204
- try {
205
- // outer harness captures Before with beforePlan
206
- } finally {
207
- await window.__MESURER__.finishCapture()
208
- }
209
-
210
- await window.__MESURER__.showArrange(arrangeId, "desired")
211
- const desiredPlan = await window.__MESURER__.arrangeCapturePlan(arrangeId, "desired")
212
-
213
- await window.__MESURER__.prepareCapture()
214
- try {
215
- // outer harness captures Desired with desiredPlan
216
- } finally {
217
- await window.__MESURER__.finishCapture()
218
- }
122
+ const intents = await window.__MESURER__.arrangements()
123
+ const intent = await window.__MESURER__.arrange(arrangeId)
219
124
  ```
220
125
 
221
- The user should not need to export, attach, or send these screenshots manually. The harness already owns its screenshot primitive; Mesurer supplies the reproducible state and exact capture geometry.
222
-
223
- For source-mounted integrations that use this capture workflow, mount `contextPlugin()` together with `arrangePlugin()` so the generic capture preparation methods are available.
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.
224
127
 
225
- ### Verify the source-produced result
226
-
227
- After editing source:
128
+ After source changes:
228
129
 
229
130
  ```js
230
131
  await window.__MESURER__.stable()
@@ -232,265 +133,90 @@ await window.__MESURER__.showArrange(arrangeId, "live")
232
133
  const review = await window.__MESURER__.reviewArrange(arrangeId)
233
134
  ```
234
135
 
235
- Live removes Arrange's temporary preview. `reviewArrange()` compares the real application layout with Desired and returns exact rectangle deltas plus `connected`, `partial`, or `stale` target status.
236
-
237
- ```text
238
- Before Desired Live
239
- x 120 → x 284 x 276
240
- remaining +8px
241
-
242
- edit source
243
-
244
- Live x 284
245
-
246
- matched ✓
247
- ```
248
-
249
- If review is still numerically wrong, continue editing. If it is `stale` or `partial`, do not silently bind the intent to a different element.
250
-
251
- ## Context acquisition after Arrange preservation
252
-
253
- Once any relevant Arrange intent is retained, use this order for ordinary context.
254
-
255
- ### Existing human selection or annotation
256
-
257
- ```js
258
- const workspace = await window.__MESURER__.context()
259
- const annotations = await window.__MESURER__.annotations()
260
-
261
- let selection = null
262
- try {
263
- selection = await window.__MESURER__.context({ scope: "selection" })
264
- } catch {}
265
- ```
136
+ Live removes the temporary Arrange preview before measuring source output.
266
137
 
267
- For a relevant annotation:
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.
268
139
 
269
- ```js
270
- const context = await window.__MESURER__.context({
271
- annotation: annotation.id,
272
- })
273
- ```
140
+ See [Arrange](../../docs/ARRANGE.md).
274
141
 
275
- Do not overwrite a meaningful human selection until its context has been retained by the current task.
142
+ ## Text and Typography intent
276
143
 
277
- ### Ambiguous target
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`.
278
145
 
279
- 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:
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.
280
147
 
281
- ```js
282
- const context = await window.__MESURER__.context({ scope: "selection" })
283
- ```
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.
284
149
 
285
- Do not guess merely to avoid asking for a selection.
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.
286
151
 
287
- ### Agent knows the exact target
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.
288
153
 
289
- Use `select()` and consume its return value:
154
+ Read durable intent through:
290
155
 
291
156
  ```js
292
- const context = await window.__MESURER__.select([
293
- "#pricing-card",
294
- "#pricing-cta",
295
- ])
157
+ const edits = await window.__MESURER__.textEdits()
158
+ const intent = await window.__MESURER__.textEdit(textEditId)
296
159
  ```
297
160
 
298
- `select()` switches Mesurer to Select, visibly highlights the exact rendered targets, makes them the live selection, waits for the selection to settle, and returns selection-scoped `MesurerContextV1`.
299
-
300
- Every selector must resolve to exactly one page target. Invalid, missing, or ambiguous selectors throw.
301
-
302
- ## What context contains
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.
303
162
 
304
- `MesurerContextV1` is JSON-safe and uses `viewport-css-px` coordinates:
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.
305
164
 
306
- ```text
307
- schema / id / createdAt
308
- scope
309
- page
310
- viewport / DPR / scroll
311
- coordinateSpace
312
- regions
313
- visualState
314
- rulersVisible
315
- xrayVisible
316
- targets[]
317
- ref
318
- inspection.selector
319
- inspection.rect
320
- margin / padding / border
321
- typography
322
- appearance
323
- layout
324
- scroll / overflow
325
- visualContext
326
- guides[]
327
- measurements[]
328
- distances[]
329
- ```
330
-
331
- Prefer rendered numbers over screenshot estimates or source assumptions.
165
+ See [Direct text editing and Typography](../../docs/TEXT_EDITING.md).
332
166
 
333
- ## Multi-selection is relational
167
+ ## Annotation and context review
334
168
 
335
- When several targets are selected, consume each target plus the useful relationships between them. Start with `visualContext.distances`; for a pair not represented there:
169
+ A saved annotation carries target-bound intent and an immutable baseline:
336
170
 
337
171
  ```js
338
- const pair = window.__MESURER__.distance(selectorA, selectorB)
172
+ const context = await window.__MESURER__.context({ annotation: annotationId })
339
173
  ```
340
174
 
341
- A useful result can be:
342
-
343
- ```text
344
- Card A width: 320px
345
- Card B width: 320px
346
- A → B horizontal gap: 24px
347
- A/B top-edge delta: 0px
348
- ```
349
-
350
- For large repeated sets, focus on adjacent or user-relevant relationships rather than dumping all O(n²) pairs.
351
-
352
- ## Fresh evidence is required after visual edits
353
-
354
- Wait for the actual rendered page:
175
+ After source changes:
355
176
 
356
177
  ```js
357
178
  await window.__MESURER__.stable()
358
- ```
359
-
360
- Then use the strongest applicable path.
361
-
362
- Arrange:
363
-
364
- ```js
365
- await window.__MESURER__.showArrange(arrangeId, "live")
366
- const review = await window.__MESURER__.reviewArrange(arrangeId)
367
- ```
368
-
369
- Annotation:
370
-
371
- ```js
372
179
  const review = await window.__MESURER__.review(annotationId)
373
180
  ```
374
181
 
375
- Still-relevant human selection:
182
+ `review()` can report exact geometry/evidence changes without any external message transport.
376
183
 
377
- ```js
378
- const after = await window.__MESURER__.context({ scope: "selection" })
379
- ```
184
+ ## Screenshots
380
185
 
381
- Agent-known changed targets:
186
+ For ordinary coding-agent evidence, the outer harness owns screenshot bytes while Mesurer prepares capture presentation:
382
187
 
383
188
  ```js
384
- const after = await window.__MESURER__.select([
385
- changedSelectorA,
386
- changedSelectorB,
387
- ])
388
- ```
389
-
390
- For meaningful visual work, lint/typecheck/tests/build are not enough. They validate implementation mechanics, not the rendered result.
391
-
392
- ## Screenshot ownership
393
-
394
- Mesurer has two different screenshot roles.
395
-
396
- ### Human camera tool
397
-
398
- The optional `mesurer.screenshot` plugin gives the person a camera tool:
399
-
400
- ```ts
401
- import { mountMesurer } from "mesurer-solid"
402
- import { screenshotPlugin } from "mesurer-solid/screenshot"
403
-
404
- const mesurer = mountMesurer({
405
- plugins: [screenshotPlugin()],
406
- })
407
- ```
408
-
409
- 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.
410
-
411
- ### Agent screenshot evidence
412
-
413
- The outer coding harness owns screenshot bytes so it controls the exact browser, viewport, timing, and artifact destination. Mesurer supplies exact geometry and clean capture presentation:
414
-
415
- ```js
416
- const plan = await window.__MESURER__.capturePlan({ scope: "selection" })
417
-
189
+ const plan = await window.__MESURER__.capturePlan({ annotation: annotationId })
418
190
  await window.__MESURER__.prepareCapture()
419
191
  try {
420
- // use the harness's real screenshot primitive
192
+ // harness screenshot
421
193
  } finally {
422
194
  await window.__MESURER__.finishCapture()
423
195
  }
424
196
  ```
425
197
 
426
- Arrange uses the same ownership model through `arrangeCapturePlan()` while adding explicit Before/Desired/Live presentation.
427
-
428
- Use both signals:
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.
429
199
 
430
- ```text
431
- Mesurer context/review → geometry, box model, styles, distances, overflow
432
- real screenshot → composition, hierarchy, clipping, color, visual judgment
433
- ```
200
+ ## Revalidate after source edits
434
201
 
435
- ## Source-mounted usage
436
-
437
- For context plus Arrange:
438
-
439
- ```ts
440
- import {
441
- contextPlugin,
442
- mountMesurer,
443
- } from "mesurer-solid"
444
- import { arrangePlugin } from "mesurer-solid/arrange"
445
-
446
- const mesurer = mountMesurer({
447
- agent: true,
448
- plugins: [
449
- contextPlugin(),
450
- arrangePlugin(),
451
- ],
452
- })
453
- ```
454
-
455
- The same API is available on `mesurer.agent` and, when configured, `window.__MESURER__`.
456
-
457
- Arrange remains optional. Applications that only want the base inspector or context workflow do not pay for the Arrange entry-point bundle.
458
-
459
- ## Low-level inspection
460
-
461
- These remain useful for focused queries:
202
+ After HMR or reload settles:
462
203
 
463
204
  ```js
464
- window.__MESURER__.inspect(".selector")
465
- window.__MESURER__.inspectAll(".selector")
466
- window.__MESURER__.at(x, y)
467
- window.__MESURER__.distance(".a", ".b")
468
- window.__MESURER__.viewport()
469
- await window.__MESURER__.feedback([".selector"])
470
- await window.__MESURER__.state()
471
205
  await window.__MESURER__.stable()
472
206
  ```
473
207
 
474
- Prefer `arrangements()`, `context()`, `select()`, `reviewArrange()`, and `review()` for visual development because those paths preserve human meaning and return evidence the agent can reason from directly.
208
+ Then compare the same evidence retained before editing:
475
209
 
476
- ## Completion rule
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()`.
477
215
 
478
- A good harness-level loop is:
216
+ Do not clear human history merely to expose Live state, and do not reinject just to refresh context.
479
217
 
480
- ```text
481
- 1. discover/reuse Mesurer and preserve human state
482
- 2. if the request is broad, inventory Arrange + annotations + selection + workspace context before narrowing
483
- 3. consume relevant Arrange intent first
484
- 4. capture Before/Desired when useful, before source edits
485
- 5. consume existing annotation/selection context
486
- 6. resolve exact targets; ask only when genuinely ambiguous
487
- 7. edit normal source
488
- 8. wait for stable render
489
- 9. switch Arrange to Live when applicable
490
- 10. get fresh Arrange review/context and optional screenshot
491
- 11. iterate until rendered evidence supports completion
492
- ```
218
+ ## Completion rule
493
219
 
494
- Do not clear Arrange history, alter human measurements/guides, replace live Mesurer state, or use a temporary preview to make unfinished source work appear correct.
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.
495
221
 
496
- **Rendered evidence is the output of the Mesurer step, not an optional side effect.**
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.