mesurer-solid 0.1.0-beta.11
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 +189 -0
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/THIRD_PARTY_LICENSES.md +29 -0
- package/dist/agent.d.ts +113 -0
- package/dist/context-contract-parity.d.ts +1 -0
- package/dist/context-plugin.d.ts +27 -0
- package/dist/context.d.ts +359 -0
- package/dist/core.d.ts +147 -0
- package/dist/core.js +226 -0
- package/dist/host-layer.d.ts +24 -0
- package/dist/index.d.ts +210 -0
- package/dist/index.js +10195 -0
- package/dist/inject-script.d.ts +1 -0
- package/dist/inject-script.js +1227 -0
- package/dist/inject.d.ts +12 -0
- package/dist/inject.js +9983 -0
- package/package.json +62 -0
- package/scripts/install-skill.mjs +31 -0
- package/skills/mesurer-ui/SKILL.md +111 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Mesurer agent integration
|
|
2
|
+
|
|
3
|
+
Mesurer uses standards and a browser contract instead of harness-specific integrations.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Agent Skill window.__MESURER__ ACP
|
|
7
|
+
how/when to use it visual context + validation standardized delivery
|
|
8
|
+
\ | /
|
|
9
|
+
\______________________|______________________/
|
|
10
|
+
|
|
|
11
|
+
any capable harness
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
There is no required OpenCode, Pi, Cursor, Codex, or other Mesurer adapter package.
|
|
15
|
+
|
|
16
|
+
## Install the portable Agent Skill
|
|
17
|
+
|
|
18
|
+
The npm package ships one canonical `mesurer-ui` Agent Skill:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx --yes --package=mesurer-solid@beta mesurer-skill install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use `--force` only when intentionally replacing an existing local copy. The install is self-contained: it writes the skill plus the exact packaged classic injector to:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
.agents/skills/mesurer-ui/
|
|
28
|
+
├── SKILL.md
|
|
29
|
+
└── assets/
|
|
30
|
+
└── inject-script.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The skill teaches agents to use Mesurer for frontend visual work, consume human annotations before editing, and revalidate the rendered result after HMR instead of treating typecheck/build success as visual completion.
|
|
34
|
+
|
|
35
|
+
## Default browser integration: inject
|
|
36
|
+
|
|
37
|
+
**Default host-project mutation budget: zero.** If the existing browser, Electron, WebView, or automation harness can execute JavaScript in the target renderer, reuse that channel.
|
|
38
|
+
|
|
39
|
+
When the Agent Skill is installed, read `.agents/skills/mesurer-ui/assets/inject-script.js` and evaluate those bytes in the page. No project dependency is required after the transient installer exits.
|
|
40
|
+
|
|
41
|
+
When `mesurer-solid` is already installed as a project/tooling dependency, the equivalent package path is the `/inject-script` export:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { readFile } from "node:fs/promises";
|
|
45
|
+
import { fileURLToPath } from "node:url";
|
|
46
|
+
|
|
47
|
+
const source = await readFile(
|
|
48
|
+
fileURLToPath(import.meta.resolve("mesurer-solid/inject-script")),
|
|
49
|
+
"utf8",
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await browser.evaluate(source);
|
|
53
|
+
await browser.evaluate(`window.__MESURER__.ready()`);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Both routes evaluate the same built injector artifact. The injection entry points install the removable `mesurer.context` plugin by default. A harness that deliberately wants only the low-level inspector can set:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
window.__MESURER_CONFIG__ = { context: false };
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Do not create another Chromium instance, another CDP connection, a Mesurer-specific server, a special application build, or source changes merely to inspect an app that the harness can already evaluate.
|
|
63
|
+
|
|
64
|
+
## Discover the browser contract
|
|
65
|
+
|
|
66
|
+
Wait for plugin setup before reading dynamic capabilities:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
if (window.__MESURER__) {
|
|
70
|
+
await window.__MESURER__.ready()
|
|
71
|
+
window.__MESURER__.capabilities()
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`capabilities().capabilities.context` reflects whether the `context:v1` plugin service is currently present. Removing `mesurer.context` switches the context/review/capture capabilities off dynamically while the original inspection API keeps working.
|
|
76
|
+
|
|
77
|
+
### Human-in-the-loop context
|
|
78
|
+
|
|
79
|
+
With the plugin loaded:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
await window.__MESURER__.annotations()
|
|
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 })
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`context()` combines the human's selected elements or dragged region and note with exact DOM inspection and relevant guides, measurements, and held distances. Scoped contexts expose their requested viewport rectangles in `regions`, so a region-only note remains useful even when no DOM element sits inside it. Transient hover/drag state is excluded.
|
|
90
|
+
|
|
91
|
+
### Revalidate after edits
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
await window.__MESURER__.stable()
|
|
95
|
+
const review = await window.__MESURER__.review(annotationId)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Annotations retain exact live DOM identity while the original node remains connected. After DOM replacement/HMR, rebinding is deliberately conservative: strong IDs are preferred, and weaker fingerprints must resolve uniquely. Ambiguous or incompatible replacements are reported stale instead of silently attaching the note to another element.
|
|
99
|
+
|
|
100
|
+
`review()` matches targets by stable annotation target IDs rather than regenerated selectors. Relevant baseline evidence that genuinely disappears is reported with `kind: "missing"` instead of being silently omitted.
|
|
101
|
+
|
|
102
|
+
### Clean screenshots
|
|
103
|
+
|
|
104
|
+
The outer harness owns real browser screenshots. The context plugin defines the evidence frame:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
const plan = await window.__MESURER__.capturePlan({ annotation: annotationId })
|
|
108
|
+
await window.__MESURER__.prepareCapture()
|
|
109
|
+
try {
|
|
110
|
+
// harness screenshot: current viewport
|
|
111
|
+
// close-up when present: plan.captures.find(c => c.id === "focus")
|
|
112
|
+
} finally {
|
|
113
|
+
await window.__MESURER__.finishCapture()
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Capture planning includes the scoped `regions`, so an arbitrary whitespace/alignment annotation can still produce a focused close-up. Capture mode hides toolbars, settings, comment editors, and action panels while preserving rulers, guides, selection/annotation markers, measurements, distance overlays, and pixel labels.
|
|
118
|
+
|
|
119
|
+
Use screenshots together with structured context. Screenshots are strong visual evidence; Mesurer geometry is stronger evidence for exact spacing/alignment claims.
|
|
120
|
+
|
|
121
|
+
## Source-mounted integrations
|
|
122
|
+
|
|
123
|
+
When Mesurer is mounted from application code, explicitly install the same plugin:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import {
|
|
127
|
+
contextPlugin,
|
|
128
|
+
mountMeasurer,
|
|
129
|
+
} from "mesurer-solid";
|
|
130
|
+
|
|
131
|
+
const mesurer = mountMeasurer({
|
|
132
|
+
agent: true,
|
|
133
|
+
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
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Remove the complete extension through the normal plugin host:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
mesurer.pluginHost?.remove("mesurer.context");
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The context UI, annotation runtime, shortcuts, service, and listeners are disposed together.
|
|
155
|
+
|
|
156
|
+
## ACP delivery
|
|
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);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The result is one context text block plus optional labeled image blocks. The calling ACP client is responsible for session selection, capability negotiation, and `session/prompt`.
|
|
167
|
+
|
|
168
|
+
## Existing low-level API
|
|
169
|
+
|
|
170
|
+
These JSON-safe primitives remain available whether or not `mesurer.context` is loaded:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
window.__MESURER__.inspect(".selector")
|
|
174
|
+
window.__MESURER__.inspectAll(".selector")
|
|
175
|
+
window.__MESURER__.at(x, y)
|
|
176
|
+
window.__MESURER__.distance(".a", ".b")
|
|
177
|
+
window.__MESURER__.viewport()
|
|
178
|
+
await window.__MESURER__.feedback([".selector"])
|
|
179
|
+
await window.__MESURER__.state()
|
|
180
|
+
await window.__MESURER__.stable()
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
When an agent is mounted with a scoped root, `inspect`, `inspectAll`, `distance`, and `at` all respect that root. A document-level hit test is never returned by `at()` unless the hit element belongs to the configured root.
|
|
184
|
+
|
|
185
|
+
Prefer scoped `context()` and `review()` for normal human-in-the-loop visual development when the context plugin is available; use the low-level primitives for narrower measurement questions.
|
|
186
|
+
|
|
187
|
+
## Ownership boundary
|
|
188
|
+
|
|
189
|
+
The base Mesurer runtime owns measurement, inspection, plugin composition, and the low-level browser API. `mesurer.context` owns annotations, context formatting/capture/review behavior, and its UI. The outer harness owns navigation, clicks, typing, screenshots, tabs/windows, authentication, browser lifetime, source editing, dev servers, and ACP session/process ownership.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jhomra21
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# mesurer-solid
|
|
2
|
+
|
|
3
|
+
Framework-agnostic UI measurement, annotation, inspection, and agent-ready visual context for browser applications.
|
|
4
|
+
|
|
5
|
+
The renderer is implemented privately in Solid 2, but consumers can use Solid 1/2, React, Vue, Svelte, vanilla DOM, or Electron renderer pages without providing Solid.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add -d mesurer-solid@beta
|
|
11
|
+
# or
|
|
12
|
+
npm install -D mesurer-solid@beta
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **Package rename:** prereleases through `0.1.0-beta.11` were published as `@jhomra21/mesurer-solid`. New releases use the canonical unscoped package name `mesurer-solid`. The API is unchanged; update dependency and import specifiers to the new name.
|
|
16
|
+
|
|
17
|
+
## Mount the base inspector
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { mountMeasurer } from "mesurer-solid";
|
|
21
|
+
|
|
22
|
+
const mesurer = mountMeasurer();
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The base inspector contains Select, X-ray, Color Picker, Rulers, Text Inspector, Guides, Distance, Settings, the plugin host, and the low-level agent inspection API.
|
|
26
|
+
|
|
27
|
+
## Enable context, copy actions, and annotations
|
|
28
|
+
|
|
29
|
+
Context and annotation features are provided by the removable `mesurer.context` plugin. Source-mounted applications opt in explicitly:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import {
|
|
33
|
+
contextPlugin,
|
|
34
|
+
mountMeasurer,
|
|
35
|
+
} from "mesurer-solid";
|
|
36
|
+
|
|
37
|
+
const mesurer = mountMeasurer({
|
|
38
|
+
agent: true,
|
|
39
|
+
plugins: [contextPlugin()],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
With the default `contextPlugin()` UI enabled, Mesurer adds these controls to the existing draggable toolbar:
|
|
44
|
+
|
|
45
|
+
| Action | Shortcut | What it does |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| Copy Context | `C` | Copies the current workspace context. |
|
|
48
|
+
| Copy Selection | `Shift+C` | Copies context scoped to the selected element(s) or dragged region. |
|
|
49
|
+
| Add Note | `N` | Creates an annotation for the current element selection or dragged region. |
|
|
50
|
+
| Send selection | `Cmd/Ctrl+Enter` | Appears only when `sendContext` is configured and sends scoped context through the host callback. |
|
|
51
|
+
|
|
52
|
+
### Annotating elements, multi-selection, and regions
|
|
53
|
+
|
|
54
|
+
For one element, select it and use the floating annotation button, **Add Note** in the toolbar, or `N`.
|
|
55
|
+
|
|
56
|
+
For multiple elements, Shift-select the elements you want to annotate. The floating annotation button starts on the first selected element and follows the selected element currently under the pointer. The composer shows the selected-element count, and the saved annotation keeps **all** selected targets in its context rather than only the element that hosted the button.
|
|
57
|
+
|
|
58
|
+
Saved annotation markers can be clicked to reopen their note panel. The note composer and saved annotation panels can both be dragged by their header, while the underlying selection remains intact. Saved panels also show how many elements the note applies to.
|
|
59
|
+
|
|
60
|
+
For an arbitrary dragged region with no element target, use **Add Note** in the toolbar or `N`. Region-only notes are fully supported even though the small floating annotation button is element-selection focused.
|
|
61
|
+
|
|
62
|
+
### Use the same context programmatically
|
|
63
|
+
|
|
64
|
+
`contextPlugin()` provides the `context:v1` service and owns annotation state, Copy Context/Copy Selection/Add Note UI, shortcuts, review/capture behavior, optional delivery callbacks, and cleanup.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const workspace = await mesurer.context();
|
|
68
|
+
const selected = await mesurer.context({ scope: "selection" });
|
|
69
|
+
const annotation = await mesurer.context({ annotation: annotationId });
|
|
70
|
+
await mesurer.copyContext({ annotation: annotationId });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A scoped context includes `regions`, the viewport rectangles the person actually selected or annotated. That keeps arbitrary-area feedback useful even when no DOM element is inside the region, and gives screenshot planning the same focus area the structured context uses.
|
|
74
|
+
|
|
75
|
+
After a source edit/HMR cycle:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const review = await mesurer.review(annotationId);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Review uses stable annotation target IDs, conservatively rebinds replaced DOM, and reports relevant baseline evidence that disappears with `kind: "missing"`.
|
|
82
|
+
|
|
83
|
+
### Context without visible UI
|
|
84
|
+
|
|
85
|
+
If a host wants the context/review APIs but not the Copy/Add Note toolbar controls or annotation UI, keep the plugin and disable only its UI:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const mesurer = mountMeasurer({
|
|
89
|
+
agent: true,
|
|
90
|
+
plugins: [contextPlugin({ ui: false })],
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Remove the complete feature through the same plugin host used by every extension:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
mesurer.pluginHost?.remove("mesurer.context");
|
|
98
|
+
console.log(mesurer.agent.capabilities().capabilities.context); // false
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The mounted/browser convenience methods resolve `context:v1`; they do not maintain a second hidden context implementation.
|
|
102
|
+
|
|
103
|
+
## Coding-agent browser API
|
|
104
|
+
|
|
105
|
+
With `agent: true` and the context plugin loaded, wait for plugin initialization before reading dynamic capabilities:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
await window.__MESURER__.ready()
|
|
109
|
+
window.__MESURER__.capabilities()
|
|
110
|
+
await window.__MESURER__.annotations()
|
|
111
|
+
await window.__MESURER__.context({ annotation: annotationId })
|
|
112
|
+
await window.__MESURER__.stable()
|
|
113
|
+
await window.__MESURER__.review(annotationId)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The original low-level inspection API remains available regardless of the context plugin:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
inspect / inspectAll / at
|
|
120
|
+
distance / viewport / feedback
|
|
121
|
+
describe / command / state / stable
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Inject into an existing harness
|
|
125
|
+
|
|
126
|
+
Do not create another browser or change application source just for Mesurer when the harness already has a page JavaScript-evaluation primitive.
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
import { readFile } from "node:fs/promises";
|
|
130
|
+
import { fileURLToPath } from "node:url";
|
|
131
|
+
|
|
132
|
+
const source = await readFile(
|
|
133
|
+
fileURLToPath(import.meta.resolve("mesurer-solid/inject-script")),
|
|
134
|
+
"utf8",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
await browser.evaluate(source);
|
|
138
|
+
await browser.evaluate(`window.__MESURER__.ready()`);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Injection installs `contextPlugin()` **by default**, so Copy Context, Copy Selection, Add Note, annotation markers, and the context/review APIs are available without extra configuration. To inject only the base/low-level inspector:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
window.__MESURER_CONFIG__ = { context: false };
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
See [`AGENT_INTEGRATION.md`](./AGENT_INTEGRATION.md).
|
|
148
|
+
|
|
149
|
+
## Clean screenshot evidence
|
|
150
|
+
|
|
151
|
+
The context plugin does not own a screenshot engine. It prepares the actual page so the outer harness can capture real pixels:
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
const plan = await window.__MESURER__.capturePlan({ annotation: annotationId })
|
|
155
|
+
await window.__MESURER__.prepareCapture()
|
|
156
|
+
try {
|
|
157
|
+
// Capture the real viewport and optional focus crop.
|
|
158
|
+
} finally {
|
|
159
|
+
await window.__MESURER__.finishCapture()
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The focus crop includes scoped `regions`, so element-free area annotations still get close-up evidence. Capture mode hides Mesurer controls while preserving guides, rulers, measurements, distances, annotation/selection markers, and pixel labels.
|
|
164
|
+
|
|
165
|
+
## Optional host delivery
|
|
166
|
+
|
|
167
|
+
Screenshot and direct-send capabilities belong to the plugin configuration rather than core mount options:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const mesurer = mountMeasurer({
|
|
171
|
+
agent: true,
|
|
172
|
+
plugins: [
|
|
173
|
+
contextPlugin({
|
|
174
|
+
evidenceProvider: async ({ context, plan }) => {
|
|
175
|
+
// Use the host/harness real screenshot primitive.
|
|
176
|
+
return [];
|
|
177
|
+
},
|
|
178
|
+
sendContext: async ({ context, text, images }) => {
|
|
179
|
+
// Deliver with the ACP client/session already owned by the host.
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
],
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Without `sendContext`, the plugin does not render a Send control.
|
|
187
|
+
|
|
188
|
+
## Portable Agent Skill
|
|
189
|
+
|
|
190
|
+
There are no Mesurer packages for individual harnesses. The npm package ships one canonical `mesurer-ui` Agent Skill:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npx --yes --package=mesurer-solid@beta mesurer-skill install
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The transient installer leaves a self-contained skill at `.agents/skills/mesurer-ui/`, including `assets/inject-script.js`. An Agent-Skills-compatible harness can therefore discover the workflow and inject Mesurer through its existing browser evaluation channel without keeping the npm package installed in the application.
|
|
197
|
+
|
|
198
|
+
## ACP
|
|
199
|
+
|
|
200
|
+
Mesurer does not discover agents, manage processes, or choose sessions. The ACP client/harness that already owns a target session sends Mesurer output.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { toAcpContentBlocks } from "mesurer-solid";
|
|
204
|
+
|
|
205
|
+
const blocks = toAcpContentBlocks(context, images);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The result is one context text block plus optional labeled image blocks. If image prompts are unavailable, send the text block only. Copy Context remains the universal fallback.
|
|
209
|
+
|
|
210
|
+
## Plugins
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import {
|
|
214
|
+
createMesurerPluginHost,
|
|
215
|
+
createMesurerRuntime,
|
|
216
|
+
defineMesurerPlugin,
|
|
217
|
+
} from "mesurer-solid/core";
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Plugins can contribute tools, commands, hooks, overlays, settings, state, services, history/persistence, renderer-owned UI, and lifecycle cleanup. Built-ins can be excluded/replaced without forking the renderer. Plugin tools render through the same canonical toolbar button path as built-ins; programmatic built-in commands use the owning renderer instance rather than toolbar DOM labels or synthetic keyboard events.
|
|
221
|
+
|
|
222
|
+
## Public surface
|
|
223
|
+
|
|
224
|
+
```text
|
|
225
|
+
mesurer-solid
|
|
226
|
+
mesurer-solid/core
|
|
227
|
+
mesurer-solid/inject
|
|
228
|
+
mesurer-solid/inject-script
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The package is self-contained. Private core/DOM/renderer workspaces and the internal Solid runtime must not leak into the published consumer surface.
|
|
232
|
+
|
|
233
|
+
MIT. Adapted from `ibelick/mesurer`; see `THIRD_PARTY_LICENSES.md`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
## Mesurer
|
|
4
|
+
|
|
5
|
+
Portions of this package are adapted from [`ibelick/mesurer`](https://github.com/ibelick/mesurer), including framework-neutral measurement/runtime logic and the user-facing visual design system ported to Solid JSX.
|
|
6
|
+
|
|
7
|
+
Mesurer is licensed under the MIT License:
|
|
8
|
+
|
|
9
|
+
> MIT License
|
|
10
|
+
>
|
|
11
|
+
> Copyright (c) 2026 Julien Thibeaut
|
|
12
|
+
>
|
|
13
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
> in the Software without restriction, including without limitation the rights
|
|
16
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
> furnished to do so, subject to the following conditions:
|
|
19
|
+
>
|
|
20
|
+
> The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
> copies or substantial portions of the Software.
|
|
22
|
+
>
|
|
23
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
> SOFTWARE.
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { MesurerPluginDescription, MesurerPluginHost } from "./core";
|
|
2
|
+
export type AgentRect = {
|
|
3
|
+
left: number;
|
|
4
|
+
top: number;
|
|
5
|
+
right: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
};
|
|
12
|
+
export type AgentEdges = {
|
|
13
|
+
top: number;
|
|
14
|
+
right: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
left: number;
|
|
17
|
+
};
|
|
18
|
+
export type AgentElementInspection = {
|
|
19
|
+
selector: string;
|
|
20
|
+
tag: string;
|
|
21
|
+
id: string | null;
|
|
22
|
+
classes: string[];
|
|
23
|
+
text: string;
|
|
24
|
+
role: string | null;
|
|
25
|
+
ariaLabel: string | null;
|
|
26
|
+
rect: AgentRect;
|
|
27
|
+
margin: AgentEdges;
|
|
28
|
+
padding: AgentEdges;
|
|
29
|
+
border: AgentEdges;
|
|
30
|
+
typography: {
|
|
31
|
+
fontFamily: string;
|
|
32
|
+
fontSize: string;
|
|
33
|
+
fontWeight: string;
|
|
34
|
+
lineHeight: string;
|
|
35
|
+
letterSpacing: string;
|
|
36
|
+
textAlign: string;
|
|
37
|
+
color: string;
|
|
38
|
+
};
|
|
39
|
+
appearance: {
|
|
40
|
+
backgroundColor: string;
|
|
41
|
+
borderColor: string;
|
|
42
|
+
borderRadius: string;
|
|
43
|
+
boxShadow: string;
|
|
44
|
+
opacity: string;
|
|
45
|
+
};
|
|
46
|
+
layout: {
|
|
47
|
+
display: string;
|
|
48
|
+
position: string;
|
|
49
|
+
zIndex: string;
|
|
50
|
+
overflowX: string;
|
|
51
|
+
overflowY: string;
|
|
52
|
+
flexDirection: string;
|
|
53
|
+
alignItems: string;
|
|
54
|
+
justifyContent: string;
|
|
55
|
+
gap: string;
|
|
56
|
+
gridTemplateColumns: string;
|
|
57
|
+
gridTemplateRows: string;
|
|
58
|
+
transform: string;
|
|
59
|
+
};
|
|
60
|
+
scroll: {
|
|
61
|
+
clientWidth: number;
|
|
62
|
+
clientHeight: number;
|
|
63
|
+
scrollWidth: number;
|
|
64
|
+
scrollHeight: number;
|
|
65
|
+
overflowsX: boolean;
|
|
66
|
+
overflowsY: boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export type AgentDistance = {
|
|
70
|
+
a: AgentElementInspection;
|
|
71
|
+
b: AgentElementInspection;
|
|
72
|
+
horizontalGap: number;
|
|
73
|
+
verticalGap: number;
|
|
74
|
+
centerDeltaX: number;
|
|
75
|
+
centerDeltaY: number;
|
|
76
|
+
};
|
|
77
|
+
export type AgentViewportSnapshot = {
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
devicePixelRatio: number;
|
|
81
|
+
scrollX: number;
|
|
82
|
+
scrollY: number;
|
|
83
|
+
documentWidth: number;
|
|
84
|
+
documentHeight: number;
|
|
85
|
+
horizontalOverflow: boolean;
|
|
86
|
+
verticalOverflow: boolean;
|
|
87
|
+
};
|
|
88
|
+
export type AgentFeedbackSnapshot = {
|
|
89
|
+
viewport: AgentViewportSnapshot;
|
|
90
|
+
elements: AgentElementInspection[];
|
|
91
|
+
plugins: MesurerPluginDescription | undefined;
|
|
92
|
+
pluginState: Record<string, unknown>;
|
|
93
|
+
};
|
|
94
|
+
export type MesurerAgentHarness = {
|
|
95
|
+
ready(): Promise<void>;
|
|
96
|
+
describe(): Promise<MesurerPluginDescription | undefined>;
|
|
97
|
+
inspect(selector: string, index?: number): AgentElementInspection | null;
|
|
98
|
+
inspectAll(selector: string, limit?: number): AgentElementInspection[];
|
|
99
|
+
at(x: number, y: number): AgentElementInspection | null;
|
|
100
|
+
distance(a: string, b: string): AgentDistance | null;
|
|
101
|
+
viewport(): AgentViewportSnapshot;
|
|
102
|
+
feedback(selectors?: string[]): Promise<AgentFeedbackSnapshot>;
|
|
103
|
+
command(id: string, args?: unknown): Promise<void>;
|
|
104
|
+
state(): Promise<Record<string, unknown>>;
|
|
105
|
+
stable(frames?: number): Promise<void>;
|
|
106
|
+
};
|
|
107
|
+
export type CreateMesurerAgentHarnessOptions = {
|
|
108
|
+
ownerDocument: Document;
|
|
109
|
+
root?: ParentNode;
|
|
110
|
+
getPluginHost: () => MesurerPluginHost | undefined;
|
|
111
|
+
waitForPluginHost: () => Promise<MesurerPluginHost>;
|
|
112
|
+
};
|
|
113
|
+
export declare function createMesurerAgentHarness(options: CreateMesurerAgentHarnessOptions): MesurerAgentHarness;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MesurerPlugin } from "./core";
|
|
2
|
+
import { type MesurerAnnotation, type MesurerCapturePlanV1, type MesurerContextRequest, type MesurerContextSender, type MesurerContextV1, type MesurerEvidenceProvider, type MesurerReviewV1 } from "./context";
|
|
3
|
+
export declare const MESURER_CONTEXT_PLUGIN_ID = "mesurer.context";
|
|
4
|
+
export declare const MESURER_CONTEXT_SERVICE_ID = "context:v1";
|
|
5
|
+
export type MesurerContextPluginOptions = {
|
|
6
|
+
/** Render Copy Context, Copy Selection, Add Note, annotation markers, and optional Send controls. Defaults to true. */
|
|
7
|
+
ui?: boolean;
|
|
8
|
+
/** Optional screenshot provider owned by the browser/harness. */
|
|
9
|
+
evidenceProvider?: MesurerEvidenceProvider;
|
|
10
|
+
/** Optional direct handoff callback, normally backed by an ACP client outside Mesurer. */
|
|
11
|
+
sendContext?: MesurerContextSender;
|
|
12
|
+
sendLabel?: string;
|
|
13
|
+
};
|
|
14
|
+
export type MesurerContextService = {
|
|
15
|
+
context(request?: MesurerContextRequest): Promise<MesurerContextV1>;
|
|
16
|
+
contextText(request?: MesurerContextRequest): Promise<string>;
|
|
17
|
+
copyContext(request?: MesurerContextRequest): Promise<void>;
|
|
18
|
+
annotations(): Promise<MesurerAnnotation[]>;
|
|
19
|
+
review(annotationId?: string): Promise<MesurerReviewV1 | MesurerReviewV1[]>;
|
|
20
|
+
capturePlan(request?: MesurerContextRequest): Promise<MesurerCapturePlanV1>;
|
|
21
|
+
prepareCapture(): Promise<void>;
|
|
22
|
+
finishCapture(): Promise<void>;
|
|
23
|
+
sendContext(request?: MesurerContextRequest): Promise<void>;
|
|
24
|
+
readonly screenshots: boolean;
|
|
25
|
+
readonly send: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare function contextPlugin(options?: MesurerContextPluginOptions): MesurerPlugin;
|