mesurer-solid 0.1.0 → 0.1.1-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 +48 -9
- package/README.md +58 -7
- package/dist/context-plugin.d.ts +2 -1
- package/dist/core.d.ts +31 -4
- package/dist/core.js +19 -3
- package/dist/index.js +795 -668
- package/dist/inject-script.js +12 -6
- package/dist/inject.d.ts +4 -1
- package/dist/inject.js +1747 -876
- package/dist/screenshot.d.ts +62 -0
- package/dist/screenshot.js +2890 -0
- package/package.json +5 -1
- package/skills/mesurer-ui/SKILL.md +37 -7
package/AGENT_INTEGRATION.md
CHANGED
|
@@ -28,10 +28,14 @@ The page is the shared state boundary. Mesurer never needs to know which chat, t
|
|
|
28
28
|
|
|
29
29
|
## Install the portable Agent Skill
|
|
30
30
|
|
|
31
|
+
Use the stable package by default:
|
|
32
|
+
|
|
31
33
|
```bash
|
|
32
|
-
npx --yes --package=mesurer-solid
|
|
34
|
+
npx --yes --package=mesurer-solid mesurer-skill install
|
|
33
35
|
```
|
|
34
36
|
|
|
37
|
+
Use `mesurer-solid@beta` only when intentionally validating a prerelease.
|
|
38
|
+
|
|
35
39
|
The installed skill is self-contained:
|
|
36
40
|
|
|
37
41
|
```text
|
|
@@ -41,7 +45,7 @@ The installed skill is self-contained:
|
|
|
41
45
|
└── inject-script.js
|
|
42
46
|
```
|
|
43
47
|
|
|
44
|
-
The skill defines the same context-first workflow described here.
|
|
48
|
+
The skill defines the same context-first workflow described here, including the distinction between Mesurer's optional human screenshot plugin and screenshots owned by the agent's outer browser harness.
|
|
45
49
|
|
|
46
50
|
## Reuse a live human instance first
|
|
47
51
|
|
|
@@ -58,7 +62,7 @@ if (hasMesurer) {
|
|
|
58
62
|
}
|
|
59
63
|
```
|
|
60
64
|
|
|
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
|
|
65
|
+
If Mesurer exists, use that exact instance. The person may already have selected elements, placed guides, measured gaps, held distances, enabled rulers/X-ray, saved annotations, or kept a screenshot preview open. Read and preserve that state before changing it.
|
|
62
66
|
|
|
63
67
|
The injector also reuses a live injected instance by default. Deliberate destructive replacement requires:
|
|
64
68
|
|
|
@@ -97,6 +101,14 @@ await browser.evaluate(() => window.__MESURER__.ready())
|
|
|
97
101
|
|
|
98
102
|
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
103
|
|
|
104
|
+
Normal injection keeps screenshot capture disabled unless requested:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
window.__MESURER_CONFIG__ = { screenshot: true }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The first-party Chrome extension enables the screenshot plugin automatically because its human-facing camera tool can use the extension's visible-tab capture bridge.
|
|
111
|
+
|
|
100
112
|
## Capability contract
|
|
101
113
|
|
|
102
114
|
After `ready()`:
|
|
@@ -123,7 +135,7 @@ Copy Selection
|
|
|
123
135
|
Add Note
|
|
124
136
|
```
|
|
125
137
|
|
|
126
|
-
There is no `send`, `screenshots`, or `sendContext` delivery capability.
|
|
138
|
+
There is no `send`, `screenshots`, or `sendContext` **delivery capability**. The optional `mesurer.screenshot` plugin is a separate human capture tool/service and does not add image delivery to the context API.
|
|
127
139
|
|
|
128
140
|
## Context acquisition precedence
|
|
129
141
|
|
|
@@ -326,9 +338,32 @@ expected target/guide/measurement missing
|
|
|
326
338
|
|
|
327
339
|
If the requested result remains numerically wrong, continue editing.
|
|
328
340
|
|
|
329
|
-
##
|
|
341
|
+
## Human screenshot plugin vs agent screenshot evidence
|
|
342
|
+
|
|
343
|
+
Mesurer has two intentionally different screenshot paths.
|
|
344
|
+
|
|
345
|
+
### Human capture tool
|
|
346
|
+
|
|
347
|
+
The optional `mesurer.screenshot` plugin gives the person a camera tool inside Mesurer:
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
import { mountMeasurer } from "mesurer-solid"
|
|
351
|
+
import { screenshotPlugin } from "mesurer-solid/screenshot"
|
|
352
|
+
|
|
353
|
+
const mesurer = mountMeasurer({
|
|
354
|
+
plugins: [screenshotPlugin()],
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The user can drag a viewport region, capture a real HiDPI-aware PNG, optionally copy/download it, keep a persistent draggable thumbnail, and open a larger Copy/Save viewer. Normal browser hosts use `getDisplayMedia()`; the first-party Chrome extension uses `chrome.tabs.captureVisibleTab()` through its isolated-world bridge and therefore avoids the screen-share chooser.
|
|
330
359
|
|
|
331
|
-
|
|
360
|
+
For advanced mounted integrations, the typed `MesurerScreenshotService` is available from the plugin host under service id `screenshot`. It is not part of `window.__MESURER__`'s context/delivery capability surface.
|
|
361
|
+
|
|
362
|
+
Agents should preserve an existing human screenshot preview unless the task explicitly asks them to test, close, replace, or otherwise manipulate the screenshot feature.
|
|
363
|
+
|
|
364
|
+
### Agent verification screenshot
|
|
365
|
+
|
|
366
|
+
For coding-agent verification, the outer harness should normally continue to own screenshot bytes so the task can control the exact browser, viewport, timing, and artifact destination. Mesurer supplies clean capture scope/presentation:
|
|
332
367
|
|
|
333
368
|
```js
|
|
334
369
|
const plan = await window.__MESURER__.capturePlan({ scope: "selection" })
|
|
@@ -348,6 +383,8 @@ Mesurer context → exact geometry, box model, styles, distances, overflow
|
|
|
348
383
|
real screenshot → composition, hierarchy, clipping, color, visual judgment
|
|
349
384
|
```
|
|
350
385
|
|
|
386
|
+
Do not replace exact Mesurer measurements with pixel estimates from either screenshot path.
|
|
387
|
+
|
|
351
388
|
## Source-mounted usage
|
|
352
389
|
|
|
353
390
|
When Mesurer is intentionally mounted from application code:
|
|
@@ -367,7 +404,7 @@ The same API is available on `mesurer.agent` and, when configured, `window.__MES
|
|
|
367
404
|
const context = await mesurer.agent.select("#target")
|
|
368
405
|
```
|
|
369
406
|
|
|
370
|
-
No transport callback is involved.
|
|
407
|
+
Screenshot capture can be composed independently with `screenshotPlugin()` when the host wants the human camera tool. No transport callback is involved.
|
|
371
408
|
|
|
372
409
|
## Low-level inspection
|
|
373
410
|
|
|
@@ -391,7 +428,7 @@ Prefer `context()`, `select()`, and `review()` for visual development because th
|
|
|
391
428
|
A good harness-level visual completion loop is:
|
|
392
429
|
|
|
393
430
|
```text
|
|
394
|
-
1. discover/reuse Mesurer
|
|
431
|
+
1. discover/reuse Mesurer and preserve human state
|
|
395
432
|
2. consume existing human context
|
|
396
433
|
3. if target ambiguous, ask user to select
|
|
397
434
|
4. otherwise select known affected rendered target(s) when needed
|
|
@@ -399,8 +436,10 @@ A good harness-level visual completion loop is:
|
|
|
399
436
|
6. edit normal source
|
|
400
437
|
7. wait for stable render
|
|
401
438
|
8. get fresh review/context; use select() for known changed targets
|
|
402
|
-
9. optionally capture real screenshot
|
|
439
|
+
9. optionally capture a real screenshot through the outer harness
|
|
403
440
|
10. iterate until rendered evidence supports the claim
|
|
404
441
|
```
|
|
405
442
|
|
|
443
|
+
When the task is specifically testing Mesurer's screenshot plugin, exercise its camera/preview/viewer path as the feature under test; otherwise do not substitute it for the harness's normal screenshot primitive.
|
|
444
|
+
|
|
406
445
|
**Context is the output of the Mesurer step, not an optional side effect.**
|
package/README.md
CHANGED
|
@@ -6,13 +6,23 @@ The renderer is implemented privately in Solid 2, but consumers can use Solid 1/
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Stable releases use the `latest` dist-tag:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add -d mesurer-solid
|
|
13
|
+
# or
|
|
14
|
+
npm install -D mesurer-solid
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use the `beta` tag only when you intentionally want to test a prerelease:
|
|
18
|
+
|
|
9
19
|
```bash
|
|
10
20
|
bun add -d mesurer-solid@beta
|
|
11
21
|
# or
|
|
12
22
|
npm install -D mesurer-solid@beta
|
|
13
23
|
```
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
Prereleases through `0.1.0-beta.11` used the old scoped package name; current releases use `mesurer-solid`.
|
|
16
26
|
|
|
17
27
|
## Mount the base inspector
|
|
18
28
|
|
|
@@ -108,6 +118,14 @@ Injection installs `contextPlugin()` by default. To deliberately inject only the
|
|
|
108
118
|
window.__MESURER_CONFIG__ = { context: false }
|
|
109
119
|
```
|
|
110
120
|
|
|
121
|
+
Screenshot capture remains opt-in for normal injection:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
window.__MESURER_CONFIG__ = { screenshot: true }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The first-party Chrome extension enables screenshot capture automatically.
|
|
128
|
+
|
|
111
129
|
## Direct context API
|
|
112
130
|
|
|
113
131
|
With the context plugin loaded:
|
|
@@ -248,9 +266,41 @@ const after = await window.__MESURER__.select([
|
|
|
248
266
|
|
|
249
267
|
For meaningful visual work, fresh Mesurer context/review is part of completion. Lint, typecheck, tests, and build are implementation checks, not rendered proof.
|
|
250
268
|
|
|
251
|
-
##
|
|
269
|
+
## Optional screenshot plugin
|
|
270
|
+
|
|
271
|
+
Screenshot capture is a removable first-party plugin instead of permanent core state:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { mountMeasurer } from "mesurer-solid"
|
|
275
|
+
import { screenshotPlugin } from "mesurer-solid/screenshot"
|
|
276
|
+
|
|
277
|
+
const mesurer = mountMeasurer({
|
|
278
|
+
plugins: [
|
|
279
|
+
screenshotPlugin({
|
|
280
|
+
copy: true,
|
|
281
|
+
download: false,
|
|
282
|
+
}),
|
|
283
|
+
],
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The camera tool lets the user drag a viewport region. Mesurer captures the real visible page, converts CSS viewport coordinates to the captured bitmap scale so Retina/HiDPI crops remain exact, temporarily hides its control chrome from the pixels, then restores the previous inspector presentation.
|
|
288
|
+
|
|
289
|
+
A successful capture can automatically copy PNG data to the clipboard and/or download a PNG according to persistent plugin settings. Those output operations are best-effort: if clipboard or download access is unavailable, the captured PNG is still kept for preview/viewer use and Mesurer reports the available result instead of discarding it.
|
|
290
|
+
|
|
291
|
+
When `contextPlugin()` and/or `screenshotPlugin()` are mounted, Settings → General → Plugins exposes compact persisted controls for the human-facing plugin surface. Context can hide or restore its toolbar/annotation controls without removing the typed context service. Screenshot can hide or restore the camera tool and control automatic copy, automatic download, and whether measurement presentation is included in captured pixels.
|
|
292
|
+
|
|
293
|
+
After capture, Mesurer shows a persistent draggable thumbnail. A new thumbnail starts in the bottom-right with an 8px viewport inset. Dragging preserves the existing viewport-clamping behavior, so the preview stays inside that safe boundary. The thumbnail can be dismissed, dragged around the viewport, right-clicked with the browser's native image context menu, or clicked to open a larger viewer. The viewer preserves native image right-click behavior and adds explicit Copy, Save, and Close controls. Escape or backdrop click closes the viewer without discarding the thumbnail. A short status message confirms whether the screenshot was copied, saved, captured, or could not complete an optional output.
|
|
294
|
+
|
|
295
|
+
Normal browser hosts use `getDisplayMedia()` and reuse a live capture stream to avoid prompting for every region. The first-party Chrome extension uses `chrome.tabs.captureVisibleTab()` through its isolated-world extension bridge, so its screenshot path does not open the screen-share chooser and does not require a broad `<all_urls>` permission.
|
|
296
|
+
|
|
297
|
+
Programmatic mounted users can get the typed `MesurerScreenshotService` from the plugin host with service id `screenshot`. `start()` opens region selection, `cancel()` closes it, `capture(rect)` captures an exact CSS-pixel viewport rectangle, `settings()` reads copy/download preferences, and `setSettings()` updates those persistent preferences.
|
|
298
|
+
|
|
299
|
+
Screenshot does not claim the global `C` shortcut because the context workflow already uses `C` and `Shift+C`.
|
|
300
|
+
|
|
301
|
+
## Clean screenshot evidence for agents
|
|
252
302
|
|
|
253
|
-
|
|
303
|
+
The optional screenshot plugin is a human capture tool, not an agent-delivery channel. Agent verification can continue to let the existing browser harness own deterministic task screenshots while Mesurer plans a clean evidence frame:
|
|
254
304
|
|
|
255
305
|
```js
|
|
256
306
|
const plan = await window.__MESURER__.capturePlan({ scope: "selection" })
|
|
@@ -262,7 +312,7 @@ try {
|
|
|
262
312
|
}
|
|
263
313
|
```
|
|
264
314
|
|
|
265
|
-
Use screenshots for visual composition and Mesurer context for exact numeric claims.
|
|
315
|
+
Use screenshots for visual composition and Mesurer context for exact numeric claims. A screenshot plugin image does not create a `screenshots` delivery capability on `window.__MESURER__` and does not replace context/select/review evidence.
|
|
266
316
|
|
|
267
317
|
## Context UI
|
|
268
318
|
|
|
@@ -279,7 +329,7 @@ Those remain the three human context controls. `select()` is a programmatic agen
|
|
|
279
329
|
## Portable Agent Skill
|
|
280
330
|
|
|
281
331
|
```bash
|
|
282
|
-
npx --yes --package=mesurer-solid
|
|
332
|
+
npx --yes --package=mesurer-solid mesurer-skill install
|
|
283
333
|
```
|
|
284
334
|
|
|
285
335
|
The installer leaves:
|
|
@@ -291,7 +341,7 @@ The installer leaves:
|
|
|
291
341
|
└── inject-script.js
|
|
292
342
|
```
|
|
293
343
|
|
|
294
|
-
The skill teaches the context-first workflow, including when to consume human selection, when to ask for a selection, when to self-select changed targets, multi-selection reads,
|
|
344
|
+
The skill teaches the context-first workflow, including when to consume human selection, when to ask for a selection, when to self-select changed targets, multi-selection reads, fresh post-edit verification, and the distinction between the optional human screenshot plugin and harness-owned agent screenshot evidence. See [`AGENT_INTEGRATION.md`](./AGENT_INTEGRATION.md).
|
|
295
345
|
|
|
296
346
|
## Low-level agent API
|
|
297
347
|
|
|
@@ -316,13 +366,14 @@ import {
|
|
|
316
366
|
} from "mesurer-solid/core"
|
|
317
367
|
```
|
|
318
368
|
|
|
319
|
-
Plugins can contribute tools, commands, hooks, overlays, settings, state, services, history/persistence, renderer-owned UI, and lifecycle cleanup.
|
|
369
|
+
Plugins can contribute tools, commands, hooks, overlays, settings, state, services, history/persistence, renderer-owned UI, and lifecycle cleanup. Plugin-defined toggle settings can register through the plugin host and appear in the canonical General → Plugins Settings section without hard-coding the plugin into Mesurer's renderer.
|
|
320
370
|
|
|
321
371
|
## Public surface
|
|
322
372
|
|
|
323
373
|
```text
|
|
324
374
|
mesurer-solid
|
|
325
375
|
mesurer-solid/core
|
|
376
|
+
mesurer-solid/screenshot
|
|
326
377
|
mesurer-solid/inject
|
|
327
378
|
mesurer-solid/inject-script
|
|
328
379
|
```
|
package/dist/context-plugin.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ import type { MesurerPlugin } from "./core";
|
|
|
2
2
|
import { type MesurerAnnotation, type MesurerCapturePlanV1, type MesurerContextRequest, type MesurerContextV1, type MesurerReviewV1 } from "./context";
|
|
3
3
|
export declare const MESURER_CONTEXT_PLUGIN_ID = "mesurer.context";
|
|
4
4
|
export declare const MESURER_CONTEXT_SERVICE_ID = "context:v1";
|
|
5
|
+
export declare const MESURER_CONTEXT_SETTINGS_STATE_ID = "mesurer.context.settings";
|
|
5
6
|
export type MesurerContextPluginOptions = {
|
|
6
|
-
/**
|
|
7
|
+
/** Initial human-facing Context UI state. The Settings toggle can change it at runtime. Defaults to true. */
|
|
7
8
|
ui?: boolean;
|
|
8
9
|
};
|
|
9
10
|
export type MesurerContextService = {
|
package/dist/core.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export type Registration = {
|
|
|
2
2
|
readonly dispose: () => void;
|
|
3
3
|
};
|
|
4
4
|
export type PluginId = string;
|
|
5
|
-
type PluginScalar = string | number | boolean | null;
|
|
6
|
-
type PluginValue = PluginScalar | PluginValue[] | {
|
|
5
|
+
export type PluginScalar = string | number | boolean | null;
|
|
6
|
+
export type PluginValue = PluginScalar | PluginValue[] | {
|
|
7
7
|
[key: string]: PluginValue;
|
|
8
8
|
};
|
|
9
9
|
export type PluginStateSnapshot = {
|
|
@@ -23,12 +23,39 @@ export type ToolContribution = {
|
|
|
23
23
|
};
|
|
24
24
|
active?: () => boolean;
|
|
25
25
|
disabled?: () => boolean;
|
|
26
|
+
hidden?: () => boolean;
|
|
26
27
|
};
|
|
28
|
+
export type SettingsToggleContribution = {
|
|
29
|
+
type: "toggle";
|
|
30
|
+
id: string;
|
|
31
|
+
label: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
value(): boolean;
|
|
34
|
+
set(value: boolean): void | Promise<void>;
|
|
35
|
+
disabled?: () => boolean;
|
|
36
|
+
};
|
|
37
|
+
export type SettingsControlContribution = SettingsToggleContribution;
|
|
27
38
|
export type SettingsContribution = {
|
|
28
39
|
id: string;
|
|
29
40
|
label: string;
|
|
30
41
|
order?: number;
|
|
31
42
|
builtin?: string;
|
|
43
|
+
controls?: SettingsControlContribution[];
|
|
44
|
+
};
|
|
45
|
+
export type SettingsControlDescription = {
|
|
46
|
+
type: "toggle";
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
description: string | undefined;
|
|
50
|
+
value: boolean;
|
|
51
|
+
disabled: boolean;
|
|
52
|
+
};
|
|
53
|
+
export type SettingsDescription = {
|
|
54
|
+
id: string;
|
|
55
|
+
label: string;
|
|
56
|
+
order?: number;
|
|
57
|
+
builtin?: string;
|
|
58
|
+
controls: SettingsControlDescription[];
|
|
32
59
|
};
|
|
33
60
|
export type OverlayContribution = {
|
|
34
61
|
id: string;
|
|
@@ -50,6 +77,7 @@ export type MesurerPluginContext = {
|
|
|
50
77
|
register<T extends PluginValue>(definition: StateSliceDefinition<T>): Registration;
|
|
51
78
|
get<T extends PluginValue>(id: string): T | undefined;
|
|
52
79
|
update<T extends PluginValue>(id: string, update: (value: T) => T): void;
|
|
80
|
+
subscribe(listener: () => void): Registration;
|
|
53
81
|
};
|
|
54
82
|
tool: {
|
|
55
83
|
register(contribution: ToolContribution): Registration;
|
|
@@ -98,7 +126,7 @@ export type MesurerPluginDescription = {
|
|
|
98
126
|
order?: number;
|
|
99
127
|
builtin?: string;
|
|
100
128
|
}>;
|
|
101
|
-
settings:
|
|
129
|
+
settings: SettingsDescription[];
|
|
102
130
|
overlays: OverlayContribution[];
|
|
103
131
|
state: Array<{
|
|
104
132
|
id: string;
|
|
@@ -151,4 +179,3 @@ export declare function createMesurerPluginHost(): MesurerPluginHost;
|
|
|
151
179
|
export declare function createMesurerRuntime(options?: {
|
|
152
180
|
plugins?: MesurerPlugin[];
|
|
153
181
|
}): Promise<MesurerPluginHost>;
|
|
154
|
-
export {};
|
package/dist/core.js
CHANGED
|
@@ -125,7 +125,13 @@ function i() {
|
|
|
125
125
|
return p.has(t.id) || p.set(t.id, t.initial), r;
|
|
126
126
|
},
|
|
127
127
|
get: k.get,
|
|
128
|
-
update: k.update
|
|
128
|
+
update: k.update,
|
|
129
|
+
subscribe(e) {
|
|
130
|
+
let t = i.on("changed", (t) => {
|
|
131
|
+
(t.reason === "state" || t.reason === "history") && e();
|
|
132
|
+
});
|
|
133
|
+
return n({ dispose: t });
|
|
134
|
+
}
|
|
129
135
|
},
|
|
130
136
|
tool: { register: (t) => n(_(o, e, t)) },
|
|
131
137
|
settings: { register: (t) => n(_(s, e, t)) },
|
|
@@ -203,8 +209,18 @@ function i() {
|
|
|
203
209
|
requires: e.requires ?? [],
|
|
204
210
|
provides: e.provides ?? []
|
|
205
211
|
})),
|
|
206
|
-
tools: I.tools().map(({ active: e, disabled: t,
|
|
207
|
-
settings: I.settings(),
|
|
212
|
+
tools: I.tools().map(({ active: e, disabled: t, hidden: n, icon: r, ...i }) => i),
|
|
213
|
+
settings: I.settings().map(({ controls: e = [], ...t }) => ({
|
|
214
|
+
...t,
|
|
215
|
+
controls: e.map((e) => ({
|
|
216
|
+
type: e.type,
|
|
217
|
+
id: e.id,
|
|
218
|
+
label: e.label,
|
|
219
|
+
description: e.description,
|
|
220
|
+
value: e.value(),
|
|
221
|
+
disabled: e.disabled?.() ?? !1
|
|
222
|
+
}))
|
|
223
|
+
})),
|
|
208
224
|
overlays: I.overlays(),
|
|
209
225
|
state: [...f.values()].map(({ id: e, history: t, persist: n }) => ({
|
|
210
226
|
id: e,
|