mesurer-solid 0.1.8-beta.3 → 0.1.9-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.
@@ -10,7 +10,6 @@ The normal agent workflow requires no Mesurer MCP server, localhost daemon, Send
10
10
  npx --yes --package=mesurer-solid mesurer-skill install
11
11
  ```
12
12
 
13
- Use `mesurer-solid@beta` only when intentionally validating a prerelease.
14
13
 
15
14
  The installer writes a self-contained skill and injection artifact:
16
15
 
@@ -124,13 +123,15 @@ Use the high-level Context and saved-intent methods for normal UI work. The lowe
124
123
  | `distance(a, b)` | Measure two selector targets. |
125
124
  | `viewport()` | Read viewport/document dimensions, scrolling, device pixel ratio, and overflow. |
126
125
  | `feedback(selectors?)` | Read viewport data, inspected elements, plugin description, and plugin state together. |
127
- | `command(id, args?)` | Execute a registered Mesurer command. |
126
+ | `command(id, args?)` | Execute a registered Mesurer command and return its JSON-safe result when present. |
128
127
  | `state()` | Read plugin state. |
129
128
  | `stable(frames?)` | Wait for fonts and rendering before measuring again. |
130
129
  | `textEdits()` / `textEdit(id)` | Read saved direct-text and typography intent. |
131
130
 
132
131
  Context adds `capabilities()`, `context()`, `contextText()`, `select()`, `annotations()`, `review()`, `capturePlan()`, `prepareCapture()`, and `finishCapture()`.
133
132
 
133
+ These inspection methods accept general DOM elements, including SVG. `select()` and point inspection can return SVG targets, and Context preserves their selector, tag, and geometry. Arrange and direct text editing remain HTML-only mutation paths.
134
+
134
135
  Arrange adds `arrangements()`, `arrange()`, `showArrange()`, `arrangeCapturePlan()`, and `reviewArrange()`.
135
136
 
136
137
  Use `capabilities().capabilities` before calling optional Context, Arrange, or text-edit paths. The complete public list is in [Capabilities](../../docs/CAPABILITIES.md).
@@ -148,7 +149,7 @@ const context = await window.__MESURER__.select([
148
149
 
149
150
  Each selector must resolve to exactly one target. Missing or ambiguous selectors throw rather than binding to a guess.
150
151
 
151
- 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.
152
+ 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. Box, guide, container, and diagonal semantics are defined in [Measurements and distance geometry](../../docs/MEASUREMENTS.md).
152
153
 
153
154
  ## Arrange intent
154
155
 
@@ -181,7 +182,7 @@ Direct text editing records copy and typography intent without pretending to edi
181
182
 
182
183
  Editing starts by double-click/double-tap while Select or Typography is active. Arrange keeps Select active, so editing works while Arrange remains selected.
183
184
 
184
- 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.
185
+ 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. Mixed inline copy can target the exact direct text run under the pointer while preserving inline children and the host element's native DOM surface.
185
186
 
186
187
  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.
187
188
 
@@ -241,7 +242,7 @@ try {
241
242
  }
242
243
  ```
243
244
 
244
- The optional human `screenshot()` plugin from `mesurer-solid/plugins` is a separate camera workflow. It is not an agent delivery capability. Preserve an existing human preview unless the task is specifically about Screenshot behavior.
245
+ The optional human `screenshot()` plugin from `mesurer-solid/plugins` is a separate camera workflow. It may use application-native, Chromium extension, or browser display capture internally, but none of those paths changes the agent contract above. Do not add or configure a host capture path only to collect coding-agent evidence. Preserve an existing human preview unless the task is specifically about Screenshot behavior.
245
246
 
246
247
  ## Optional human-to-Codex delivery
247
248
 
@@ -253,7 +254,7 @@ Delivery uses Codex's native durable queue. Mesurer queues once and keeps the qu
253
254
 
254
255
  The browser plugin connects lazily on the first Queue to Codex or thread-picker action. Page affinity and an explicit destination override persist per tab. If several registered threads are visible and the page has no saved destination, the user must choose one.
