@generative-a11y/devtools 0.1.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.
@@ -0,0 +1,802 @@
1
+ // src/overlay.ts
2
+ import { createElement } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+
5
+ // src/inspector.tsx
6
+ import * as React4 from "react";
7
+ import { Copy, Pause, Play, RefreshCw, Search, X } from "lucide-react";
8
+ import { AnimatePresence, motion, useReducedMotion } from "motion/react";
9
+
10
+ // src/components/ui/button.tsx
11
+ import "react";
12
+ import { cva } from "class-variance-authority";
13
+ import { Slot } from "radix-ui";
14
+
15
+ // src/lib/utils.ts
16
+ import { clsx } from "clsx";
17
+ import { twMerge } from "tailwind-merge";
18
+ function cn(...inputs) {
19
+ return twMerge(clsx(inputs));
20
+ }
21
+
22
+ // src/components/ui/button.tsx
23
+ import { jsx } from "react/jsx-runtime";
24
+ var buttonVariants = cva(
25
+ "ga-:group/button ga-:inline-flex ga-:shrink-0 ga-:items-center ga-:justify-center ga-:rounded-lg ga-:border ga-:border-transparent ga-:bg-clip-padding ga-:text-sm ga-:font-medium ga-:whitespace-nowrap ga-:transition-all ga-:outline-none ga-:select-none ga-:focus-visible:border-ring ga-:focus-visible:ring-3 ga-:focus-visible:ring-ring/50 ga-:active:not-aria-[haspopup]:translate-y-px ga-:disabled:pointer-events-none ga-:disabled:opacity-50 ga-:aria-invalid:border-destructive ga-:aria-invalid:ring-3 ga-:aria-invalid:ring-destructive/20 ga-:dark:aria-invalid:border-destructive/50 ga-:dark:aria-invalid:ring-destructive/40 ga-:[&_svg]:pointer-events-none ga-:[&_svg]:shrink-0 ga-:[&_svg:not([class*=size-])]:size-4",
26
+ {
27
+ variants: {
28
+ variant: {
29
+ default: "ga-:bg-primary ga-:text-primary-foreground ga-:hover:bg-primary/80",
30
+ outline: "ga-:border-border ga-:bg-background ga-:hover:bg-muted ga-:hover:text-foreground ga-:aria-expanded:bg-muted ga-:aria-expanded:text-foreground ga-:dark:border-input ga-:dark:bg-input/30 ga-:dark:hover:bg-input/50",
31
+ secondary: "ga-:bg-secondary ga-:text-secondary-foreground ga-:hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] ga-:aria-expanded:bg-secondary ga-:aria-expanded:text-secondary-foreground",
32
+ ghost: "ga-:hover:bg-muted ga-:hover:text-foreground ga-:aria-expanded:bg-muted ga-:aria-expanded:text-foreground ga-:dark:hover:bg-muted/50",
33
+ destructive: "ga-:bg-destructive/10 ga-:text-destructive ga-:hover:bg-destructive/20 ga-:focus-visible:border-destructive/40 ga-:focus-visible:ring-destructive/20 ga-:dark:bg-destructive/20 ga-:dark:hover:bg-destructive/30 ga-:dark:focus-visible:ring-destructive/40",
34
+ link: "ga-:text-primary ga-:underline-offset-4 ga-:hover:underline"
35
+ },
36
+ size: {
37
+ default: "ga-:h-8 ga-:gap-1.5 ga-:px-2.5 ga-:has-data-[icon=inline-end]:pr-2 ga-:has-data-[icon=inline-start]:pl-2",
38
+ xs: "ga-:h-6 ga-:gap-1 ga-:rounded-[min(var(--radius-md),10px)] ga-:px-2 ga-:text-xs ga-:in-data-[slot=button-group]:rounded-lg ga-:has-data-[icon=inline-end]:pr-1.5 ga-:has-data-[icon=inline-start]:pl-1.5 ga-:[&_svg:not([class*=size-])]:size-3",
39
+ sm: "ga-:h-7 ga-:gap-1 ga-:rounded-[min(var(--radius-md),12px)] ga-:px-2.5 ga-:text-[0.8rem] ga-:in-data-[slot=button-group]:rounded-lg ga-:has-data-[icon=inline-end]:pr-1.5 ga-:has-data-[icon=inline-start]:pl-1.5 ga-:[&_svg:not([class*=size-])]:size-3.5",
40
+ lg: "ga-:h-9 ga-:gap-1.5 ga-:px-2.5 ga-:has-data-[icon=inline-end]:pr-2 ga-:has-data-[icon=inline-start]:pl-2",
41
+ icon: "ga-:size-8",
42
+ "icon-xs": "ga-:size-6 ga-:rounded-[min(var(--radius-md),10px)] ga-:in-data-[slot=button-group]:rounded-lg ga-:[&_svg:not([class*=size-])]:size-3",
43
+ "icon-sm": "ga-:size-7 ga-:rounded-[min(var(--radius-md),12px)] ga-:in-data-[slot=button-group]:rounded-lg",
44
+ "icon-lg": "ga-:size-9"
45
+ }
46
+ },
47
+ defaultVariants: {
48
+ variant: "default",
49
+ size: "default"
50
+ }
51
+ }
52
+ );
53
+ function Button({
54
+ className,
55
+ variant = "default",
56
+ size = "default",
57
+ asChild = false,
58
+ ...props
59
+ }) {
60
+ const Comp = asChild ? Slot.Root : "button";
61
+ return /* @__PURE__ */ jsx(
62
+ Comp,
63
+ {
64
+ "data-slot": "button",
65
+ "data-variant": variant,
66
+ "data-size": size,
67
+ className: cn(buttonVariants({ variant, size, className })),
68
+ ...props
69
+ }
70
+ );
71
+ }
72
+
73
+ // src/components/ui/input.tsx
74
+ import "react";
75
+ import { jsx as jsx2 } from "react/jsx-runtime";
76
+ function Input({ className, type, ...props }) {
77
+ return /* @__PURE__ */ jsx2(
78
+ "input",
79
+ {
80
+ type,
81
+ "data-slot": "input",
82
+ className: cn(
83
+ "ga-:h-8 ga-:w-full ga-:min-w-0 ga-:rounded-lg ga-:border ga-:border-input ga-:bg-transparent ga-:px-2.5 ga-:py-1 ga-:text-base ga-:transition-colors ga-:outline-none ga-:file:inline-flex ga-:file:h-6 ga-:file:border-0 ga-:file:bg-transparent ga-:file:text-sm ga-:file:font-medium ga-:file:text-foreground ga-:placeholder:text-muted-foreground ga-:focus-visible:border-ring ga-:focus-visible:ring-3 ga-:focus-visible:ring-ring/50 ga-:disabled:pointer-events-none ga-:disabled:cursor-not-allowed ga-:disabled:bg-input/50 ga-:disabled:opacity-50 ga-:aria-invalid:border-destructive ga-:aria-invalid:ring-3 ga-:aria-invalid:ring-destructive/20 ga-:md:text-sm ga-:dark:bg-input/30 ga-:dark:disabled:bg-input/80 ga-:dark:aria-invalid:border-destructive/50 ga-:dark:aria-invalid:ring-destructive/40",
84
+ className
85
+ ),
86
+ ...props
87
+ }
88
+ );
89
+ }
90
+
91
+ // src/components/ui/resizable.tsx
92
+ import * as ResizablePrimitive from "react-resizable-panels";
93
+ import { jsx as jsx3 } from "react/jsx-runtime";
94
+ function ResizablePanelGroup({
95
+ className,
96
+ ...props
97
+ }) {
98
+ return /* @__PURE__ */ jsx3(
99
+ ResizablePrimitive.Group,
100
+ {
101
+ "data-slot": "resizable-panel-group",
102
+ className: cn(
103
+ "ga-:flex ga-:h-full ga-:w-full ga-:aria-[orientation=vertical]:flex-col",
104
+ className
105
+ ),
106
+ ...props
107
+ }
108
+ );
109
+ }
110
+ function ResizablePanel({ ...props }) {
111
+ return /* @__PURE__ */ jsx3(ResizablePrimitive.Panel, { "data-slot": "resizable-panel", ...props });
112
+ }
113
+ function ResizableHandle({
114
+ withHandle,
115
+ className,
116
+ ...props
117
+ }) {
118
+ return /* @__PURE__ */ jsx3(
119
+ ResizablePrimitive.Separator,
120
+ {
121
+ "data-slot": "resizable-handle",
122
+ className: cn(
123
+ "ga-:relative ga-:flex ga-:w-px ga-:items-center ga-:justify-center ga-:bg-border ga-:ring-offset-background ga-:after:absolute ga-:after:inset-y-0 ga-:after:left-1/2 ga-:after:w-1 ga-:after:-translate-x-1/2 ga-:focus-visible:ring-1 ga-:focus-visible:ring-ring ga-:focus-visible:outline-hidden ga-:aria-[orientation=horizontal]:h-px ga-:aria-[orientation=horizontal]:w-full ga-:aria-[orientation=horizontal]:after:left-0 ga-:aria-[orientation=horizontal]:after:h-1 ga-:aria-[orientation=horizontal]:after:w-full ga-:aria-[orientation=horizontal]:after:translate-x-0 ga-:aria-[orientation=horizontal]:after:-translate-y-1/2 ga-:[&[aria-orientation=horizontal]>div]:rotate-90",
124
+ className
125
+ ),
126
+ ...props,
127
+ children: withHandle && /* @__PURE__ */ jsx3("div", { className: "ga-:z-10 ga-:flex ga-:h-6 ga-:w-1 ga-:shrink-0 ga-:rounded-lg ga-:bg-border" })
128
+ }
129
+ );
130
+ }
131
+
132
+ // src/components/ui/scroll-area.tsx
133
+ import "react";
134
+ import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
135
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
136
+ function ScrollArea({
137
+ className,
138
+ children,
139
+ ...props
140
+ }) {
141
+ return /* @__PURE__ */ jsxs(
142
+ ScrollAreaPrimitive.Root,
143
+ {
144
+ "data-slot": "scroll-area",
145
+ className: cn("ga-:relative", className),
146
+ ...props,
147
+ children: [
148
+ /* @__PURE__ */ jsx4(
149
+ ScrollAreaPrimitive.Viewport,
150
+ {
151
+ "data-slot": "scroll-area-viewport",
152
+ className: "ga-:size-full ga-:rounded-[inherit] ga-:transition-[color,box-shadow] ga-:outline-none ga-:focus-visible:ring-[3px] ga-:focus-visible:ring-ring/50 ga-:focus-visible:outline-1",
153
+ children
154
+ }
155
+ ),
156
+ /* @__PURE__ */ jsx4(ScrollBar, {}),
157
+ /* @__PURE__ */ jsx4(ScrollAreaPrimitive.Corner, {})
158
+ ]
159
+ }
160
+ );
161
+ }
162
+ function ScrollBar({
163
+ className,
164
+ orientation = "vertical",
165
+ ...props
166
+ }) {
167
+ return /* @__PURE__ */ jsx4(
168
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
169
+ {
170
+ "data-slot": "scroll-area-scrollbar",
171
+ "data-orientation": orientation,
172
+ orientation,
173
+ className: cn(
174
+ "ga-:flex ga-:touch-none ga-:p-px ga-:transition-colors ga-:select-none ga-:data-horizontal:h-2.5 ga-:data-horizontal:flex-col ga-:data-horizontal:border-t ga-:data-horizontal:border-t-transparent ga-:data-vertical:h-full ga-:data-vertical:w-2.5 ga-:data-vertical:border-l ga-:data-vertical:border-l-transparent",
175
+ className
176
+ ),
177
+ ...props,
178
+ children: /* @__PURE__ */ jsx4(
179
+ ScrollAreaPrimitive.ScrollAreaThumb,
180
+ {
181
+ "data-slot": "scroll-area-thumb",
182
+ className: "ga-:relative ga-:flex-1 ga-:rounded-full ga-:bg-border"
183
+ }
184
+ )
185
+ }
186
+ );
187
+ }
188
+
189
+ // src/inspector.tsx
190
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
191
+ var key = (record) => String(record.captureSequence);
192
+ var time = (value) => value >= 1e10 ? new Date(value).toISOString().slice(11, 23) : `+${(value / 1e3).toFixed(2)}s`;
193
+ var stage = (record) => record.kind === "event-observed" ? "Source evidence" : record.kind === "decision" ? "Runtime decision" : "DOM delivery";
194
+ var title = (record) => record.kind === "event-observed" ? record.sourceType ?? "Observed event" : record.kind === "dom-delivery" ? `${record.deliveryStatus ?? "Delivery"} via ${record.deliveryMethod ?? "browser"}` : record.reason ?? record.disposition ?? "Runtime decision";
195
+ function correlation(record) {
196
+ const prefix = `${record.runtimeId}:`;
197
+ if (record.sourceEventId) return `${prefix}event:${record.sourceEventId}`;
198
+ if (record.responseId)
199
+ return `${prefix}response:${record.responseId}:${record.sourceType ?? "unknown"}`;
200
+ if (record.toolId)
201
+ return `${prefix}tool:${record.toolId}:${record.sourceType ?? "unknown"}`;
202
+ if (record.interactionId)
203
+ return `${prefix}interaction:${record.interactionId}`;
204
+ if (record.approvalId) return `${prefix}approval:${record.approvalId}`;
205
+ if (record.announcementId)
206
+ return `${prefix}announcement:${record.announcementId}`;
207
+ return `${prefix}record:${record.captureSequence}`;
208
+ }
209
+ function explain(record) {
210
+ if (record.kind === "event-observed")
211
+ return "The adapter supplied this normalized public lifecycle signal.";
212
+ if (record.kind === "dom-delivery")
213
+ return record.deliveryStatus === "unavailable" ? "The DOM driver was unavailable, so no browser delivery action was observed." : "The DOM driver reported this delivery action.";
214
+ return {
215
+ scheduled: "The runtime placed an announcement intent into its bounded queue.",
216
+ coalesced: "The runtime merged this work with an existing queued announcement.",
217
+ duplicate: "The runtime suppressed a recently delivered duplicate.",
218
+ "policy-silent": "The active policy intentionally made this event silent.",
219
+ "queue-capacity": "The bounded queue rejected or displaced this work.",
220
+ "scope-cancelled": "The lifecycle scope ended before this queued work delivered.",
221
+ "runtime-disposed": "The runtime was disposed and cancelled remaining queued work.",
222
+ "stale-response": "This signal did not match the active response epoch.",
223
+ "stale-tool": "This signal did not match the active tool instance.",
224
+ "delivery-error": "A registered announcement listener failed during delivery.",
225
+ delivered: "A registered runtime announcement listener accepted this intent."
226
+ }[record.reason ?? ""] ?? "The runtime recorded this policy decision.";
227
+ }
228
+ function useSnapshot(store) {
229
+ return React4.useSyncExternalStore(
230
+ React4.useCallback((listener) => store.subscribe(listener), [store]),
231
+ React4.useCallback(() => store.getSnapshot(), [store])
232
+ );
233
+ }
234
+ function CausalChain({
235
+ records
236
+ }) {
237
+ return /* @__PURE__ */ jsxs2("section", { className: "ga-causal-chain", "data-testid": "causal-chain", children: [
238
+ /* @__PURE__ */ jsxs2("div", { className: "ga-section-heading", children: [
239
+ /* @__PURE__ */ jsxs2("div", { children: [
240
+ /* @__PURE__ */ jsx5("p", { children: "Related evidence" }),
241
+ /* @__PURE__ */ jsx5("h3", { children: "Observed path" })
242
+ ] }),
243
+ /* @__PURE__ */ jsxs2("span", { children: [
244
+ records.length,
245
+ " records"
246
+ ] })
247
+ ] }),
248
+ /* @__PURE__ */ jsx5("ol", { children: records.map((record, index) => /* @__PURE__ */ jsxs2("li", { "data-stage": record.kind, children: [
249
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: String(index + 1).padStart(2, "0") }),
250
+ /* @__PURE__ */ jsxs2("div", { children: [
251
+ /* @__PURE__ */ jsx5("b", { children: stage(record) }),
252
+ /* @__PURE__ */ jsx5("code", { children: title(record) })
253
+ ] }),
254
+ /* @__PURE__ */ jsx5("time", { children: time(record.at) })
255
+ ] }, key(record))) })
256
+ ] });
257
+ }
258
+ function Detail({
259
+ record,
260
+ related,
261
+ snapshot
262
+ }) {
263
+ if (!record)
264
+ return /* @__PURE__ */ jsx5("aside", { className: "ga-trace-detail", "data-testid": "trace-detail", children: /* @__PURE__ */ jsxs2("div", { className: "ga-empty-state", children: [
265
+ /* @__PURE__ */ jsx5("p", { children: "Choose a record" }),
266
+ /* @__PURE__ */ jsx5("h3", { children: "Trace one accessibility decision from source to delivery." }),
267
+ /* @__PURE__ */ jsx5("span", { children: "Selection never changes the runtime, queue, or host focus." })
268
+ ] }) });
269
+ const source = snapshot.runtimeSources[record.runtimeSourceId ?? record.runtimeId];
270
+ const runtime = snapshot.runtimeSnapshots[record.runtimeId];
271
+ const deliveries = related.filter((item) => item.kind === "dom-delivery");
272
+ return /* @__PURE__ */ jsxs2("aside", { className: "ga-trace-detail", "data-testid": "trace-detail", children: [
273
+ /* @__PURE__ */ jsxs2("div", { className: "ga-detail-intro", children: [
274
+ /* @__PURE__ */ jsx5("p", { children: stage(record) }),
275
+ /* @__PURE__ */ jsx5("h3", { children: title(record) }),
276
+ /* @__PURE__ */ jsx5("span", { children: explain(record) })
277
+ ] }),
278
+ /* @__PURE__ */ jsxs2("section", { className: "ga-detail-section", children: [
279
+ /* @__PURE__ */ jsx5("h4", { children: "Why inspect this" }),
280
+ /* @__PURE__ */ jsx5("p", { children: record.reason?.startsWith("stale") ? "Compare the instance with the current runtime snapshot." : "Inspect related evidence and queue context if this was unexpected." })
281
+ ] }),
282
+ /* @__PURE__ */ jsx5(CausalChain, { records: related }),
283
+ /* @__PURE__ */ jsxs2("section", { className: "ga-detail-section", children: [
284
+ /* @__PURE__ */ jsx5("h4", { children: "Source evidence" }),
285
+ source ? /* @__PURE__ */ jsxs2("dl", { className: "ga-key-values", children: [
286
+ /* @__PURE__ */ jsxs2("div", { children: [
287
+ /* @__PURE__ */ jsx5("dt", { children: "Adapter" }),
288
+ /* @__PURE__ */ jsx5("dd", { children: source.adapter })
289
+ ] }),
290
+ /* @__PURE__ */ jsxs2("div", { children: [
291
+ /* @__PURE__ */ jsx5("dt", { children: "Interruption" }),
292
+ /* @__PURE__ */ jsx5("dd", { children: source.fidelity.interruption })
293
+ ] }),
294
+ /* @__PURE__ */ jsxs2("div", { children: [
295
+ /* @__PURE__ */ jsx5("dt", { children: "Retry" }),
296
+ /* @__PURE__ */ jsx5("dd", { children: source.fidelity.retries })
297
+ ] }),
298
+ /* @__PURE__ */ jsxs2("div", { children: [
299
+ /* @__PURE__ */ jsx5("dt", { children: "Connection" }),
300
+ /* @__PURE__ */ jsx5("dd", { children: source.fidelity.connection })
301
+ ] }),
302
+ /* @__PURE__ */ jsxs2("div", { className: "ga-key-values-wide", children: [
303
+ /* @__PURE__ */ jsx5("dt", { children: "Public signals" }),
304
+ /* @__PURE__ */ jsx5("dd", { children: source.evidence.join(", ") })
305
+ ] })
306
+ ] }) : /* @__PURE__ */ jsx5("p", { children: "Adapter evidence was not declared for this runtime." })
307
+ ] }),
308
+ /* @__PURE__ */ jsxs2("section", { className: "ga-detail-section", children: [
309
+ /* @__PURE__ */ jsx5("h4", { children: "Policy and scheduling" }),
310
+ runtime ? /* @__PURE__ */ jsxs2("dl", { className: "ga-key-values", children: [
311
+ /* @__PURE__ */ jsxs2("div", { children: [
312
+ /* @__PURE__ */ jsx5("dt", { children: "Queue" }),
313
+ /* @__PURE__ */ jsxs2("dd", { children: [
314
+ runtime.pendingCount,
315
+ " pending"
316
+ ] })
317
+ ] }),
318
+ /* @__PURE__ */ jsxs2("div", { children: [
319
+ /* @__PURE__ */ jsx5("dt", { children: "Minimum gap" }),
320
+ /* @__PURE__ */ jsxs2("dd", { children: [
321
+ runtime.policy.minimumGapMs,
322
+ " ms"
323
+ ] })
324
+ ] }),
325
+ /* @__PURE__ */ jsxs2("div", { children: [
326
+ /* @__PURE__ */ jsx5("dt", { children: "Due" }),
327
+ /* @__PURE__ */ jsx5("dd", { children: record.dueAt === void 0 ? "Not scheduled" : time(record.dueAt) })
328
+ ] }),
329
+ /* @__PURE__ */ jsxs2("div", { children: [
330
+ /* @__PURE__ */ jsx5("dt", { children: "Queue sequence" }),
331
+ /* @__PURE__ */ jsx5("dd", { children: record.queueSequence ?? "Not queued" })
332
+ ] })
333
+ ] }) : /* @__PURE__ */ jsx5("p", { children: "No current runtime snapshot is available." })
334
+ ] }),
335
+ /* @__PURE__ */ jsxs2("section", { className: "ga-detail-section", children: [
336
+ /* @__PURE__ */ jsx5("h4", { children: "Delivery evidence" }),
337
+ deliveries.length ? deliveries.map((delivery) => /* @__PURE__ */ jsx5("p", { children: /* @__PURE__ */ jsxs2("code", { children: [
338
+ delivery.deliveryMethod,
339
+ " \xB7 ",
340
+ delivery.deliveryStatus
341
+ ] }) }, key(delivery))) : /* @__PURE__ */ jsx5("p", { children: "No instrumented DOM delivery result is correlated with this record." }),
342
+ /* @__PURE__ */ jsx5("p", { className: "ga-boundary", children: "A runtime intent or DOM result does not prove that a screen reader spoke." })
343
+ ] }),
344
+ /* @__PURE__ */ jsxs2("details", { className: "ga-raw-record", children: [
345
+ /* @__PURE__ */ jsx5("summary", { children: "Structured record" }),
346
+ /* @__PURE__ */ jsx5("pre", { children: JSON.stringify(record, null, 2) })
347
+ ] })
348
+ ] });
349
+ }
350
+ function List({
351
+ records,
352
+ selected,
353
+ onSelect
354
+ }) {
355
+ const index = Math.max(
356
+ 0,
357
+ records.findIndex((record) => key(record) === selected)
358
+ );
359
+ return /* @__PURE__ */ jsxs2(
360
+ "div",
361
+ {
362
+ "aria-activedescendant": selected ? `trace-${selected}` : void 0,
363
+ "aria-label": "Captured accessibility trace",
364
+ className: "ga-trace-list",
365
+ onKeyDown: (event) => {
366
+ const direction = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
367
+ if (!direction || !records.length) return;
368
+ event.preventDefault();
369
+ const next = records[Math.max(0, Math.min(records.length - 1, index + direction))];
370
+ if (next) {
371
+ onSelect(key(next));
372
+ event.currentTarget.querySelector(`#trace-${key(next)}`)?.scrollIntoView({ block: "nearest" });
373
+ }
374
+ },
375
+ role: "listbox",
376
+ tabIndex: 0,
377
+ children: [
378
+ records.map((record) => /* @__PURE__ */ jsxs2(
379
+ "div",
380
+ {
381
+ "aria-selected": key(record) === selected,
382
+ className: "ga-trace-row",
383
+ id: `trace-${key(record)}`,
384
+ onClick: () => onSelect(key(record)),
385
+ role: "option",
386
+ children: [
387
+ /* @__PURE__ */ jsx5("time", { children: time(record.at) }),
388
+ /* @__PURE__ */ jsx5("span", { children: stage(record) }),
389
+ /* @__PURE__ */ jsx5("strong", { children: title(record) }),
390
+ /* @__PURE__ */ jsx5("code", { children: record.reason ?? record.sourceType ?? record.deliveryStatus ?? "recorded" })
391
+ ]
392
+ },
393
+ key(record)
394
+ )),
395
+ !records.length ? /* @__PURE__ */ jsx5("div", { className: "ga-empty-list", children: "No trace records match this view." }) : null
396
+ ]
397
+ }
398
+ );
399
+ }
400
+ function DevtoolsInspector({
401
+ store,
402
+ onClose,
403
+ onCopy
404
+ }) {
405
+ const snapshot = useSnapshot(store);
406
+ const reduceMotion = useReducedMotion();
407
+ const [query, setQuery] = React4.useState("");
408
+ const [filter, setFilter] = React4.useState("all");
409
+ const [selectedKey, setSelectedKey] = React4.useState();
410
+ const [feedback, setFeedback] = React4.useState();
411
+ const feedbackTimer = React4.useRef(void 0);
412
+ const actionRevision = React4.useRef(0);
413
+ const visible = snapshot.records.filter(
414
+ (record) => (filter === "all" || record.kind === filter) && [
415
+ record.kind,
416
+ record.sourceType,
417
+ record.reason,
418
+ record.disposition,
419
+ record.responseId,
420
+ record.toolId,
421
+ record.interactionId,
422
+ record.approvalId
423
+ ].filter(Boolean).join(" ").toLowerCase().includes(query.trim().toLowerCase())
424
+ ).slice().reverse();
425
+ const selected = visible.find((record) => key(record) === selectedKey) ?? visible[0];
426
+ const related = selected ? snapshot.records.filter(
427
+ (record) => correlation(record) === correlation(selected)
428
+ ) : [];
429
+ React4.useEffect(
430
+ () => () => {
431
+ if (feedbackTimer.current !== void 0)
432
+ window.clearTimeout(feedbackTimer.current);
433
+ },
434
+ []
435
+ );
436
+ const closeInspector = () => {
437
+ actionRevision.current += 1;
438
+ if (feedbackTimer.current !== void 0)
439
+ window.clearTimeout(feedbackTimer.current);
440
+ feedbackTimer.current = void 0;
441
+ setFeedback(void 0);
442
+ onClose();
443
+ };
444
+ const notice = (message) => {
445
+ if (feedbackTimer.current !== void 0)
446
+ window.clearTimeout(feedbackTimer.current);
447
+ setFeedback(message);
448
+ feedbackTimer.current = window.setTimeout(
449
+ () => setFeedback(void 0),
450
+ 2200
451
+ );
452
+ };
453
+ const copy = async () => {
454
+ if (!onCopy) return notice("Copy is unavailable in this mount");
455
+ const revision = ++actionRevision.current;
456
+ if (feedbackTimer.current !== void 0)
457
+ window.clearTimeout(feedbackTimer.current);
458
+ setFeedback("Copying trace");
459
+ try {
460
+ await onCopy(JSON.stringify(store.exportTrace(), null, 2));
461
+ if (actionRevision.current === revision) notice("Trace copied");
462
+ } catch {
463
+ if (actionRevision.current === revision)
464
+ notice("Copy could not complete");
465
+ }
466
+ };
467
+ return /* @__PURE__ */ jsxs2(
468
+ "section",
469
+ {
470
+ "aria-label": "Generative accessibility trace explorer",
471
+ className: "ga-inspector",
472
+ onKeyDown: (event) => {
473
+ if (event.key === "Escape") {
474
+ event.stopPropagation();
475
+ closeInspector();
476
+ }
477
+ },
478
+ children: [
479
+ /* @__PURE__ */ jsxs2("header", { className: "ga-inspector-header", children: [
480
+ /* @__PURE__ */ jsxs2("div", { children: [
481
+ /* @__PURE__ */ jsx5("p", { children: "Local diagnostic session" }),
482
+ /* @__PURE__ */ jsx5("h2", { children: "Accessibility trace" })
483
+ ] }),
484
+ /* @__PURE__ */ jsxs2("div", { className: "ga-header-actions", children: [
485
+ /* @__PURE__ */ jsxs2(
486
+ Button,
487
+ {
488
+ onClick: () => {
489
+ if (snapshot.paused) {
490
+ store.resumeCapture();
491
+ notice("Capture resumed");
492
+ } else {
493
+ store.pauseCapture();
494
+ notice("Capture paused");
495
+ }
496
+ },
497
+ size: "sm",
498
+ variant: "secondary",
499
+ children: [
500
+ snapshot.paused ? /* @__PURE__ */ jsx5(Play, { "aria-hidden": "true", size: 14 }) : /* @__PURE__ */ jsx5(Pause, { "aria-hidden": "true", size: 14 }),
501
+ snapshot.paused ? "Resume" : "Pause"
502
+ ]
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsx5(
506
+ Button,
507
+ {
508
+ "aria-label": "Refresh runtime snapshot",
509
+ onClick: () => {
510
+ store.refreshSnapshots();
511
+ notice("Runtime snapshot refreshed");
512
+ },
513
+ size: "icon-sm",
514
+ variant: "ghost",
515
+ children: /* @__PURE__ */ jsx5(RefreshCw, { "aria-hidden": "true", size: 15 })
516
+ }
517
+ ),
518
+ /* @__PURE__ */ jsx5(
519
+ Button,
520
+ {
521
+ "aria-label": "Close inspector",
522
+ onClick: closeInspector,
523
+ size: "icon-sm",
524
+ variant: "ghost",
525
+ children: /* @__PURE__ */ jsx5(X, { "aria-hidden": "true", size: 17 })
526
+ }
527
+ )
528
+ ] })
529
+ ] }),
530
+ /* @__PURE__ */ jsx5("div", { "aria-live": "polite", role: "status", children: /* @__PURE__ */ jsx5(AnimatePresence, { children: feedback ? /* @__PURE__ */ jsx5(
531
+ motion.div,
532
+ {
533
+ animate: reduceMotion ? false : { opacity: 1, y: 0 },
534
+ className: "ga-workspace-feedback",
535
+ "data-testid": "workspace-feedback",
536
+ ...reduceMotion ? {} : { exit: { opacity: 0, y: 4 } },
537
+ initial: reduceMotion ? false : { opacity: 0, y: 4 },
538
+ children: feedback
539
+ }
540
+ ) : null }) }),
541
+ /* @__PURE__ */ jsxs2("div", { className: "ga-session-toolbar", children: [
542
+ /* @__PURE__ */ jsxs2("label", { className: "ga-inspector-search", children: [
543
+ /* @__PURE__ */ jsx5(Search, { "aria-hidden": "true", size: 15 }),
544
+ /* @__PURE__ */ jsx5(
545
+ Input,
546
+ {
547
+ "aria-label": "Search trace",
548
+ onChange: (event) => setQuery(event.target.value),
549
+ placeholder: "Search safe metadata",
550
+ type: "search",
551
+ value: query
552
+ }
553
+ )
554
+ ] }),
555
+ /* @__PURE__ */ jsx5(
556
+ "div",
557
+ {
558
+ "aria-label": "Trace category",
559
+ className: "ga-filter-group",
560
+ role: "group",
561
+ children: ["all", "event-observed", "decision", "dom-delivery"].map(
562
+ (value) => /* @__PURE__ */ jsx5(
563
+ Button,
564
+ {
565
+ "aria-pressed": filter === value,
566
+ onClick: () => setFilter(value),
567
+ size: "sm",
568
+ variant: filter === value ? "secondary" : "ghost",
569
+ children: value === "all" ? "All" : value === "event-observed" ? "Source" : value === "decision" ? "Decisions" : "Delivery"
570
+ },
571
+ value
572
+ )
573
+ )
574
+ }
575
+ ),
576
+ /* @__PURE__ */ jsxs2("span", { className: "ga-session-count", children: [
577
+ snapshot.records.length,
578
+ " captured \xB7 ",
579
+ snapshot.droppedCount,
580
+ " dropped"
581
+ ] }),
582
+ /* @__PURE__ */ jsxs2(Button, { onClick: () => void copy(), size: "sm", variant: "ghost", children: [
583
+ /* @__PURE__ */ jsx5(Copy, { "aria-hidden": "true", size: 14 }),
584
+ " Copy"
585
+ ] }),
586
+ /* @__PURE__ */ jsx5(
587
+ Button,
588
+ {
589
+ onClick: () => {
590
+ store.clear();
591
+ setSelectedKey(void 0);
592
+ notice("Local trace cleared");
593
+ },
594
+ size: "sm",
595
+ variant: "ghost",
596
+ children: "Clear"
597
+ }
598
+ )
599
+ ] }),
600
+ /* @__PURE__ */ jsxs2(
601
+ ResizablePanelGroup,
602
+ {
603
+ className: "ga-explorer-layout",
604
+ orientation: "horizontal",
605
+ children: [
606
+ /* @__PURE__ */ jsx5(ResizablePanel, { defaultSize: 48, minSize: 34, children: /* @__PURE__ */ jsx5(ScrollArea, { className: "ga-trace-scroll", children: /* @__PURE__ */ jsx5(
607
+ List,
608
+ {
609
+ onSelect: setSelectedKey,
610
+ records: visible,
611
+ selected: selected ? key(selected) : void 0
612
+ }
613
+ ) }) }),
614
+ /* @__PURE__ */ jsx5(ResizableHandle, { withHandle: true }),
615
+ /* @__PURE__ */ jsx5(ResizablePanel, { defaultSize: 52, minSize: 34, children: /* @__PURE__ */ jsx5(Detail, { record: selected, related, snapshot }) })
616
+ ]
617
+ }
618
+ )
619
+ ]
620
+ }
621
+ );
622
+ }
623
+
624
+ // src/overlay.ts
625
+ var styles = `
626
+ :host { all: initial; color: #e7e5e4; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
627
+ *, *::before, *::after { box-sizing: border-box; }
628
+ button, input { font: inherit; }
629
+ button { cursor: pointer; }
630
+ button:focus-visible, input:focus-visible, [role="separator"]:focus-visible { outline: 1px solid #d6d3d1; outline-offset: 2px; }
631
+ .ga-launcher { position: fixed; z-index: 2147483646; left: 50%; bottom: 0; min-width: 184px; min-height: 34px; padding: 0 16px; color: #d6d3d1; background: #18181b; border: 1px solid #3f3f46; border-bottom: 0; border-radius: 8px 8px 0 0; box-shadow: 0 -8px 28px #0004; font-size: 12px; font-weight: 560; letter-spacing: -.005em; transform: translateX(-50%); }
632
+ .ga-launcher:hover { color: #fafaf9; background: #27272a; }
633
+ .ga-bottom-dock { position: fixed; z-index: 2147483647; right: 0; bottom: 0; left: 0; width: 100%; height: min(570px, 62vh); min-height: 340px; overflow: hidden; color: #e7e5e4; background: #18181b; border-top: 1px solid #3f3f46; border-radius: 12px 12px 0 0; box-shadow: 0 -20px 60px #0008; }
634
+ .ga-inspector { position: relative; display: flex; height: 100%; min-height: 0; flex-direction: column; background: #171717; }
635
+ .ga-inspector-header { display: flex; min-height: 48px; align-items: center; justify-content: space-between; gap: 16px; padding: 9px 18px; border-bottom: 1px solid #3f3f46; background: #18181b; }
636
+ [data-slot="button"] { display: inline-flex; min-height: 30px; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; color: #d6d3d1; background: transparent; border: 1px solid transparent; border-radius: 7px; font-size: 12px; font-weight: 600; }
637
+ [data-slot="button"][data-variant="outline"] { border-color: #52525b; background: #27272a; }
638
+ [data-slot="button"][data-variant="outline"]:hover, [data-slot="button"][data-variant="ghost"]:hover { color: #fafaf9; background: #3f3f46; }
639
+ [data-slot="button"][data-variant="secondary"] { color: #1c1917; background: #e7e5e4; }
640
+ [data-slot="button"][data-variant="secondary"]:hover { background: #fafaf9; }
641
+ [data-slot="button"][data-size="icon-sm"] { width: 30px; padding: 0; }
642
+ .ga-workspace-feedback { position: absolute; z-index: 5; top: 58px; right: 16px; padding: 8px 10px; color: #fafaf9; background: #27272a; border: 1px solid #52525b; border-radius: 7px; box-shadow: 0 10px 30px #0007; font-size: 12px; }
643
+ .ga-inspector-search { display: flex; width: min(360px, 50%); align-items: center; gap: 8px; padding: 0 9px; color: #a8a29e; background: #20201f; border: 1px solid #3f3f46; border-radius: 7px; }
644
+ [data-slot="input"] { width: 100%; height: 31px; padding: 0; color: #e7e5e4; background: transparent; border: 0; outline: 0; font-size: 12px; }
645
+ [data-slot="input"]::placeholder { color: #a8a29e; }
646
+ [data-slot="resizable-panel"] { min-width: 0; min-height: 0; }
647
+ [data-slot="resizable-handle"] { position: relative; width: 1px; background: #3f3f46; }
648
+ [data-slot="resizable-handle"] > div { position: absolute; top: calc(50% - 14px); left: -2px; width: 5px; height: 28px; background: #71717a; border-radius: 999px; }
649
+ [data-slot="scroll-area"], [data-slot="scroll-area-viewport"] { height: 100%; }
650
+ @media (prefers-reduced-motion: no-preference) { .ga-bottom-dock { animation: ga-inspector-enter 180ms cubic-bezier(.2,.8,.2,1); } @keyframes ga-inspector-enter { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } } }
651
+ :host { color: #171717; font-family: Manrope, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
652
+ button:focus-visible, input:focus-visible, [role="listbox"]:focus-visible, [role="separator"]:focus-visible { outline: 2px solid #111; outline-offset: 2px; }
653
+ .ga-launcher { color: #f7f7f7; background: #111; border-color: #111; border-radius: 4px 4px 0 0; box-shadow: none; }
654
+ .ga-launcher:hover { color: #fff; background: #333; }
655
+ .ga-bottom-dock { height: min(620px, 68vh); color: #171717; background: #f7f7f7; border-color: #111; border-radius: 0; box-shadow: 0 -12px 34px #0003; }
656
+ .ga-inspector { background: #f7f7f7; }
657
+ .ga-inspector-header { min-height: 58px; padding: 12px 24px; color: #111; background: #f7f7f7; border-color: #d0d0d0; }
658
+ .ga-inspector-header p, .ga-inspector-header h2 { margin: 0; }
659
+ .ga-inspector-header p, .ga-section-heading p, .ga-detail-intro > p { color: #5f5f5f; font: 600 10px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .08em; text-transform: uppercase; }
660
+ .ga-inspector-header h2 { margin-top: 3px; color: #111; font-size: 16px; font-weight: 650; letter-spacing: -.03em; }
661
+ .ga-header-actions { display: flex; align-items: center; gap: 4px; }
662
+ [data-slot="button"] { color: #171717; border-radius: 3px; font-weight: 600; }
663
+ [data-slot="button"][data-variant="secondary"] { color: #f7f7f7; background: #111; }
664
+ [data-slot="button"][data-variant="outline"] { color: #171717; border-color: #bdbdbd; background: transparent; }
665
+ [data-slot="button"][data-variant="ghost"]:hover { color: #111; background: #e8e8e8; }
666
+ .ga-session-toolbar { display: flex; min-height: 50px; align-items: center; gap: 8px; padding: 8px 24px; border-bottom: 1px solid #d0d0d0; }
667
+ .ga-inspector-search { width: min(330px, 35%); color: #5f5f5f; background: #fff; border-color: #d0d0d0; border-radius: 3px; }
668
+ [data-slot="input"] { color: #171717; }
669
+ .ga-filter-group { display: inline-flex; gap: 2px; }
670
+ .ga-filter-group [aria-pressed="true"] { color: #f7f7f7; background: #111; }
671
+ .ga-session-count { margin-left: auto; color: #5f5f5f; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }
672
+ .ga-explorer-layout { min-height: 0; flex: 1; }
673
+ .ga-inspector > [data-slot="resizable-panel-group"], .ga-explorer-layout { display: flex; min-height: 0; flex: 1; }
674
+ [data-slot="resizable-handle"] { background: #d0d0d0; }
675
+ .ga-trace-scroll, .ga-trace-detail { height: 100%; }
676
+ .ga-trace-list { min-height: 100%; padding: 8px 0; outline: none; }
677
+ .ga-trace-row { display: grid; grid-template-columns: 76px 110px minmax(130px, 1fr) auto; gap: 9px; align-items: center; min-height: 44px; padding: 8px 24px; cursor: pointer; border-bottom: 1px solid #e0e0e0; }
678
+ .ga-trace-row:hover, .ga-trace-row[aria-selected="true"] { background: #e8e8e8; }
679
+ .ga-trace-row time, .ga-trace-row span, .ga-trace-row code, .ga-causal-chain code, .ga-key-values dt, .ga-key-values dd { font: 600 10px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace; }
680
+ .ga-trace-row time, .ga-trace-row span, .ga-trace-row code { color: #5f5f5f; }
681
+ .ga-trace-row strong { overflow: hidden; color: #171717; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
682
+ .ga-empty-list { padding: 24px; color: #5f5f5f; font-size: 12px; }
683
+ .ga-trace-detail { overflow: auto; padding: 22px 24px; background: #fff; }
684
+ .ga-detail-intro h3, .ga-empty-state h3, .ga-section-heading h3 { margin: 4px 0 8px; color: #111; font-size: 16px; font-weight: 650; letter-spacing: -.03em; }
685
+ .ga-detail-intro > span, .ga-empty-state span, .ga-detail-section p { color: #5f5f5f; font-size: 12px; line-height: 1.55; }
686
+ .ga-detail-section { padding: 16px 0; border-top: 1px solid #d0d0d0; }
687
+ .ga-detail-section h4 { margin: 0 0 8px; color: #111; font-size: 12px; font-weight: 650; }
688
+ .ga-detail-section p { margin: 0 0 6px; }
689
+ .ga-boundary { padding-left: 10px; border-left: 2px solid #111; }
690
+ .ga-causal-chain { margin: 20px 0; padding: 16px 0; border-top: 1px solid #111; border-bottom: 1px solid #111; }
691
+ .ga-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
692
+ .ga-section-heading > span { color: #5f5f5f; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }
693
+ .ga-causal-chain ol { margin: 14px 0 0; padding: 0; list-style: none; }
694
+ .ga-causal-chain li { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; gap: 8px; align-items: center; min-height: 38px; border-top: 1px solid #e0e0e0; }
695
+ .ga-causal-chain li > span, .ga-causal-chain li b, .ga-causal-chain time { color: #5f5f5f; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }
696
+ .ga-causal-chain li code { display: block; margin-top: 3px; color: #111; }
697
+ .ga-key-values { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; margin: 0; background: #d0d0d0; border: 1px solid #d0d0d0; }
698
+ .ga-key-values > div { min-width: 0; padding: 8px; background: #f7f7f7; }
699
+ .ga-key-values-wide { grid-column: 1 / -1; }
700
+ .ga-key-values dt { color: #5f5f5f; text-transform: uppercase; }
701
+ .ga-key-values dd { overflow: hidden; margin: 4px 0 0; color: #111; text-overflow: ellipsis; white-space: nowrap; }
702
+ .ga-raw-record { margin-top: 16px; color: #171717; font-size: 12px; }
703
+ .ga-raw-record summary { cursor: pointer; font-weight: 650; }
704
+ .ga-raw-record pre { overflow: auto; margin: 10px 0 0; padding: 10px; color: #f7f7f7; background: #111; font: 10px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
705
+ .ga-workspace-feedback { top: 68px; color: #f7f7f7; background: #111; border-color: #111; border-radius: 3px; box-shadow: none; }
706
+ @media (max-width: 760px) { .ga-bottom-dock { height: min(720px, 80vh); } .ga-inspector-header, .ga-session-toolbar { padding-right: 12px; padding-left: 12px; } .ga-session-toolbar { flex-wrap: wrap; } .ga-inspector-search { width: 100%; } .ga-session-count { margin-left: 0; } .ga-explorer-layout { flex-direction: column; } [data-slot="resizable-handle"] { width: 100%; height: 1px; } .ga-trace-row { grid-template-columns: 70px 1fr; padding: 8px 12px; } .ga-trace-row span, .ga-trace-row code { grid-column: 2; } .ga-trace-detail { padding: 18px 12px; } .ga-key-values { grid-template-columns: 1fr; } .ga-key-values-wide { grid-column: auto; } }
707
+ `;
708
+ function mountDevtoolsOverlay(options) {
709
+ const selectedDocument = options.document ?? (typeof document === "undefined" ? void 0 : document);
710
+ if (!selectedDocument?.body)
711
+ throw new Error("A mountable document is required");
712
+ const host = selectedDocument.createElement("generative-a11y-devtools");
713
+ const shadow = host.attachShadow({ mode: "open" });
714
+ const style = selectedDocument.createElement("style");
715
+ style.textContent = styles;
716
+ const launcher = selectedDocument.createElement("button");
717
+ launcher.className = "ga-launcher";
718
+ launcher.type = "button";
719
+ launcher.textContent = "Open workspace";
720
+ launcher.setAttribute("aria-expanded", "false");
721
+ const panel = selectedDocument.createElement("aside");
722
+ panel.className = "ga-bottom-dock";
723
+ panel.hidden = true;
724
+ panel.tabIndex = -1;
725
+ panel.setAttribute(
726
+ "aria-label",
727
+ "Generative accessibility runtime inspector"
728
+ );
729
+ const reactContainer = selectedDocument.createElement("div");
730
+ reactContainer.style.height = "100%";
731
+ panel.append(reactContainer);
732
+ shadow.append(style, launcher, panel);
733
+ let root;
734
+ let open = false;
735
+ let disposed = false;
736
+ let previousFocus = null;
737
+ let focusBeforePointer = null;
738
+ const asHTMLElement = (element) => {
739
+ const HTMLElementConstructor = selectedDocument.defaultView?.HTMLElement;
740
+ return HTMLElementConstructor && element instanceof HTMLElementConstructor ? element : null;
741
+ };
742
+ const close = () => {
743
+ open = false;
744
+ panel.hidden = true;
745
+ panel.setAttribute("data-state", "collapsed");
746
+ launcher.setAttribute("aria-expanded", "false");
747
+ root?.unmount();
748
+ root = void 0;
749
+ (previousFocus ?? launcher).focus();
750
+ };
751
+ const openPanel = () => {
752
+ open = true;
753
+ panel.hidden = false;
754
+ panel.setAttribute("data-state", "expanded");
755
+ launcher.setAttribute("aria-expanded", "true");
756
+ if (!root) root = createRoot(reactContainer);
757
+ const copyText = options.copyText ?? ((value) => {
758
+ const clipboard = selectedDocument.defaultView?.navigator.clipboard;
759
+ if (!clipboard) throw new Error("Clipboard access is unavailable");
760
+ return clipboard.writeText(value);
761
+ });
762
+ root.render(
763
+ createElement(DevtoolsInspector, {
764
+ onClose: close,
765
+ onCopy: copyText,
766
+ store: options.store
767
+ })
768
+ );
769
+ panel.focus();
770
+ };
771
+ launcher.addEventListener("pointerdown", () => {
772
+ const active = asHTMLElement(selectedDocument.activeElement);
773
+ focusBeforePointer = active === host ? null : active;
774
+ });
775
+ launcher.addEventListener("click", () => {
776
+ if (open) close();
777
+ else {
778
+ const documentFocus = asHTMLElement(selectedDocument.activeElement);
779
+ previousFocus = focusBeforePointer ?? asHTMLElement(shadow.activeElement) ?? (documentFocus === selectedDocument.body ? null : documentFocus);
780
+ focusBeforePointer = null;
781
+ openPanel();
782
+ }
783
+ });
784
+ shadow.addEventListener("keydown", (event) => {
785
+ if (open && event.key === "Escape") close();
786
+ });
787
+ selectedDocument.body.append(host);
788
+ return {
789
+ host,
790
+ dispose() {
791
+ if (disposed) return;
792
+ disposed = true;
793
+ if (open) close();
794
+ else root?.unmount();
795
+ root = void 0;
796
+ host.remove();
797
+ }
798
+ };
799
+ }
800
+ export {
801
+ mountDevtoolsOverlay
802
+ };