@reticlehq/browser 2.2.1 → 2.4.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.
- package/dist/actions/actions-dom.d.ts +0 -1
- package/dist/actions/actions-dom.js +0 -1
- package/dist/actions/actions.d.ts +3 -1
- package/dist/actions/actions.js +55 -36
- package/dist/actions/scroll.d.ts +0 -1
- package/dist/actions/scroll.js +0 -1
- package/dist/actions/value-input.d.ts +33 -0
- package/dist/actions/value-input.js +63 -0
- package/dist/commands/commands.d.ts +0 -1
- package/dist/commands/commands.js +27 -7
- package/dist/connect-options.d.ts +97 -0
- package/dist/connect-options.js +1 -0
- package/dist/dom/a11y.d.ts +0 -2
- package/dist/dom/a11y.js +54 -24
- package/dist/dom/auto-anchor.d.ts +0 -1
- package/dist/dom/auto-anchor.js +0 -1
- package/dist/dom/blind-spots.d.ts +0 -1
- package/dist/dom/blind-spots.js +0 -1
- package/dist/dom/chart.d.ts +0 -1
- package/dist/dom/chart.js +0 -1
- package/dist/dom/desktop-capture.d.ts +36 -0
- package/dist/dom/desktop-capture.js +113 -0
- package/dist/dom/dom-ignore.d.ts +7 -1
- package/dist/dom/dom-ignore.js +1 -2
- package/dist/dom/occlusion.d.ts +0 -1
- package/dist/dom/occlusion.js +0 -1
- package/dist/dom/query.d.ts +0 -1
- package/dist/dom/query.js +61 -13
- package/dist/dom/realm.d.ts +30 -0
- package/dist/dom/realm.js +58 -0
- package/dist/dom/refs.d.ts +0 -1
- package/dist/dom/refs.js +0 -1
- package/dist/dom/shadow-registry.d.ts +40 -0
- package/dist/dom/shadow-registry.js +165 -0
- package/dist/dom/snapshot.d.ts +2 -2
- package/dist/dom/snapshot.js +21 -4
- package/dist/dom/source.d.ts +0 -1
- package/dist/dom/source.js +0 -1
- package/dist/dom/theme.d.ts +0 -1
- package/dist/dom/theme.js +0 -1
- package/dist/dom/virtualized.d.ts +19 -0
- package/dist/dom/virtualized.js +80 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2 -3
- package/dist/observers/animation.d.ts +0 -1
- package/dist/observers/animation.js +0 -1
- package/dist/observers/blind-spots.d.ts +16 -4
- package/dist/observers/blind-spots.js +65 -11
- package/dist/observers/console.d.ts +0 -1
- package/dist/observers/console.js +0 -1
- package/dist/observers/dom.d.ts +0 -2
- package/dist/observers/dom.js +67 -9
- package/dist/observers/download.d.ts +6 -0
- package/dist/observers/download.js +103 -0
- package/dist/observers/focus.d.ts +0 -1
- package/dist/observers/focus.js +0 -1
- package/dist/observers/frames.d.ts +12 -0
- package/dist/observers/frames.js +67 -0
- package/dist/observers/health.d.ts +0 -1
- package/dist/observers/health.js +30 -2
- package/dist/observers/install-all.d.ts +5 -0
- package/dist/observers/install-all.js +63 -0
- package/dist/observers/ipc.d.ts +22 -0
- package/dist/observers/ipc.js +121 -0
- package/dist/observers/network-body.d.ts +17 -1
- package/dist/observers/network-body.js +26 -1
- package/dist/observers/network-redact.d.ts +0 -1
- package/dist/observers/network-redact.js +6 -3
- package/dist/observers/network-stream.d.ts +23 -0
- package/dist/observers/network-stream.js +102 -0
- package/dist/observers/network.d.ts +11 -1
- package/dist/observers/network.js +7 -1
- package/dist/observers/perf.d.ts +0 -1
- package/dist/observers/perf.js +0 -1
- package/dist/observers/route.d.ts +0 -1
- package/dist/observers/route.js +0 -1
- package/dist/observers/scroll.d.ts +0 -1
- package/dist/observers/scroll.js +0 -1
- package/dist/observers/sdk-failure.d.ts +28 -0
- package/dist/observers/sdk-failure.js +60 -0
- package/dist/observers/state.d.ts +0 -1
- package/dist/observers/state.js +0 -1
- package/dist/observers/storage.d.ts +0 -1
- package/dist/observers/storage.js +0 -1
- package/dist/observers/types.d.ts +0 -1
- package/dist/observers/types.js +0 -1
- package/dist/patching/capture-method.d.ts +0 -1
- package/dist/patching/capture-method.js +0 -1
- package/dist/presenter/overlay.d.ts +0 -1
- package/dist/presenter/overlay.js +0 -1
- package/dist/presenter/presenter-config.d.ts +0 -1
- package/dist/presenter/presenter-config.js +0 -1
- package/dist/presenter/presenter-controls.d.ts +0 -1
- package/dist/presenter/presenter-controls.js +0 -1
- package/dist/presenter/presenter-effects.d.ts +0 -1
- package/dist/presenter/presenter-effects.js +0 -1
- package/dist/presenter/presenter-glow.d.ts +0 -1
- package/dist/presenter/presenter-glow.js +0 -1
- package/dist/presenter/presenter-log.d.ts +0 -1
- package/dist/presenter/presenter-log.js +0 -1
- package/dist/presenter/presenter-run-state.d.ts +0 -1
- package/dist/presenter/presenter-run-state.js +0 -1
- package/dist/presenter/presenter-styles.d.ts +0 -1
- package/dist/presenter/presenter-styles.js +0 -1
- package/dist/presenter/presenter-tally.d.ts +0 -1
- package/dist/presenter/presenter-tally.js +0 -1
- package/dist/presenter/presenter-test-helpers.d.ts +0 -1
- package/dist/presenter/presenter-test-helpers.js +0 -1
- package/dist/presenter/presenter-verbs.d.ts +0 -1
- package/dist/presenter/presenter-verbs.js +0 -1
- package/dist/presenter/presenter.d.ts +0 -1
- package/dist/presenter/presenter.js +0 -1
- package/dist/recorder/recorder-styles.d.ts +0 -1
- package/dist/recorder/recorder-styles.js +0 -1
- package/dist/recorder/recorder.d.ts +0 -1
- package/dist/recorder/recorder.js +0 -1
- package/dist/registry/adapters.d.ts +0 -1
- package/dist/registry/adapters.js +0 -1
- package/dist/registry/capabilities.d.ts +2 -1
- package/dist/registry/capabilities.js +16 -1
- package/dist/registry/commit-and-signal.d.ts +0 -1
- package/dist/registry/commit-and-signal.js +0 -1
- package/dist/registry/domains.d.ts +0 -1
- package/dist/registry/domains.js +0 -1
- package/dist/registry/emitter.d.ts +0 -1
- package/dist/registry/emitter.js +0 -1
- package/dist/registry/store-adapters.d.ts +141 -7
- package/dist/registry/store-adapters.js +178 -1
- package/dist/registry/stores.d.ts +0 -1
- package/dist/registry/stores.js +8 -1
- package/dist/reticle-presenter-helpers.d.ts +0 -1
- package/dist/reticle-presenter-helpers.js +0 -1
- package/dist/reticle.d.ts +5 -59
- package/dist/reticle.js +37 -33
- package/dist/review/annotator.d.ts +0 -1
- package/dist/review/annotator.js +0 -1
- package/dist/review/mark-anchor.d.ts +0 -1
- package/dist/review/mark-anchor.js +0 -1
- package/dist/security/serialization.d.ts +0 -1
- package/dist/security/serialization.js +0 -1
- package/dist/timers/clock.d.ts +0 -1
- package/dist/timers/clock.js +0 -1
- package/dist/timers/native-console.d.ts +0 -1
- package/dist/timers/native-console.js +0 -1
- package/dist/timers/native-timers.d.ts +0 -1
- package/dist/timers/native-timers.js +0 -1
- package/dist/transport/transport.d.ts +12 -6
- package/dist/transport/transport.js +107 -17
- package/package.json +13 -3
- package/dist/actions/actions-dom.d.ts.map +0 -1
- package/dist/actions/actions-dom.js.map +0 -1
- package/dist/actions/actions.d.ts.map +0 -1
- package/dist/actions/actions.js.map +0 -1
- package/dist/actions/scroll.d.ts.map +0 -1
- package/dist/actions/scroll.js.map +0 -1
- package/dist/commands/commands.d.ts.map +0 -1
- package/dist/commands/commands.js.map +0 -1
- package/dist/dom/a11y.d.ts.map +0 -1
- package/dist/dom/a11y.js.map +0 -1
- package/dist/dom/auto-anchor.d.ts.map +0 -1
- package/dist/dom/auto-anchor.js.map +0 -1
- package/dist/dom/blind-spots.d.ts.map +0 -1
- package/dist/dom/blind-spots.js.map +0 -1
- package/dist/dom/chart.d.ts.map +0 -1
- package/dist/dom/chart.js.map +0 -1
- package/dist/dom/dom-ignore.d.ts.map +0 -1
- package/dist/dom/dom-ignore.js.map +0 -1
- package/dist/dom/occlusion.d.ts.map +0 -1
- package/dist/dom/occlusion.js.map +0 -1
- package/dist/dom/query.d.ts.map +0 -1
- package/dist/dom/query.js.map +0 -1
- package/dist/dom/refs.d.ts.map +0 -1
- package/dist/dom/refs.js.map +0 -1
- package/dist/dom/snapshot.d.ts.map +0 -1
- package/dist/dom/snapshot.js.map +0 -1
- package/dist/dom/source.d.ts.map +0 -1
- package/dist/dom/source.js.map +0 -1
- package/dist/dom/theme.d.ts.map +0 -1
- package/dist/dom/theme.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/observers/animation.d.ts.map +0 -1
- package/dist/observers/animation.js.map +0 -1
- package/dist/observers/blind-spots.d.ts.map +0 -1
- package/dist/observers/blind-spots.js.map +0 -1
- package/dist/observers/console.d.ts.map +0 -1
- package/dist/observers/console.js.map +0 -1
- package/dist/observers/dom.d.ts.map +0 -1
- package/dist/observers/dom.js.map +0 -1
- package/dist/observers/focus.d.ts.map +0 -1
- package/dist/observers/focus.js.map +0 -1
- package/dist/observers/health.d.ts.map +0 -1
- package/dist/observers/health.js.map +0 -1
- package/dist/observers/network-body.d.ts.map +0 -1
- package/dist/observers/network-body.js.map +0 -1
- package/dist/observers/network-redact.d.ts.map +0 -1
- package/dist/observers/network-redact.js.map +0 -1
- package/dist/observers/network.d.ts.map +0 -1
- package/dist/observers/network.js.map +0 -1
- package/dist/observers/perf.d.ts.map +0 -1
- package/dist/observers/perf.js.map +0 -1
- package/dist/observers/route.d.ts.map +0 -1
- package/dist/observers/route.js.map +0 -1
- package/dist/observers/scroll.d.ts.map +0 -1
- package/dist/observers/scroll.js.map +0 -1
- package/dist/observers/state.d.ts.map +0 -1
- package/dist/observers/state.js.map +0 -1
- package/dist/observers/storage.d.ts.map +0 -1
- package/dist/observers/storage.js.map +0 -1
- package/dist/observers/types.d.ts.map +0 -1
- package/dist/observers/types.js.map +0 -1
- package/dist/patching/capture-method.d.ts.map +0 -1
- package/dist/patching/capture-method.js.map +0 -1
- package/dist/presenter/overlay.d.ts.map +0 -1
- package/dist/presenter/overlay.js.map +0 -1
- package/dist/presenter/presenter-config.d.ts.map +0 -1
- package/dist/presenter/presenter-config.js.map +0 -1
- package/dist/presenter/presenter-controls.d.ts.map +0 -1
- package/dist/presenter/presenter-controls.js.map +0 -1
- package/dist/presenter/presenter-effects.d.ts.map +0 -1
- package/dist/presenter/presenter-effects.js.map +0 -1
- package/dist/presenter/presenter-glow.d.ts.map +0 -1
- package/dist/presenter/presenter-glow.js.map +0 -1
- package/dist/presenter/presenter-log.d.ts.map +0 -1
- package/dist/presenter/presenter-log.js.map +0 -1
- package/dist/presenter/presenter-run-state.d.ts.map +0 -1
- package/dist/presenter/presenter-run-state.js.map +0 -1
- package/dist/presenter/presenter-styles.d.ts.map +0 -1
- package/dist/presenter/presenter-styles.js.map +0 -1
- package/dist/presenter/presenter-tally.d.ts.map +0 -1
- package/dist/presenter/presenter-tally.js.map +0 -1
- package/dist/presenter/presenter-test-helpers.d.ts.map +0 -1
- package/dist/presenter/presenter-test-helpers.js.map +0 -1
- package/dist/presenter/presenter-verbs.d.ts.map +0 -1
- package/dist/presenter/presenter-verbs.js.map +0 -1
- package/dist/presenter/presenter.d.ts.map +0 -1
- package/dist/presenter/presenter.js.map +0 -1
- package/dist/recorder/recorder-styles.d.ts.map +0 -1
- package/dist/recorder/recorder-styles.js.map +0 -1
- package/dist/recorder/recorder.d.ts.map +0 -1
- package/dist/recorder/recorder.js.map +0 -1
- package/dist/registry/adapters.d.ts.map +0 -1
- package/dist/registry/adapters.js.map +0 -1
- package/dist/registry/capabilities.d.ts.map +0 -1
- package/dist/registry/capabilities.js.map +0 -1
- package/dist/registry/commit-and-signal.d.ts.map +0 -1
- package/dist/registry/commit-and-signal.js.map +0 -1
- package/dist/registry/domains.d.ts.map +0 -1
- package/dist/registry/domains.js.map +0 -1
- package/dist/registry/emitter.d.ts.map +0 -1
- package/dist/registry/emitter.js.map +0 -1
- package/dist/registry/store-adapters.d.ts.map +0 -1
- package/dist/registry/store-adapters.js.map +0 -1
- package/dist/registry/stores.d.ts.map +0 -1
- package/dist/registry/stores.js.map +0 -1
- package/dist/reticle-presenter-helpers.d.ts.map +0 -1
- package/dist/reticle-presenter-helpers.js.map +0 -1
- package/dist/reticle.d.ts.map +0 -1
- package/dist/reticle.js.map +0 -1
- package/dist/review/annotator.d.ts.map +0 -1
- package/dist/review/annotator.js.map +0 -1
- package/dist/review/mark-anchor.d.ts.map +0 -1
- package/dist/review/mark-anchor.js.map +0 -1
- package/dist/security/serialization.d.ts.map +0 -1
- package/dist/security/serialization.js.map +0 -1
- package/dist/timers/clock.d.ts.map +0 -1
- package/dist/timers/clock.js.map +0 -1
- package/dist/timers/native-console.d.ts.map +0 -1
- package/dist/timers/native-console.js.map +0 -1
- package/dist/timers/native-timers.d.ts.map +0 -1
- package/dist/timers/native-timers.js.map +0 -1
- package/dist/transport/transport.d.ts.map +0 -1
- package/dist/transport/transport.js.map +0 -1
|
@@ -21,6 +21,32 @@ const CAPTURABLE_CONTENT = /application\/json|text\/|application\/xml|x-www-form
|
|
|
21
21
|
* observability SDK. Never read these; the frame observer covers SSE properly.
|
|
22
22
|
*/
|
|
23
23
|
const STREAMING_CONTENT = /event-stream|x-ndjson|application\/stream/i;
|
|
24
|
+
/**
|
|
25
|
+
* Content types that stay OPEN by design — watching them would make every first settle pay the full
|
|
26
|
+
* watch bound for a stream doing exactly its job. Real SSE is covered by the EventSource observer.
|
|
27
|
+
*/
|
|
28
|
+
const ENDLESS_BY_DESIGN = /event-stream/i;
|
|
29
|
+
/**
|
|
30
|
+
* Is this response body still arriving after the request itself resolved?
|
|
31
|
+
*
|
|
32
|
+
* STRUCTURAL, not a content-type allowlist. The first version listed `x-component|x-ndjson|
|
|
33
|
+
* application/stream` — which is to say "Next.js RSC, plus two guesses". It closed the exact defect
|
|
34
|
+
* it was written against and left every other streamed response as invisible as before: a chunked
|
|
35
|
+
* `application/json` from a token API, a streamed HTML document, an export assembled server-side.
|
|
36
|
+
* Fixing one app's symptom and calling the class solved is how a false green comes back wearing
|
|
37
|
+
* different clothes.
|
|
38
|
+
*
|
|
39
|
+
* The real signal is on the wire: a response with a BODY and no `content-length` is being sent
|
|
40
|
+
* chunked, which is the definition of "the server is still writing". `content-length` present means
|
|
41
|
+
* the size was known up front and the body cannot grow.
|
|
42
|
+
*
|
|
43
|
+
* Deliberately independent of content type, with one exception above for streams that never end.
|
|
44
|
+
*/
|
|
45
|
+
export function isStreamingBody(contentType, contentLength) {
|
|
46
|
+
if (contentType !== null && ENDLESS_BY_DESIGN.test(contentType))
|
|
47
|
+
return false;
|
|
48
|
+
return contentLength === null;
|
|
49
|
+
}
|
|
24
50
|
/**
|
|
25
51
|
* Longest the SDK will wait on a response-body clone before emitting without it.
|
|
26
52
|
*
|
|
@@ -140,4 +166,3 @@ export function projectBody(rawText, contentType) {
|
|
|
140
166
|
const truncated = oversized || out.length > MAX_BODY_CHARS;
|
|
141
167
|
return { body: out.length > MAX_BODY_CHARS ? out.slice(0, MAX_BODY_CHARS) : out, truncated };
|
|
142
168
|
}
|
|
143
|
-
//# sourceMappingURL=network-body.js.map
|
|
@@ -30,13 +30,17 @@ export function redactUrl(raw) {
|
|
|
30
30
|
let newQuery = query;
|
|
31
31
|
if (query !== '') {
|
|
32
32
|
const params = new URLSearchParams(query);
|
|
33
|
+
let queryChanged = false;
|
|
33
34
|
for (const key of [...params.keys()]) {
|
|
34
35
|
if (isSensitiveKey(key)) {
|
|
35
36
|
params.set(key, REDACTED_VALUE);
|
|
36
|
-
|
|
37
|
+
queryChanged = true;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
if (queryChanged) {
|
|
41
|
+
newQuery = params.toString();
|
|
42
|
+
changed = true;
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
const segments = authority.split('/');
|
|
42
46
|
for (let i = 0; i + 1 < segments.length; i++) {
|
|
@@ -63,4 +67,3 @@ export function redactUrl(raw) {
|
|
|
63
67
|
const queryOut = queryStart === -1 ? '' : `?${newQuery}`;
|
|
64
68
|
return `${segments.join('/')}${queryOut}${newHash}`;
|
|
65
69
|
}
|
|
66
|
-
//# sourceMappingURL=network-redact.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Emit } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Watch a STREAMED response body to its end, so `settled` cannot pass while the server is still
|
|
4
|
+
* writing.
|
|
5
|
+
*
|
|
6
|
+
* `fetch` resolves at HEADERS. On a Next.js App Router page the RSC payload's NET_REQUEST landed
|
|
7
|
+
* 16 ms in, the shell rendered at 3013, and the Suspense boundary's rows arrived at 3902 — 889 ms
|
|
8
|
+
* in which NOTHING was observed. Quiet is exactly what settle tests for, so it passed with the
|
|
9
|
+
* fallback still on screen and zero rows rendered: a green on every streaming boundary.
|
|
10
|
+
*
|
|
11
|
+
* STRUCTURAL, not an allowlist: a response with a body and no `content-length` is being sent
|
|
12
|
+
* chunked, which is the wire's own way of saying the server is still writing. An earlier version
|
|
13
|
+
* gated on a content-type list containing `x-component` — "Next.js RSC, plus two guesses" — which
|
|
14
|
+
* closed the one defect it was written against and left every other streamed response as invisible
|
|
15
|
+
* as before. The clone is DRAINED as it arrives rather than buffered, so watching costs a second
|
|
16
|
+
* read of bytes already in flight, not a second copy held in memory.
|
|
17
|
+
*
|
|
18
|
+
* BOUNDED both ways: an endless stream (SSE over fetch, a live feed) must not hold `settled` open
|
|
19
|
+
* forever, so the watch gives up after STREAM_WATCH_MS and closes the record anyway, marked
|
|
20
|
+
* `gaveUp`. That is the safe direction — a settle that eventually proceeds beats one that never
|
|
21
|
+
* does — and it says which happened rather than pretending the stream ended.
|
|
22
|
+
*/
|
|
23
|
+
export declare function watchStreamedBody(emit: Emit, res: Response, id: string, url: string, contentType: string | null, contentLength: string | null): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching a STREAMED response body to its end, so `settled` cannot pass mid-stream.
|
|
3
|
+
*
|
|
4
|
+
* Split out of network.ts, which was already at its line cap. One question: when did this response
|
|
5
|
+
* actually finish arriving?
|
|
6
|
+
*/
|
|
7
|
+
import { EventType, StreamDirection, StreamTransport } from '@reticlehq/core';
|
|
8
|
+
import { isStreamingBody } from './network-body.js';
|
|
9
|
+
import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
|
|
10
|
+
/**
|
|
11
|
+
* Longest a streamed response body is watched before the record is closed anyway.
|
|
12
|
+
*
|
|
13
|
+
* An endless stream must never hold `settled` open forever. Ten seconds is longer than any
|
|
14
|
+
* page-render stream and short enough that a genuinely endless one costs one settle window, not the
|
|
15
|
+
* session.
|
|
16
|
+
*/
|
|
17
|
+
const STREAM_WATCH_MS = 10_000;
|
|
18
|
+
/**
|
|
19
|
+
* Watch a STREAMED response body to its end, so `settled` cannot pass while the server is still
|
|
20
|
+
* writing.
|
|
21
|
+
*
|
|
22
|
+
* `fetch` resolves at HEADERS. On a Next.js App Router page the RSC payload's NET_REQUEST landed
|
|
23
|
+
* 16 ms in, the shell rendered at 3013, and the Suspense boundary's rows arrived at 3902 — 889 ms
|
|
24
|
+
* in which NOTHING was observed. Quiet is exactly what settle tests for, so it passed with the
|
|
25
|
+
* fallback still on screen and zero rows rendered: a green on every streaming boundary.
|
|
26
|
+
*
|
|
27
|
+
* STRUCTURAL, not an allowlist: a response with a body and no `content-length` is being sent
|
|
28
|
+
* chunked, which is the wire's own way of saying the server is still writing. An earlier version
|
|
29
|
+
* gated on a content-type list containing `x-component` — "Next.js RSC, plus two guesses" — which
|
|
30
|
+
* closed the one defect it was written against and left every other streamed response as invisible
|
|
31
|
+
* as before. The clone is DRAINED as it arrives rather than buffered, so watching costs a second
|
|
32
|
+
* read of bytes already in flight, not a second copy held in memory.
|
|
33
|
+
*
|
|
34
|
+
* BOUNDED both ways: an endless stream (SSE over fetch, a live feed) must not hold `settled` open
|
|
35
|
+
* forever, so the watch gives up after STREAM_WATCH_MS and closes the record anyway, marked
|
|
36
|
+
* `gaveUp`. That is the safe direction — a settle that eventually proceeds beats one that never
|
|
37
|
+
* does — and it says which happened rather than pretending the stream ended.
|
|
38
|
+
*/
|
|
39
|
+
export function watchStreamedBody(emit, res, id, url, contentType, contentLength) {
|
|
40
|
+
// `=== null` is not enough: jsdom's Response has no `body` at all, and older/polyfilled
|
|
41
|
+
// implementations return undefined. Reaching for `.getReader()` on that threw an unhandled
|
|
42
|
+
// rejection out of a detached promise — an observability layer throwing into the host page, from
|
|
43
|
+
// code the host never called. Duck-type the reader instead of trusting the shape.
|
|
44
|
+
if (!isStreamingBody(contentType, contentLength))
|
|
45
|
+
return;
|
|
46
|
+
if (typeof res.body?.getReader !== 'function')
|
|
47
|
+
return;
|
|
48
|
+
let clone;
|
|
49
|
+
try {
|
|
50
|
+
clone = res.clone();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return; // already consumed or locked — the app owns it; observing it would take it away
|
|
54
|
+
}
|
|
55
|
+
const body = clone.body;
|
|
56
|
+
if (body === null)
|
|
57
|
+
return;
|
|
58
|
+
emit(EventType.NET_STREAM, {
|
|
59
|
+
transport: StreamTransport.FETCH,
|
|
60
|
+
direction: StreamDirection.OPEN,
|
|
61
|
+
url,
|
|
62
|
+
id,
|
|
63
|
+
});
|
|
64
|
+
void (async () => {
|
|
65
|
+
const reader = body.getReader();
|
|
66
|
+
let gaveUp = false;
|
|
67
|
+
// The deadline must be a TIMER, not a check after each read. An idle stream parks inside
|
|
68
|
+
// `read()` waiting for a chunk that may never come, so a post-read check never runs and the
|
|
69
|
+
// record never closes — which would hold `settled` open forever on exactly the endless streams
|
|
70
|
+
// the bound exists to protect against.
|
|
71
|
+
let timer;
|
|
72
|
+
const expired = new Promise((resolve) => {
|
|
73
|
+
timer = nativeSetTimeout(() => resolve('expired'), STREAM_WATCH_MS);
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
for (;;) {
|
|
77
|
+
const next = await Promise.race([reader.read(), expired]);
|
|
78
|
+
if (next === 'expired') {
|
|
79
|
+
gaveUp = true;
|
|
80
|
+
void reader.cancel().catch(() => undefined);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (next.done)
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* aborted or errored — the stream is over either way, which is what we report */
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
if (timer !== undefined)
|
|
92
|
+
nativeClearTimeout(timer);
|
|
93
|
+
}
|
|
94
|
+
emit(EventType.NET_STREAM, {
|
|
95
|
+
transport: StreamTransport.FETCH,
|
|
96
|
+
direction: StreamDirection.CLOSE,
|
|
97
|
+
url,
|
|
98
|
+
id,
|
|
99
|
+
...(gaveUp ? { gaveUp: true } : {}),
|
|
100
|
+
});
|
|
101
|
+
})();
|
|
102
|
+
}
|
|
@@ -2,9 +2,20 @@ import type { Emit, Teardown } from './types.js';
|
|
|
2
2
|
import { redactUrl } from './network-redact.js';
|
|
3
3
|
export { redactUrl };
|
|
4
4
|
/** Config for the network observer. Body capture is OFF by default and dev-only opt-in. */
|
|
5
|
+
/**
|
|
6
|
+
* Reinterpret a completed request's fields from its response.
|
|
7
|
+
*
|
|
8
|
+
* The seam exists so this observer stays ignorant of desktop IPC. A Tauri `invoke` arrives here as an
|
|
9
|
+
* ordinary fetch to an `ipc://` protocol, and the header that says whether the Rust command
|
|
10
|
+
* succeeded means nothing to a network observer — so the knowledge lives in the IPC observer and is
|
|
11
|
+
* passed IN, rather than this module importing it. Returns undefined to leave the record untouched.
|
|
12
|
+
*/
|
|
13
|
+
export type NetResponseReinterpreter = (url: string, header: (name: string) => string | null) => Record<string, unknown> | undefined;
|
|
5
14
|
export interface NetworkOptions {
|
|
6
15
|
/** Capture request/response bodies (text-like content only, redacted, per-body capped). */
|
|
7
16
|
captureBodies?: boolean;
|
|
17
|
+
/** Optional hook that reinterprets a completed request — see NetResponseReinterpreter. */
|
|
18
|
+
reinterpret?: NetResponseReinterpreter;
|
|
8
19
|
}
|
|
9
20
|
/** Pure: the first real app-code frame in a stack string, capped. Exported for unit testing. */
|
|
10
21
|
export declare function firstAppFrame(stack: string | undefined): string | undefined;
|
|
@@ -20,4 +31,3 @@ export interface NetTiming {
|
|
|
20
31
|
*/
|
|
21
32
|
export declare function extractTiming(entry: PerformanceResourceTiming | undefined): NetTiming;
|
|
22
33
|
export declare function installNetwork(emit: Emit, opts?: NetworkOptions): Teardown;
|
|
23
|
-
//# sourceMappingURL=network.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { BlindSpotKind, EventType, RETICLE_WS_PATH, StreamTransport, StreamDirec
|
|
|
2
2
|
import { captureMethod } from '../patching/capture-method.js';
|
|
3
3
|
import { isCapturableType, projectBody, withBodyDeadline } from './network-body.js';
|
|
4
4
|
import { redactUrl } from './network-redact.js';
|
|
5
|
+
import { watchStreamedBody } from './network-stream.js';
|
|
5
6
|
// Redaction moved to its own cohesive module (network.ts is at its line cap); re-exported so callers
|
|
6
7
|
// and the existing test suite keep importing it from here.
|
|
7
8
|
export { redactUrl };
|
|
@@ -191,6 +192,7 @@ function isNativeFetch(fn) {
|
|
|
191
192
|
const OURS = new WeakSet();
|
|
192
193
|
export function installNetwork(emit, opts = {}) {
|
|
193
194
|
const captureBodies = opts.captureBodies === true;
|
|
195
|
+
const reinterpret = opts.reinterpret;
|
|
194
196
|
// Keep the true original for teardown identity, plus a window-bound copy to invoke
|
|
195
197
|
// (fetch throws "Illegal invocation" if called with the wrong `this`).
|
|
196
198
|
const origFetch = window.fetch;
|
|
@@ -230,6 +232,8 @@ export function installNetwork(emit, opts = {}) {
|
|
|
230
232
|
// headers-received, so it stays honest whether or not we read the body.
|
|
231
233
|
const headersAt = performance.now();
|
|
232
234
|
const contentType = res.headers.get('content-type');
|
|
235
|
+
// The request is done; the BODY may not be. Watch it so settle cannot pass mid-stream.
|
|
236
|
+
watchStreamedBody(emit, res, id, url, contentType, res.headers.get('content-length'));
|
|
233
237
|
const emitRequest = (responseBodyFields) => {
|
|
234
238
|
emit(EventType.NET_REQUEST, {
|
|
235
239
|
id,
|
|
@@ -244,6 +248,9 @@ export function installNetwork(emit, opts = {}) {
|
|
|
244
248
|
...netResponseMeta(res.statusText, contentType, res.headers.get('content-length')),
|
|
245
249
|
...projectRequestBody(init?.body, captureBodies),
|
|
246
250
|
...responseBodyFields,
|
|
251
|
+
// Applied LAST so a reinterpreted verdict wins over the transport's own fields — a Tauri
|
|
252
|
+
// command that returned Err still travelled down a fetch that answered HTTP 200.
|
|
253
|
+
...(reinterpret?.(url, (name) => res.headers.get(name)) ?? {}),
|
|
247
254
|
});
|
|
248
255
|
};
|
|
249
256
|
if (captureBodies && isCapturableType(contentType)) {
|
|
@@ -511,4 +518,3 @@ export function installNetwork(emit, opts = {}) {
|
|
|
511
518
|
}
|
|
512
519
|
};
|
|
513
520
|
}
|
|
514
|
-
//# sourceMappingURL=network.js.map
|
package/dist/observers/perf.d.ts
CHANGED
package/dist/observers/perf.js
CHANGED
package/dist/observers/route.js
CHANGED
package/dist/observers/scroll.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Emit } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where in the SDK a failure happened. A fixed vocabulary of OUR module names — never a file path,
|
|
4
|
+
* never anything derived from the user's app — so the field stays low-cardinality and non-identifying
|
|
5
|
+
* while still telling a maintainer which subsystem to open.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SdkSite: {
|
|
8
|
+
readonly CONNECT: "connect";
|
|
9
|
+
readonly DOM_OBSERVER: "dom_observer";
|
|
10
|
+
readonly NETWORK_OBSERVER: "network_observer";
|
|
11
|
+
readonly CONSOLE_OBSERVER: "console_observer";
|
|
12
|
+
readonly IPC_OBSERVER: "ipc_observer";
|
|
13
|
+
readonly HEALTH_OBSERVER: "health_observer";
|
|
14
|
+
readonly ROUTER_OBSERVER: "router_observer";
|
|
15
|
+
readonly ANIMATION_OBSERVER: "animation_observer";
|
|
16
|
+
readonly STORE_ADAPTER: "store_adapter";
|
|
17
|
+
readonly SNAPSHOT: "snapshot";
|
|
18
|
+
readonly ACTION: "action";
|
|
19
|
+
readonly COMMAND: "command";
|
|
20
|
+
readonly RECORDER: "recorder";
|
|
21
|
+
readonly TRANSPORT: "transport";
|
|
22
|
+
};
|
|
23
|
+
export type SdkSite = (typeof SdkSite)[keyof typeof SdkSite];
|
|
24
|
+
/**
|
|
25
|
+
* Report that a piece of the SDK failed. Never throws, and never prevents the caller from doing what
|
|
26
|
+
* it was going to do anyway (which is almost always: carry on degraded).
|
|
27
|
+
*/
|
|
28
|
+
export declare function reportSdkFailure(emit: Emit, site: SdkSite, error: unknown): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK reporting its OWN failures.
|
|
3
|
+
*
|
|
4
|
+
* Half of Reticle runs inside the page, and that half was completely silent. Every `catch` in the SDK
|
|
5
|
+
* swallows by design — correctly, because an instrumentation layer must never break the app it
|
|
6
|
+
* observes — but the consequence was that an observer throwing on page load just produced a quieter
|
|
7
|
+
* product. No error, no signal, no way to know. A user would report "Reticle doesn't see my network
|
|
8
|
+
* calls" and there would be nothing on our side to look at.
|
|
9
|
+
*
|
|
10
|
+
* Two properties make this safe to add:
|
|
11
|
+
*
|
|
12
|
+
* 1. IT MAKES NO NETWORK REQUEST. The report rides the bridge WebSocket the SDK is already connected
|
|
13
|
+
* to — localhost, the same channel every other event uses. The published promise that the SDK
|
|
14
|
+
* talks only to a local bridge stays exactly true, and the daemon decides what (if anything) goes
|
|
15
|
+
* anywhere else. Adding an outbound call from inside someone's app would have been a different
|
|
16
|
+
* product with a different privacy posture.
|
|
17
|
+
* 2. IT CANNOT BREAK THE APP. Reporting is wrapped in its own try/catch and is strictly additive to
|
|
18
|
+
* an existing `catch` block. A failure to report a failure is discarded.
|
|
19
|
+
*/
|
|
20
|
+
import { EventType } from '@reticlehq/core';
|
|
21
|
+
/**
|
|
22
|
+
* Where in the SDK a failure happened. A fixed vocabulary of OUR module names — never a file path,
|
|
23
|
+
* never anything derived from the user's app — so the field stays low-cardinality and non-identifying
|
|
24
|
+
* while still telling a maintainer which subsystem to open.
|
|
25
|
+
*/
|
|
26
|
+
export const SdkSite = {
|
|
27
|
+
CONNECT: 'connect',
|
|
28
|
+
DOM_OBSERVER: 'dom_observer',
|
|
29
|
+
NETWORK_OBSERVER: 'network_observer',
|
|
30
|
+
CONSOLE_OBSERVER: 'console_observer',
|
|
31
|
+
IPC_OBSERVER: 'ipc_observer',
|
|
32
|
+
HEALTH_OBSERVER: 'health_observer',
|
|
33
|
+
ROUTER_OBSERVER: 'router_observer',
|
|
34
|
+
ANIMATION_OBSERVER: 'animation_observer',
|
|
35
|
+
STORE_ADAPTER: 'store_adapter',
|
|
36
|
+
SNAPSHOT: 'snapshot',
|
|
37
|
+
ACTION: 'action',
|
|
38
|
+
COMMAND: 'command',
|
|
39
|
+
RECORDER: 'recorder',
|
|
40
|
+
TRANSPORT: 'transport',
|
|
41
|
+
};
|
|
42
|
+
/** Cap the message here too — the server strips variables, but an unbounded string is a bad idea twice. */
|
|
43
|
+
const MAX_MESSAGE = 500;
|
|
44
|
+
/**
|
|
45
|
+
* Report that a piece of the SDK failed. Never throws, and never prevents the caller from doing what
|
|
46
|
+
* it was going to do anyway (which is almost always: carry on degraded).
|
|
47
|
+
*/
|
|
48
|
+
export function reportSdkFailure(emit, site, error) {
|
|
49
|
+
try {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
emit(EventType.SDK_FAILED, {
|
|
52
|
+
site,
|
|
53
|
+
message: message.slice(0, MAX_MESSAGE),
|
|
54
|
+
...(error instanceof Error ? { errorType: error.constructor.name.slice(0, 64) } : {}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* a failure report must never become a second failure */
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/observers/state.js
CHANGED
|
@@ -20,4 +20,3 @@ export declare function readStorage(area?: string): StorageSnapshot | Record<str
|
|
|
20
20
|
* method access) and re-dispatched via `.call(this)`. Fully reversible; values redacted like the reads.
|
|
21
21
|
*/
|
|
22
22
|
export declare function installStorage(emit: Emit): Teardown;
|
|
23
|
-
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -18,4 +18,3 @@ export type Teardown = () => void;
|
|
|
18
18
|
export declare function observeSafely(observation: () => void): void;
|
|
19
19
|
/** Like observeSafely, but for reading a value the observation needs; undefined if the read throws. */
|
|
20
20
|
export declare function observeValue<T>(read: () => T): T | undefined;
|
|
21
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/observers/types.js
CHANGED
|
@@ -24,4 +24,3 @@ export declare function captureMethod<T, K extends keyof T>(target: T, key: K):
|
|
|
24
24
|
* `set` is an unbound function, and naming that intention once beats disabling the rule at each use.
|
|
25
25
|
*/
|
|
26
26
|
export declare function captureValueSetter(proto: object): ((this: HTMLElement, value: string) => void) | undefined;
|
|
27
|
-
//# sourceMappingURL=capture-method.d.ts.map
|
|
@@ -83,4 +83,3 @@ export declare const DATA_ON = "data-on";
|
|
|
83
83
|
/** Overlay-root attribute toggled when the panel is minimised to a bar. */
|
|
84
84
|
export declare const MIN_ATTR = "data-reticle-min";
|
|
85
85
|
export declare const THROTTLED_ATTR = "data-reticle-throttled";
|
|
86
|
-
//# sourceMappingURL=presenter-config.d.ts.map
|
|
@@ -11,4 +11,3 @@ export declare function ringAround(ring: HTMLElement | undefined, rect: DOMRect)
|
|
|
11
11
|
export declare function spawnRipple(root: HTMLElement | undefined, x: number, y: number): void;
|
|
12
12
|
/** Pace for the human so an action is watchable, not instant. */
|
|
13
13
|
export declare function pace(ms: number): Promise<void>;
|
|
14
|
-
//# sourceMappingURL=presenter-effects.d.ts.map
|
|
@@ -40,4 +40,3 @@ export declare function formatElapsed(ms: number): string;
|
|
|
40
40
|
* Uses createElement/textContent (never innerHTML) so arbitrary narration text can't inject markup.
|
|
41
41
|
*/
|
|
42
42
|
export declare function appendLogRow(container: HTMLElement, kind: LogKind, text: string, ts: string, logMax: number): LogHandle;
|
|
43
|
-
//# sourceMappingURL=presenter-log.d.ts.map
|
|
@@ -4,4 +4,3 @@
|
|
|
4
4
|
* pure style data (no behavior). LOG_CSS + CONTROLS_CSS are composed in at the end as before.
|
|
5
5
|
*/
|
|
6
6
|
export declare const PRESENTER_CSS = "\n[data-reticle-glow]{position:fixed;inset:0;pointer-events:none;z-index:2147483600;opacity:0;\n transition:opacity .25s ease;box-shadow:inset 0 0 0 3px rgba(99,102,241,.9),inset 0 0 28px 6px rgba(99,102,241,.45);}\n[data-reticle-glow][data-on=\"1\"]{opacity:1;animation:reticle-pulse 1.6s ease-in-out infinite;}\n[data-reticle-glow][data-on=\"1\"][data-busy=\"1\"]{animation:reticle-shimmer 1.1s ease-in-out infinite;}\n@keyframes reticle-pulse{0%,100%{box-shadow:inset 0 0 0 3px rgba(99,102,241,.9),inset 0 0 22px 4px rgba(99,102,241,.35)}\n 50%{box-shadow:inset 0 0 0 3px rgba(124,127,242,1),inset 0 0 40px 10px rgba(99,102,241,.6)}}\n@keyframes reticle-shimmer{0%,100%{box-shadow:inset 0 0 0 3px rgba(124,127,242,1),inset 0 0 34px 8px rgba(99,102,241,.55)}\n 50%{box-shadow:inset 0 0 0 3px rgba(140,142,255,1),inset 0 0 48px 12px rgba(99,102,241,.7)}}\n[data-reticle-cursor]{position:fixed;top:0;left:0;width:22px;height:22px;margin:-11px 0 0 -11px;\n border:2px solid #6366f1;border-radius:50%;background:rgba(99,102,241,.25);pointer-events:none;\n z-index:2147483646;opacity:0;transition:transform .32s cubic-bezier(.22,1,.36,1),opacity .2s ease;}\n[data-reticle-cursor][data-on=\"1\"]{opacity:1;}\n[data-reticle-cursor]::after{content:\"\";position:absolute;inset:7px;border-radius:50%;background:#6366f1;}\n[data-reticle-ripple]{position:fixed;width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;\n background:rgba(99,102,241,.5);pointer-events:none;z-index:2147483645;animation:reticle-ripple .5s ease-out forwards;}\n@keyframes reticle-ripple{from{transform:scale(.4);opacity:.8}to{transform:scale(5);opacity:0}}\n[data-reticle-ring]{position:fixed;pointer-events:none;z-index:2147483644;border:2px solid #22c55e;border-radius:8px;\n box-shadow:0 0 0 3px rgba(34,197,94,.25);opacity:0;transition:opacity .15s ease;}\n[data-reticle-ring][data-on=\"1\"]{opacity:1;}\n[data-reticle-hud]{\n --reticle-accent:#7c83ff;--reticle-accent-soft:rgba(124,131,255,.16);\n --reticle-bg:rgba(13,15,22,.92);--reticle-bg2:rgba(19,22,32,.88);\n --reticle-fg:#e9ebf2;--reticle-muted:#9aa0b2;--reticle-faint:#6a7186;\n --reticle-line:rgba(255,255,255,.09);--reticle-line2:rgba(255,255,255,.05);\n --reticle-read:#54d2e6;--reticle-ok:#3dd7a6;--reticle-bad:#ff7a7a;\n --reticle-font:\"Inter\",system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;\n --reticle-serif:\"IBM Plex Serif\",Georgia,\"Times New Roman\",serif;\n position:fixed;left:50%;right:auto;bottom:20px;box-sizing:border-box;\n width:384px;height:468px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);\n display:flex;flex-direction:column;overflow:hidden;text-align:left;z-index:2147483647;pointer-events:none;\n font-family:var(--reticle-font);font-size:13px;line-height:1.5;color:var(--reticle-fg);-webkit-font-smoothing:antialiased;\n background:linear-gradient(180deg,var(--reticle-bg),var(--reticle-bg2));\n /* No backdrop-filter here, deliberately. blur(24px) on this panel was measured as the single most\n expensive thing the whole SDK does: a backdrop blur must re-sample everything behind it whenever\n that content changes, and on a page repainting at 60fps that is continuous full-panel work. On\n the hostile fixture it cost +4.0pp of main thread \u2014 five times the rest of the presenter, and\n more than every observer combined. Removing it took total main-thread time from 2.15s to 1.67s\n over an 8s window. The panel background is ~85% opaque, so the glass effect it bought was\n marginal. If you want it back, add it here and expect to pay that. */\n border:1px solid var(--reticle-line);border-radius:20px;\n box-shadow:0 28px 70px -18px rgba(0,0,0,.66),0 0 0 1px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.07),0 0 54px -22px var(--reticle-accent);\n opacity:0;transform:translateX(-50%) translateY(14px) scale(.985);\n transition:opacity .3s ease,transform .42s cubic-bezier(.16,1,.3,1),height .42s cubic-bezier(.16,1,.3,1),border-radius .42s ease,box-shadow .35s ease;}\n[data-reticle-overlay][data-reticle-state=\"paused\"] [data-reticle-hud]{--reticle-accent:#f6b44c;--reticle-accent-soft:rgba(246,180,76,.16);}\n[data-reticle-overlay][data-reticle-state=\"ended\"] [data-reticle-hud]{--reticle-accent:#3dd7a6;--reticle-accent-soft:rgba(61,215,166,.14);}\n[data-reticle-hud]::before{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;\n background:radial-gradient(130% 90% at 50% 0%,var(--reticle-accent-soft),transparent 60%);}\n[data-reticle-hud]>*{position:relative;}\n[data-reticle-hud][data-on=\"1\"]{opacity:1;transform:translateX(-50%) translateY(0) scale(1);}\n/* Click-through: the glassy panel itself never blocks the app \u2014 only its interactive controls\n capture clicks (buttons / inputs), so a human can click straight through the HUD to the page.\n The log auto-scrolls, so it stays click-through too (drag-scroll is traded for click-through). */\n[data-reticle-hud] button,[data-reticle-hud] input,[data-reticle-hud] textarea,\n[data-reticle-hud] select,[data-reticle-hud] [contenteditable]{pointer-events:auto;}\n/* When minimised to a pill, the whole bar is the (single) click target to restore. */\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud][data-on=\"1\"]{pointer-events:auto;}\n[data-reticle-hud] .reticle-hud-head{display:flex;align-items:center;gap:8px;flex:none;\n padding:12px 12px 12px 15px;border-bottom:1px solid var(--reticle-line2);}\n[data-reticle-hud] .reticle-dot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--reticle-accent);\n animation:reticle-breathe 2.6s ease-in-out infinite;}\n@keyframes reticle-breathe{0%,100%{box-shadow:0 0 0 0 var(--reticle-accent),0 0 7px 1px var(--reticle-accent);opacity:.85}\n 50%{box-shadow:0 0 0 4px var(--reticle-accent-soft),0 0 15px 3px var(--reticle-accent);opacity:1}}\n[data-reticle-hud] .reticle-brand{font-family:var(--reticle-serif);font-weight:500;font-size:15px;letter-spacing:.01em;color:var(--reticle-fg);}\n[data-reticle-hud] .reticle-head-sp{flex:1;}\n[data-reticle-hud] .reticle-live{display:none;flex:1;min-width:0;color:var(--reticle-muted);font-size:12.5px;\n white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n[data-reticle-hud] .reticle-maxhint{display:none;flex:none;color:var(--reticle-faint);font-size:13px;line-height:1;}\n[data-reticle-hud] .reticle-act-strip{flex:none;padding:7px 15px;border-bottom:1px solid var(--reticle-line2);background:rgba(0,0,0,.14);}\n[data-reticle-hud] .reticle-act{display:block;color:var(--reticle-muted);font-size:11.5px;\n white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n[data-reticle-hud] [data-reticle-min-btn]{flex:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;\n width:26px;height:26px;border-radius:8px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);\n color:var(--reticle-muted);font-size:15px;line-height:1;transition:background .15s,color .15s,transform .1s;}\n[data-reticle-hud] [data-reticle-min-btn]:hover{color:var(--reticle-fg);background:rgba(255,255,255,.08);}\n[data-reticle-hud] [data-reticle-min-btn]:active{transform:scale(.94);}\n[data-reticle-hud] .reticle-pass{color:var(--reticle-ok);}[data-reticle-hud] .reticle-fail{color:var(--reticle-bad);}\n[data-reticle-hud] .reticle-tally{flex:none;display:inline-flex;align-items:center;gap:8px;font-size:11.5px;\n font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em;}\n[data-reticle-hud] .reticle-tally[hidden]{display:none;}\n[data-reticle-hud] .reticle-tally .reticle-t-pass{color:var(--reticle-ok);}\n[data-reticle-hud] .reticle-tally .reticle-t-fail{color:var(--reticle-bad);}\n[data-reticle-hud] .reticle-tally [data-z=\"1\"]{opacity:.4;}\n@keyframes reticle-tally-pop{0%{transform:scale(1)}38%{transform:scale(1.3)}100%{transform:scale(1)}}\n[data-reticle-hud] .reticle-tally [data-bump=\"1\"]{display:inline-block;animation:reticle-tally-pop .36s cubic-bezier(.16,1,.3,1);}\n[data-reticle-hud] .reticle-chip{display:none;flex:none;font-size:9px;font-weight:600;letter-spacing:.08em;\n padding:2px 7px;border-radius:6px;vertical-align:middle;}\n[data-reticle-hud] .reticle-chip[data-mode=\"reading\"]{display:inline-block;color:var(--reticle-read);\n background:rgba(84,210,230,.12);border:1px solid rgba(84,210,230,.32);}\n[data-reticle-hud] .reticle-chip[data-mode=\"acting\"]{display:inline-block;color:var(--reticle-accent);\n background:var(--reticle-accent-soft);border:1px solid var(--reticle-accent);}\n[data-reticle-hud] .reticle-chip[data-mode=\"idle\"]{display:none;}\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud]{height:50px;border-radius:25px;cursor:pointer;}\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-hud-head{border-bottom:none;height:50px;padding:0 12px 0 16px;}\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-brand,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-chip,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-head-sp,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] [data-reticle-min-btn],\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-ctl,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-badge,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-act-strip,\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] [data-reticle-log],\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] [data-reticle-foot],\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-banner{display:none;}\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-live{display:block;}\n[data-reticle-overlay][data-reticle-min=\"1\"] [data-reticle-hud] .reticle-maxhint{display:inline-flex;}\n[data-reticle-mode=\"reading\"] [data-reticle-glow][data-on=\"1\"]{\n box-shadow:inset 0 0 0 3px rgba(34,211,238,.9),inset 0 0 28px 6px rgba(34,211,238,.4);}\n[data-reticle-mode=\"reading\"] [data-reticle-ring]{border-color:#22d3ee;\n box-shadow:0 0 0 3px rgba(34,211,238,.25);}\n[data-reticle-overlay][data-reticle-throttled=\"1\"] [data-reticle-glow][data-on=\"1\"]{\n box-shadow:inset 0 0 0 3px rgba(251,191,36,.9),inset 0 0 28px 6px rgba(251,191,36,.45);}\n[data-reticle-overlay][data-reticle-throttled=\"1\"] [data-reticle-hud]{--reticle-accent:#fbbf24;--reticle-accent-soft:rgba(251,191,36,.16);}\n\n[data-reticle-log]{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;\n gap:7px;padding:12px 14px;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.16) transparent;}\n[data-reticle-log]::-webkit-scrollbar{width:9px;}\n[data-reticle-log]::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}\n[data-reticle-log]::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.26);background-clip:content-box;}\n[data-reticle-log-row]{display:flex;align-items:baseline;gap:8px;font-size:12px;line-height:1.45;\n animation:reticle-row-in .26s cubic-bezier(.16,1,.3,1);}\n@keyframes reticle-row-in{from{opacity:0;transform:translateY(5px);}to{opacity:1;transform:none;}}\n[data-reticle-log-ts]{flex:none;color:var(--reticle-faint);font-size:10px;font-variant-numeric:tabular-nums;padding-top:1px;}\n[data-reticle-log] .reticle-log-text{flex:1;min-width:0;color:#d6dae4;overflow-wrap:anywhere;word-break:break-word;}\n[data-reticle-log] .reticle-res{flex:none;font-weight:700;}\n[data-reticle-log-row][data-kind=\"human\"]{align-self:flex-end;max-width:88%;\n background:var(--reticle-accent-soft);border:1px solid var(--reticle-accent);border-radius:13px 13px 4px 13px;padding:6px 11px;}\n[data-reticle-log-row][data-kind=\"human\"] [data-reticle-log-ts]{display:none;}\n[data-reticle-log-row][data-kind=\"human\"] .reticle-log-text{color:var(--reticle-fg);}\n\n\n[data-reticle-hud] .reticle-ctl{pointer-events:auto;cursor:pointer;flex:none;display:inline-flex;align-items:center;justify-content:center;\n height:26px;padding:0 11px;border-radius:8px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);\n color:var(--reticle-muted);font-family:var(--reticle-font);font-size:11px;font-weight:500;letter-spacing:.01em;line-height:1;\n transition:background .15s,color .15s,border-color .15s,transform .1s;}\n[data-reticle-hud] .reticle-ctl:hover{color:var(--reticle-fg);background:rgba(255,255,255,.09);}\n[data-reticle-hud] .reticle-ctl:active{transform:scale(.95);}\n[data-reticle-hud] .reticle-ctl:disabled{opacity:.35;cursor:default;}\n[data-reticle-hud] [data-reticle-end]{color:#ff9aa2;border-color:rgba(255,107,107,.22);}\n[data-reticle-hud] [data-reticle-end]:hover{color:#ff7a7a;border-color:rgba(255,107,107,.5);background:rgba(255,107,107,.1);}\n[data-reticle-hud] .reticle-badge{display:none;align-items:center;flex:none;font-weight:600;letter-spacing:.1em;font-size:9px;\n color:var(--reticle-accent);border:1px solid var(--reticle-accent);background:var(--reticle-accent-soft);padding:2px 8px;border-radius:999px;}\n[data-reticle-overlay][data-reticle-state=\"paused\"] [data-reticle-badge]{display:inline-flex;}\n[data-reticle-hud] [data-reticle-foot]{flex:none;padding:10px 12px 12px;border-top:1px solid var(--reticle-line2);background:rgba(0,0,0,.16);}\n[data-reticle-hud] .reticle-composer{display:flex;align-items:flex-end;gap:6px;background:rgba(255,255,255,.05);\n border:1px solid var(--reticle-line);border-radius:14px;padding:5px 6px 5px 14px;transition:border-color .15s,box-shadow .15s;}\n[data-reticle-hud] .reticle-composer:focus-within{border-color:var(--reticle-accent);box-shadow:0 0 0 3px var(--reticle-accent-soft);}\n[data-reticle-hud] .reticle-msg{flex:1;min-width:0;pointer-events:auto;background:transparent;border:none;outline:none;resize:none;\n box-sizing:border-box;color:var(--reticle-fg);font-family:var(--reticle-font);font-size:13px;line-height:18px;\n height:28px;min-height:28px;max-height:96px;padding:5px 0;overflow-y:auto;\n scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.14) transparent;}\n[data-reticle-hud] .reticle-msg::-webkit-scrollbar{width:9px;}\n[data-reticle-hud] .reticle-msg::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}\n[data-reticle-hud] .reticle-msg::placeholder{color:var(--reticle-faint);}\n[data-reticle-hud] .reticle-msg:disabled{opacity:.5;}\n[data-reticle-hud] .reticle-send{flex:none;width:30px;height:30px;padding:0;border-radius:10px;border:none;cursor:pointer;pointer-events:auto;\n background:var(--reticle-accent);color:#0b0d14;display:inline-flex;align-items:center;justify-content:center;transition:filter .15s,transform .1s;}\n[data-reticle-hud] .reticle-send svg{display:block;}\n[data-reticle-hud] .reticle-send:hover{filter:brightness(1.12);}\n[data-reticle-hud] .reticle-send:active{transform:scale(.9);}\n[data-reticle-hud] .reticle-send:disabled{opacity:.4;cursor:default;}\n[data-reticle-hud] .reticle-banner{display:none;flex:none;padding:8px 15px;color:var(--reticle-accent);\n font-size:11.5px;font-weight:500;border-bottom:1px solid var(--reticle-line2);background:var(--reticle-accent-soft);}\n[data-reticle-overlay][data-reticle-state=\"ended\"] [data-reticle-banner]{display:block;}\n/* Export row: hidden during a live session; revealed when ended so the run can be copied/saved. */\n[data-reticle-hud] .reticle-export{display:none;align-items:center;gap:8px;margin-top:9px;}\n[data-reticle-overlay][data-reticle-state=\"ended\"] [data-reticle-hud] .reticle-export{display:flex;}\n[data-reticle-hud] .reticle-export-msg{color:var(--reticle-ok);font-size:11px;opacity:0;transition:opacity .15s;}\n[data-reticle-hud] .reticle-export-msg[data-show=\"1\"]{opacity:1;}\n[data-reticle-overlay][data-reticle-state=\"paused\"] [data-reticle-glow][data-on=\"1\"]{animation:none;\n box-shadow:inset 0 0 0 3px rgba(246,180,76,.9),inset 0 0 30px 6px rgba(246,180,76,.4);}\n[data-reticle-overlay][data-reticle-state=\"ended\"] [data-reticle-glow][data-on=\"1\"]{animation:none;\n box-shadow:inset 0 0 0 2px rgba(61,215,166,.55);}\n/* Handoff tones tell the human the agent's mode at a glance. waiting = calm teal \"your turn\" (no\n alarm); ask = amber \"answer me\" with a pulse; warn = amber \"agent crashed\" with a pulse. Each leads\n the banner with an icon and overrides the calm ended-green accent. */\n[data-reticle-overlay][data-reticle-tone=\"waiting\"] [data-reticle-hud]{--reticle-accent:#38bdf8;--reticle-accent-soft:rgba(56,189,248,.16);}\n[data-reticle-overlay][data-reticle-tone=\"waiting\"] [data-reticle-banner]{font-weight:600;color:#7dd3fc;}\n[data-reticle-overlay][data-reticle-tone=\"waiting\"] [data-reticle-banner]::before{content:\"\\270B \";}\n[data-reticle-overlay][data-reticle-tone=\"waiting\"] [data-reticle-glow][data-on=\"1\"]{animation:none;\n box-shadow:inset 0 0 0 2px rgba(56,189,248,.5);}\n[data-reticle-overlay][data-reticle-tone=\"ask\"] [data-reticle-hud],\n[data-reticle-overlay][data-reticle-tone=\"warn\"] [data-reticle-hud]{--reticle-accent:#fb923c;--reticle-accent-soft:rgba(251,146,60,.18);}\n[data-reticle-overlay][data-reticle-tone=\"ask\"] [data-reticle-banner],\n[data-reticle-overlay][data-reticle-tone=\"warn\"] [data-reticle-banner]{font-weight:600;color:#fdba74;}\n[data-reticle-overlay][data-reticle-tone=\"ask\"] [data-reticle-banner]::before{content:\"\\2753 \";}\n[data-reticle-overlay][data-reticle-tone=\"warn\"] [data-reticle-banner]::before{content:\"\\26A0\\FE0F \";}\n[data-reticle-overlay][data-reticle-tone=\"ask\"] [data-reticle-glow][data-on=\"1\"],\n[data-reticle-overlay][data-reticle-tone=\"warn\"] [data-reticle-glow][data-on=\"1\"]{animation:reticle-warn-pulse 1.5s ease-in-out infinite;\n box-shadow:inset 0 0 0 2px rgba(251,146,60,.7);}\n@keyframes reticle-warn-pulse{0%,100%{box-shadow:inset 0 0 0 2px rgba(251,146,60,.32);}\n 50%{box-shadow:inset 0 0 0 3px rgba(251,146,60,.85),inset 0 0 26px 5px rgba(251,146,60,.34);}}\n/* Replay-a-flow row: the human re-runs a saved flow with no agent. Hidden until flows are pushed.\n Bounded + self-scrolling: it sits between the flex:1 log and the flex:none composer, so without a\n height cap a long flow list would squeeze the log to nothing and push the message input past the\n panel's overflow:hidden clip. flex:none + max-height + overflow-y keep the log and input always\n visible; extra flow chips scroll inside this section instead of growing the panel. */\n[data-reticle-hud] .reticle-flows{display:none;flex:none;flex-wrap:wrap;align-content:flex-start;gap:6px;\n padding:9px 12px;border-top:1px solid var(--reticle-line2);max-height:88px;overflow-y:auto;overscroll-behavior:contain;}\n[data-reticle-hud] .reticle-flows[data-has=\"1\"]{display:flex;}\n[data-reticle-hud] .reticle-flows::-webkit-scrollbar{width:9px;}\n[data-reticle-hud] .reticle-flows::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}\n[data-reticle-hud] .reticle-flows-cap{flex:0 0 100%;margin-bottom:1px;color:var(--reticle-faint);font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;}\n[data-reticle-hud] .reticle-flow{pointer-events:auto;cursor:pointer;display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 10px;\n border-radius:7px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);color:var(--reticle-muted);\n font-family:var(--reticle-font);font-size:11px;font-weight:500;transition:background .15s,color .15s,border-color .15s,transform .1s;}\n[data-reticle-hud] .reticle-flow:hover{color:var(--reticle-fg);background:var(--reticle-accent-soft);border-color:var(--reticle-accent);}\n[data-reticle-hud] .reticle-flow:active{transform:scale(.95);}\n";
|
|
7
|
-
//# sourceMappingURL=presenter-styles.d.ts.map
|