255
256
 
256
- The typed `codex:v1` service exposes `health()`, `listThreads()`, `useThread(thread)`, `delivery(deliveryId)`, and `send({ thread })`. Queue submission is single-flight. A matched completed turn may remove only the annotation ids sent with that delivery; interrupted, failed, ambiguous, or unreadable work keeps them.
257
+ The typed `codex:v1` service exposes `health()`, `listThreads()`, `useThread(thread)`, `delivery(deliveryId)`, and canonical `queue(request?)`. `send(request?)` remains a compatibility alias. The generic plugin command is `codex.queue`; `codex.send` remains its compatibility alias. Queue submission is single-flight. A matched completed turn may remove only the annotation ids sent with that delivery; interrupted, failed, ambiguous, or unreadable work keeps them.
257
258
 
258
259
  Mesurer does not create new Codex threads. Create or open the thread in Codex and let `SessionStart` register it. Turn completion is transport lifecycle, not proof that the requested UI result is correct.
259
260
 
package/README.md CHANGED
@@ -4,6 +4,10 @@ Framework-agnostic UI inspection, measurement, visual intent, and agent-readable
4
4
 
5
5
  Mesurer Solid ships its own isolated Solid 2 renderer. Host applications can use Solid 1 or 2, React, Vue, Svelte, vanilla DOM, or an Electron renderer without providing Solid.
6
6
 
7
+ <p align="center">
8
+ <img src="https://raw.githubusercontent.com/jhomra21/mesurer-solid/main/docs/assets/readme/hero-multi-spacing.png" alt="Mesurer Solid measuring spacing between selected elements" width="100%">
9
+ </p>
10
+
7
11
  ## Installation
8
12
 
9
13
  ```bash
@@ -16,7 +20,6 @@ or:
16
20
  npm install -D mesurer-solid
17
21
  ```
18
22
 
19
- Use `mesurer-solid@beta` only when intentionally testing a prerelease.
20
23
 
21
24
  ## Usage
22
25
 
@@ -34,7 +37,7 @@ if (import.meta.env.DEV) {
34
37
  }
35
38
  ```
36
39
 
37
- For Vite, put this in the existing browser entry such as `src/main.tsx`, `src/main.ts`, or `src/index.tsx`. In Electron, use the renderer entry. In SSR applications, mount from a client-only module or lifecycle.
40
+ For Vite, put this in the existing browser entry such as `src/main.tsx`, `src/main.ts`, or `src/index.tsx`. In Electron, use the renderer entry and keep privileged Electron work in preload/main. If preload exposes `window.__MESURER_HOST__.captureScreenshot`, Screenshot uses native capture automatically. Renderer configuration remains `screenshot()`; there is no Electron-specific Screenshot factory or provider option. In SSR applications, mount from a client-only module or lifecycle.
38
41
 
39
42
  `src/dev/mesurer.ts` is an optional organization pattern, not a required filename or directory. Do not mount Mesurer from `vite.config.ts`, server/API code, Node-only scripts, an Electron main process, or a module that also executes during SSR.
40
43
 
@@ -46,13 +49,14 @@ All public first-party plugin factories are exported from `mesurer-solid/plugins
46
49
 
47
50
  ```ts
48
51
  import { mountMesurer } from "mesurer-solid"
49
- import { arrange, context, screenshot } from "mesurer-solid/plugins"
52
+ import { arrange, context, layoutGuides, screenshot } from "mesurer-solid/plugins"
50
53
 
51
54
  const mesurer = mountMesurer({
52
55
  agent: true,
53
56
  plugins: [
54
57
  context(),
55
58
  arrange(),
59
+ layoutGuides(),
56
60
  screenshot(),
57
61
  ],
58
62
  })
@@ -62,6 +66,8 @@ The base inspector includes Select, X-ray, Rulers, Typography, Guides, Distance,
62
66
 
63
67
  For explicit plugin composition, `mesurer-solid/plugins` also exports `select`, `xray`, `colorPicker`, `rulers`, `typography`, `guides`, `distance`, `settings`, `defaults`, and `compose`.
64
68
 
69
+ Resolve plugin-owned capabilities with `await mesurer.service<T>(serviceId)`. The helper waits for configured plugins to finish loading and keeps normal consumers on the mounted API instead of requiring `pluginHost.service.get(...)`.
70
+
65
71
  | Entry | Purpose |
66
72
  | --- | --- |
67
73
  | `mesurer-solid` | Mount API, public domain types, and agent API |
@@ -73,27 +79,47 @@ For explicit plugin composition, `mesurer-solid/plugins` also exports `select`,
73
79
  | `mesurer-codex` | Run the optional loopback Codex queue companion |
74
80
  | `mesurer-codex-connect` | Start or reuse the matching Codex companion and register the current Codex session |
75
81
 
82
+ Programmatic injection reuses an existing connected instance by default. Lifecycle-owning integrations can set `recoverDisconnected: true` in `MesurerInjectConfig` to remount Mesurer when page DOM replacement disconnects its host. The option defaults to `false`, so ordinary one-shot injection does not silently reappear after disposal.
83
+
76
84
  ## Features
77
85
 
78
- - Select one or many rendered elements and inspect exact geometry.
86
+ - Select one or many rendered HTML or SVG elements and inspect exact geometry.
79
87
  - Measure distance and pairwise multi-selection spacing.
80
88
  - Use X-ray, guides, rulers, and persisted settings.
89
+ - Add page-scoped columns, rows, or pixel grids with the optional `layoutGuides()` plugin. Guide edits participate in plugin undo/redo, and Context includes the current page's saved guides with their visibility state.
81
90
  - Inspect Typography and preview reversible direct copy/style changes.
82
91
  - Arrange selected UI into a Desired position without changing source.
83
- - Capture visible-tab regions through the optional Screenshot plugin.
92
+ - Capture page regions through the optional Screenshot plugin. It selects native host capture, the Chromium extension adapter, or browser display capture internally.
84
93
  - Read selection, measurements, guides, annotations, layout, styles, and saved human intent through Context and agent APIs.
85
94
  - Keep saved annotations across same-tab reloads and conservatively rebind them to their original DOM targets; markers, cards, and ownership evidence stay attached through scrolling, repeated-note markers stay local, Add Note remains available while a note is open, and cards/composers occlude Select hover and selection chrome.
86
95
  - Extend the runtime with tools, settings, overlays, commands, hooks, state, and services.
87
96
  - Compact the toolbar to active controls without changing tool state or order.
97
+ - Choose System, Light, or Dark appearance while keeping the same theme across isolated and document-backed Mesurer UI.
88
98
 
89
99
  Arrange is not a toolbar mode. It can be activated before a selection exists and enables Select automatically. Turning Arrange off leaves Select active; turning Select off exits Arrange.
90
100
 
91
- Direct text editing respects native editing boundaries. Descendants of an editable ancestor remain native, while a nested `contenteditable="false"` boundary ends inherited editability and can become a Mesurer target when the normal direct-text rules pass.
101
+ Toolbar dragging starts after the pointer crosses the drag threshold. Dragging from Settings, Guide, or plugin triggers closes the open menu or panel. Pointer activity inside menus, dialogs, form controls, editable regions, and sliders does not drag the toolbar.
102
+
103
+ Select and agent point inspection use the same rendered hit-test path. They can target SVG and visible `pointer-events:none` descendants instead of collapsing those descendants to an interactive ancestor. Open shadow roots are traversed; closed shadow roots remain browser-owned boundaries. Context and annotations accept the same SVG targets. Arrange and direct text editing only mutate HTML elements.
104
+
105
+ Persisted workspace evidence is page-scoped by route, including sorted query parameters. In-tab navigation swaps the current page workspace without carrying page-owned guides or selection state to another route. Toolbar placement remains tab-session UI and survives those route changes and reloads.
106
+
107
+ Direct text editing respects native editing boundaries. Descendants of an editable ancestor remain native, while a nested `contenteditable="false"` boundary ends inherited editability and can become a Mesurer target when the normal direct-text rules pass. Mixed inline copy can target the exact direct text run before or after an inline child without flattening or recreating that child, and the host element keeps its native DOM APIs throughout the interaction.
92
108
 
93
109
  While direct text editing is active, Mesurer keeps one visible edit ring, keeps the selected dimensions pill and Typography separated by the same `2px` rendered gap when the card is below the source, and keeps Typography stationary during ordinary pointer movement. The selection-adjacent Add Note button is suppressed only for the active edit and returns when the editor closes; saved annotation markers and panels remain available.
94
110
 
95
111
  Mesurer previews text, styles, and Arrange transforms only while it still owns the value it applied. Host-authored changes take ownership and are preserved through undo/redo, Live review, cleanup, and disposal.
96
112
 
113
+ ## Appearance
114
+
115
+ The default appearance is `"system"`. Users can change it under **Settings > General > Appearance**, or applications can choose the initial mode:
116
+
117
+ ```ts
118
+ mountMesurer({ theme: "light" })
119
+ ```
120
+
121
+ The setting accepts `"system"`, `"light"`, or `"dark"` and persists with the other Mesurer settings. System mode follows `prefers-color-scheme`.
122
+
97
123
  ## Shortcuts
98
124
 
99
125
  Global shortcuts are enabled by default. Turn them off from **Settings > General > Shortcuts** or pass `shortcutsEnabled: false` to `mountMesurer()`. Toolbar controls, editor-local keys, and Escape/cancel behavior remain available.
@@ -107,6 +133,7 @@ Global shortcuts are enabled by default. Turn them off from **Settings > General
107
133
  | `R` | Rulers |
108
134
  | `A` | Typography |
109
135
  | `G` | Guides |
136
+ | `L` | Layout Guides when the plugin is enabled |
110
137
  | `H` / `V` | Horizontal / vertical guide orientation |
111
138
  | `Alt` / `Option` | Distance overlay |
112
139
  | `Cmd/Ctrl + ,` | Settings |
@@ -166,8 +193,10 @@ See [Queue Context feedback to Codex](https://github.com/jhomra21/mesurer-solid/
166
193
  - [Capabilities](https://github.com/jhomra21/mesurer-solid/blob/main/docs/CAPABILITIES.md)
167
194
  - [Getting started](https://github.com/jhomra21/mesurer-solid/blob/main/docs/GETTING_STARTED.md)
168
195
  - [Direct text editing and Typography](https://github.com/jhomra21/mesurer-solid/blob/main/docs/TEXT_EDITING.md)
169
- - [Arrange](https://github.com/jhomra21/mesurer-solid/blob/main/docs/ARRANGE.md)
196
+ - [Arrange](https://github.com/jhomra21/mesurer-solid/blob/main/docs/ARRANGE.md) and [Layout Guides](https://github.com/jhomra21/mesurer-solid/blob/main/docs/LAYOUT_GUIDES.md)
197
+ - [Measurements and distance geometry](https://github.com/jhomra21/mesurer-solid/blob/main/docs/MEASUREMENTS.md)
170
198
  - [Screenshots](https://github.com/jhomra21/mesurer-solid/blob/main/docs/SCREENSHOTS.md)
199
+ - [Electron renderer example](https://github.com/jhomra21/mesurer-solid/blob/main/examples/electron-renderer/README.md)
171
200
  - [Context workflow](https://github.com/jhomra21/mesurer-solid/blob/main/docs/CONTEXT_WORKFLOW.md)
172
201
  - [Queue Context feedback to Codex](https://github.com/jhomra21/mesurer-solid/blob/main/docs/CODEX.md)
173
202
  - [Browser and agent integration](https://github.com/jhomra21/mesurer-solid/blob/main/docs/BROWSER_HARNESS.md)
package/dist/agent.d.ts CHANGED
@@ -123,7 +123,7 @@ export type MesurerAgentHarness = {
123
123
  distance(a: string, b: string): AgentDistance | null;
124
124
  viewport(): AgentViewportSnapshot;
125
125
  feedback(selectors?: string[]): Promise<AgentFeedbackSnapshot>;
126
- command(id: string, args?: AgentCommandArgs): Promise<void>;
126
+ command(id: string, args?: AgentCommandArgs): ReturnType<MesurerPluginHost["command"]["execute"]>;
127
127
  state(): Promise<PluginStateSnapshot>;
128
128
  textEdits(): Promise<MesurerTextEditIntent[]>;
129
129
  textEdit(id: string): Promise<MesurerTextEditIntent>;
@@ -11,14 +11,16 @@ export type MesurerCodexPluginOptions = {
11
11
  /** Remove annotations included in a delivery after Codex reports that turn finished. Defaults to true. */
12
12
  clearCompletedAnnotations?: boolean;
13
13
  };
14
- export type MesurerCodexSendRequest = {
15
- /** Optional instruction for this send. */
14
+ export type MesurerCodexQueueRequest = {
15
+ /** Optional instruction for this queue operation. */
16
16
  instruction?: string;
17
- /** Send only these saved annotation ids. When omitted, all saved annotations are sent. */
17
+ /** Queue only these saved annotation ids. When omitted, all saved annotations are queued. */
18
18
  annotationIds?: string[];
19
- /** Send to a particular bridge-visible Codex thread for this request. */
19
+ /** Queue to a particular bridge-visible Codex thread for this request. */
20
20
  thread?: string;
21
21
  };
22
+ /** @deprecated Use `MesurerCodexQueueRequest`. */
23
+ export type MesurerCodexSendRequest = MesurerCodexQueueRequest;
22
24
  export type MesurerCodexDeliveryStatus = "queued" | "working" | "completed" | "interrupted";
23
25
  export type MesurerCodexDispatchStatus = "persisting" | "persisted" | "resumed" | "already-loaded" | "unsupported" | "wake-failed" | "untracked" | "desktop-local" | "waiting-active" | "desktop-sent" | "desktop-opened" | "desktop-send-uncertain" | "desktop-wait-failed";
24
26
  export type MesurerCodexDelivery = {
@@ -35,7 +37,7 @@ export type MesurerCodexDelivery = {
35
37
  createdAt: number;
36
38
  updatedAt: number;
37
39
  };
38
- export type MesurerCodexSendResult = {
40
+ export type MesurerCodexQueueResult = {
39
41
  thread: string;
40
42
  output: string;
41
43
  /** Mesurer currently delivers feedback as a queued Codex follow-up, never as an in-flight steer. */
@@ -52,6 +54,8 @@ export type MesurerCodexSendResult = {
52
54
  /** Exact saved annotations included in this queued request. */
53
55
  annotationIds: string[];
54
56
  };
57
+ /** @deprecated Use `MesurerCodexQueueResult`. */
58
+ export type MesurerCodexSendResult = MesurerCodexQueueResult;
55
59
  export type MesurerCodexHealth = {
56
60
  /** Current bridge default target. Null when the bridge has not been bound yet. */
57
61
  thread: string | null;
@@ -91,6 +95,8 @@ export type MesurerCodexService = {
91
95
  * Queue Context for the page-pinned/default target or one explicit bridge-visible thread.
92
96
  * This does not steer or interrupt an in-flight Codex turn.
93
97
  */
94
- send(request?: MesurerCodexSendRequest): Promise<MesurerCodexSendResult>;
98
+ queue(request?: MesurerCodexQueueRequest): Promise<MesurerCodexQueueResult>;
99
+ /** @deprecated Use `queue()`; Mesurer delivery is durable queueing, not in-flight send/steer. */
100
+ send(request?: MesurerCodexQueueRequest): Promise<MesurerCodexQueueResult>;
95
101
  };
96
102
  export declare function codex(options?: MesurerCodexPluginOptions): MesurerPlugin;
package/dist/context.d.ts CHANGED
@@ -147,6 +147,18 @@ export type MesurerContextGuide = {
147
147
  orientation: "vertical" | "horizontal";
148
148
  position: number;
149
149
  };
150
+ export type MesurerContextLayoutGuide = {
151
+ id: string;
152
+ kind: "columns" | "rows" | "grid";
153
+ visible: boolean;
154
+ color: string;
155
+ opacity: number;
156
+ count: number;
157
+ size: number;
158
+ gutter: number;
159
+ offset: number;
160
+ align: "stretch" | "min" | "center" | "max";
161
+ };
150
162
  export type MesurerContextMeasurement = {
151
163
  id: string;
152
164
  rect: MesurerContextRect;
@@ -209,6 +221,7 @@ export type MesurerContextV1 = {
209
221
  targets: MesurerContextTarget[];
210
222
  visualContext: {
211
223
  guides: MesurerContextGuide[];
224
+ layoutGuides: MesurerContextLayoutGuide[];
212
225
  measurements: MesurerContextMeasurement[];
213
226
  distances: MesurerContextDistance[];
214
227
  };
@@ -266,7 +279,7 @@ export type MesurerWorkspaceContextSource = {
266
279
  deltaX: number;
267
280
  deltaY: number;
268
281
  snapped?: boolean;
269
- elementRef?: HTMLElement | null;
282
+ elementRef?: Element | null;
270
283
  }>;
271
284
  activeMeasurement: {
272
285
  id: string;
@@ -274,14 +287,14 @@ export type MesurerWorkspaceContextSource = {
274
287
  deltaX: number;
275
288
  deltaY: number;
276
289
  snapped?: boolean;
277
- elementRef?: HTMLElement | null;
290
+ elementRef?: Element | null;
278
291
  } | null;
279
292
  heldDistances: Array<{
280
293
  id: string;
281
294
  rectA: MesurerContextRect;
282
295
  rectB: MesurerContextRect;
283
- elementRefA?: HTMLElement | null;
284
- elementRefB?: HTMLElement | null;
296
+ elementRefA?: Element | null;
297
+ elementRefB?: Element | null;
285
298
  horizontal: {
286
299
  x1: number;
287
300
  x2: number;
@@ -302,14 +315,14 @@ export type MesurerWorkspaceContextSource = {
302
315
  }>;
303
316
  } | null;
304
317
  currentSelection(): {
305
- elements: HTMLElement[];
318
+ elements: Element[];
306
319
  region: MesurerContextRect | null;
307
320
  };
308
321
  annotations(): MesurerAnnotation[];
309
322
  annotation(id: string): (MesurerAnnotation & {
310
323
  resolvedTargets: Array<{
311
324
  target: MesurerAnnotationTarget;
312
- element: HTMLElement | null;
325
+ element: Element | null;
313
326
  }>;
314
327
  }) | null;
315
328
  annotationRect(id: string): MesurerContextRect | null;
@@ -319,6 +332,7 @@ export declare function captureMesurerContext(options: {
319
332
  ownerDocument: Document;
320
333
  ownerWindow: Window;
321
334
  request?: MesurerContextRequest;
335
+ layoutGuides?: MesurerContextLayoutGuide[];
322
336
  }): MesurerContextV1;
323
337
  export declare function formatMesurerContext(context: MesurerContextV1): string;
324
338
  export declare function createMesurerCapturePlan(context: MesurerContextV1): MesurerCapturePlanV1;
@@ -327,5 +341,6 @@ export declare function reviewMesurerAnnotation(options: {
327
341
  ownerDocument: Document;
328
342
  ownerWindow: Window;
329
343
  annotationId: string;
344
+ layoutGuides?: MesurerContextLayoutGuide[];
330
345
  }): MesurerReviewV1;
331
346
  export declare function copyTextToClipboard(ownerDocument: Document, ownerWindow: Window, text: string): Promise<void>;
package/dist/core.d.ts CHANGED
@@ -77,7 +77,7 @@ export type OverlayContribution = {
77
77
  };
78
78
  export type CommandHandler = (args: PluginValue | undefined, context: {
79
79
  source?: PluginValue;
80
- }) => void | Promise<void>;
80
+ }) => void | PluginValue | Promise<void | PluginValue>;
81
81
  export type HookHandler = (event: PluginValue) => void | Promise<void>;
82
82
  export type StateSliceDefinition<T extends PluginValue = PluginValue> = {
83
83
  id: string;
@@ -103,7 +103,7 @@ export type MesurerPluginContext = {
103
103
  };
104
104
  command: {
105
105
  register(id: string, handler: CommandHandler): Registration;
106
- execute(id: string, args?: PluginValue, source?: PluginValue): Promise<void>;
106
+ execute(id: string, args?: PluginValue, source?: PluginValue): Promise<PluginValue | undefined>;
107
107
  };
108
108
  hook: {
109
109
  on(name: string, handler: HookHandler): Registration;
@@ -175,7 +175,7 @@ export type MesurerPluginHost = {
175
175
  get<T>(id: string): T | undefined;
176
176
  };
177
177
  command: {
178
- execute(id: string, args?: PluginValue, source?: PluginValue): Promise<void>;
178
+ execute(id: string, args?: PluginValue, source?: PluginValue): Promise<PluginValue | undefined>;
179
179
  };
180
180
  hook: {
181
181
  emit(name: string, event: PluginValue): Promise<void>;
package/dist/core.js CHANGED
@@ -85,8 +85,10 @@ function i() {
85
85
  if (!o) throw Error(`Unknown Mesurer command: ${e}`);
86
86
  let s = t === 0, c = s ? x("history") : null;
87
87
  t += 1;
88
+ let l;
88
89
  try {
89
- await o.handler(n, { source: a }), await i.emit("command", {
90
+ let t = await o.handler(n, { source: a });
91
+ t !== void 0 && (l = t), await i.emit("command", {
90
92
  id: e,
91
93
  args: n
92
94
  });
@@ -97,6 +99,7 @@ function i() {
97
99
  let e = x("history");
98
100
  C(c, e) || (h.push(c), h.length > r && h.shift(), g.length = 0);
99
101
  }
102
+ return l;
100
103
  }, E = () => {
101
104
  let e = h.pop();
102
105
  return e ? (g.push(x("history")), g.length > r && g.shift(), S(e, "history"), _(void 0, "history"), !0) : !1;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { type MesurerAgentHarness, type MesurerTextEditIntent } from "./agent";
2
- import type { ArrangeCapturePlan, ArrangeIntent, ArrangePresentation, ArrangeReview } from "./arrange";
2
+ import { type ArrangeCapturePlan, type ArrangeIntent, type ArrangePresentation, type ArrangeReview } from "./arrange";
3
3
  import type { MesurerAnnotation, MesurerCapturePlanV1, MesurerContextRequest, MesurerContextV1, MesurerReviewV1 } from "./context";
4
4
  import type { MesurerPlugin, MesurerPluginDescription, MesurerPluginHost } from "./core";
5
5
  import { type MesurerHostLayerMode } from "./host-layer";
6
6
  export type ColorPickerFormat = "hex" | "rgb" | "hsl" | "oklch";
7
+ export type MesurerTheme = "system" | "light" | "dark";
7
8
  export type MesurerBuiltinPluginId = "select" | "xray" | "color-picker" | "rulers" | "text-inspector" | "guides" | "distance" | "settings";
8
9
  export type LinePattern = "solid" | "dashed" | "dotted";
9
10
  export type LineStyle = {
@@ -80,6 +81,7 @@ export type MesurerStoredSettings = {
80
81
  multiMeasureEnabled?: boolean;
81
82
  persistOnReload?: boolean;
82
83
  shortcutsEnabled?: boolean;
84
+ theme?: MesurerTheme;
83
85
  guideStyle?: Partial<GuideStyle>;
84
86
  selectionSpacingStyle?: Partial<SelectionSpacingStyle>;
85
87
  rulerSettings?: Partial<RulerSettings>;
@@ -106,6 +108,7 @@ export type MesurerPersistence = {
106
108
  saveWorkspace(workspace: MesurerStoredWorkspace): void;
107
109
  clearWorkspace(): void;
108
110
  clearSettings(): void;
111
+ setPageKey?(pageKey: string): void;
109
112
  subscribe?: (listener: (snapshot: MesurerPersistenceSnapshot | null, source?: {
110
113
  settings?: boolean;
111
114
  workspace?: boolean;
@@ -122,6 +125,7 @@ export type MesurerOptions = {
122
125
  hoverHighlightEnabled?: boolean;
123
126
  persistOnReload?: boolean;
124
127
  shortcutsEnabled?: boolean;
128
+ theme?: MesurerTheme;
125
129
  persistKey?: string;
126
130
  persistence?: MesurerPersistence;
127
131
  onPersistenceError?: (cause: unknown) => void;
@@ -192,6 +196,7 @@ export type MountedMesurer = {
192
196
  readonly pluginHost: MesurerPluginHost | undefined;
193
197
  readonly ready: Promise<void>;
194
198
  readonly agent: MesurerBrowserAgent;
199
+ service<T>(id: string): Promise<T>;
195
200
  context(request?: MesurerContextRequest): Promise<MesurerContextV1>;
196
201
  contextText(request?: MesurerContextRequest): Promise<string>;
197
202
  copyContext(request?: MesurerContextRequest): Promise<void>;
@@ -224,7 +229,7 @@ export { MESURER_VERSION } from "./version";
224
229
  export type { AgentDistance, AgentEdges, AgentElementInspection, AgentFeedbackSnapshot, AgentRect, AgentViewportSnapshot, CreateMesurerAgentHarnessOptions, MesurerAgentHarness, MesurerTextEditIntent, MesurerTextEditService, MesurerTextStyleChange, MesurerTextStyleProperty, } from "./agent";
225
230
  export type { ArrangeCapturePlan, ArrangeIntent, ArrangeOffset, ArrangePresentation, ArrangeRect, ArrangeReview, ArrangeReviewTarget, ArrangeTarget, MesurerArrangeService, } from "./arrange";
226
231
  export { captureMesurerContext, copyTextToClipboard, createMesurerCapturePlan, formatMesurerContext, reviewMesurerAnnotation, } from "./context";
227
- export type { MesurerAnnotation, MesurerAnnotationBaseline, MesurerAnnotationTarget, MesurerCapturePlanV1, MesurerContextDistance, MesurerContextEdges, MesurerContextGuide, MesurerContextMeasurement, MesurerContextRect, MesurerContextRequest, MesurerContextTarget, MesurerContextV1, MesurerElementFingerprint, MesurerElementInspection, MesurerReviewChange, MesurerReviewMetricChange, MesurerReviewPresenceChange, MesurerReviewV1, } from "./context";
232
+ export type { MesurerAnnotation, MesurerAnnotationBaseline, MesurerAnnotationTarget, MesurerCapturePlanV1, MesurerContextDistance, MesurerContextEdges, MesurerContextGuide, MesurerContextLayoutGuide, MesurerContextMeasurement, MesurerContextRect, MesurerContextRequest, MesurerContextTarget, MesurerContextV1, MesurerElementFingerprint, MesurerElementInspection, MesurerReviewChange, MesurerReviewMetricChange, MesurerReviewPresenceChange, MesurerReviewV1, } from "./context";
228
233
  export { createMesurerPluginHost, createMesurerRuntime, defineMesurerPlugin } from "./core";
229
234
  export type { CommandHandler as MesurerCommandHandler, MesurerPlugin, MesurerPluginContext, MesurerPluginDescription, MesurerPluginHost, OverlayContribution, Registration as MesurerRegistration, SettingsContribution, StateSliceDefinition, ToolContribution, } from "./core";
230
235
  export type { MesurerHostLayerMode } from "./host-layer";