@getpinax/widget 0.0.0-stage → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +267 -2
- package/dist/api.d.ts +62 -0
- package/dist/capture/breadcrumbs.d.ts +49 -0
- package/dist/capture/console.d.ts +17 -0
- package/dist/capture/env.d.ts +25 -0
- package/dist/capture/network.d.ts +23 -0
- package/dist/capture/ring.d.ts +14 -0
- package/dist/capture/signature.d.ts +2 -0
- package/dist/capture/text.d.ts +7 -0
- package/dist/capture/ua.d.ts +13 -0
- package/dist/chunks/editor-DbHLGwOA.mjs +1368 -0
- package/dist/chunks/privacy-DmU6v5Fr.mjs +39 -0
- package/dist/chunks/recorder-BUtEB_em.mjs +2859 -0
- package/dist/chunks/text-8QYEyRYs.mjs +18 -0
- package/dist/chunks/widget-CkR1Ptzb.mjs +961 -0
- package/dist/editor/annotator.d.ts +20 -0
- package/dist/editor/capture.d.ts +28 -0
- package/dist/editor/encode.d.ts +7 -0
- package/dist/editor/index.d.ts +5 -0
- package/dist/editor/mask.d.ts +29 -0
- package/dist/editor/pii.d.ts +22 -0
- package/dist/editor/shapes.d.ts +31 -0
- package/dist/editor-loader.d.ts +6 -0
- package/dist/editor-loader.embed.d.ts +5 -0
- package/dist/fixcheck.d.ts +31 -0
- package/dist/index.d.ts +46 -0
- package/dist/mount.d.ts +8 -0
- package/dist/navigation.d.ts +1 -0
- package/dist/paths.d.ts +4 -0
- package/dist/pinax-editor.js +1417 -0
- package/dist/pinax-recorder.js +2902 -0
- package/dist/pinax.js +113 -0
- package/dist/pinax.mjs +1041 -0
- package/dist/preview.d.ts +35 -0
- package/dist/preview.mjs +54 -0
- package/dist/privacy.d.ts +22 -0
- package/dist/queue.d.ts +9 -0
- package/dist/replay/buffer.d.ts +40 -0
- package/dist/replay/recorder.d.ts +15 -0
- package/dist/replay/sanitize.d.ts +13 -0
- package/dist/report.d.ts +109 -0
- package/dist/segment.d.ts +31 -0
- package/dist/settings.d.ts +31 -0
- package/dist/types.d.ts +137 -0
- package/dist/ui/color.d.ts +7 -0
- package/dist/ui/dom.d.ts +14 -0
- package/dist/ui/styles.d.ts +3 -0
- package/dist/ui/widget.d.ts +161 -0
- package/package.json +73 -4
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Annotation } from '../types';
|
|
2
|
+
import type { Rect } from './mask';
|
|
3
|
+
import { type Shape } from './shapes';
|
|
4
|
+
export type EditState = {
|
|
5
|
+
shapes: Shape[];
|
|
6
|
+
area: Rect | null;
|
|
7
|
+
};
|
|
8
|
+
export type EditResult = {
|
|
9
|
+
canvas: HTMLCanvasElement;
|
|
10
|
+
annotations: Annotation[];
|
|
11
|
+
state: EditState;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* W-04 to W-06, W-12: full-screen editor over the screenshot. Pointer events
|
|
15
|
+
* cover mouse, touch and pen; a tap (or Enter on the focused image) places a
|
|
16
|
+
* default-size mark, so drawing never needs a drag (WCAG 2.5.7).
|
|
17
|
+
*/
|
|
18
|
+
export declare function annotate(root: ShadowRoot, base: HTMLCanvasElement, initial?: EditState,
|
|
19
|
+
/** AI-12: areas blurred automatically. Baked into `base`; shown, never removable. */
|
|
20
|
+
auto?: Rect[]): Promise<EditResult | null>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Rect } from './mask';
|
|
2
|
+
/** Longest side of the captured image, in pixels. */
|
|
3
|
+
export declare const MAX_SIDE = 2560;
|
|
4
|
+
/**
|
|
5
|
+
* W-12: most pixels in one capture. iOS Safari refuses canvases over 16.7 M
|
|
6
|
+
* pixels and has a small total canvas memory budget (the editor holds about
|
|
7
|
+
* four canvases of this size), so stay well under it on every device.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_PIXELS = 5000000;
|
|
10
|
+
/** Pixel scale for a viewport: device pixels up to 2x, within both caps. */
|
|
11
|
+
export declare function captureScale(viewport: {
|
|
12
|
+
w: number;
|
|
13
|
+
h: number;
|
|
14
|
+
}, devicePixelRatio: number): number;
|
|
15
|
+
export type Capture = {
|
|
16
|
+
canvas: HTMLCanvasElement;
|
|
17
|
+
/** AI-12: areas blurred automatically, in image pixels. */
|
|
18
|
+
auto: Rect[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* W-03: an image of what the visitor can see right now. The widget itself
|
|
22
|
+
* (`exclude`) is left out, form fields and owner-marked elements are masked
|
|
23
|
+
* (C-07), and the live page is never modified. With `autoBlur` (AI-12),
|
|
24
|
+
* emails, phone numbers and likely names in the page text are masked too.
|
|
25
|
+
*/
|
|
26
|
+
export declare function captureViewport(exclude: Element | null, win?: Window, options?: {
|
|
27
|
+
autoBlur?: boolean;
|
|
28
|
+
}): Promise<Capture>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Aim well under the backend's 4.5 MB so reports upload quickly on phones. */
|
|
2
|
+
export declare const TARGET_BYTES: number;
|
|
3
|
+
/**
|
|
4
|
+
* The flattened image as WebP (JPEG where WebP encoding isn't supported, as
|
|
5
|
+
* in older Safari), lowering quality and then size until it fits.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeCanvas(source: HTMLCanvasElement, target?: number): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C-07/C-08: what is hidden in screenshots. Every form field is masked
|
|
3
|
+
* (passwords and card fields included), plus anything the owner marks with
|
|
4
|
+
* `data-pinax-mask` or the `pinax-mask` class. Button-like inputs stay
|
|
5
|
+
* visible because they carry no visitor data.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MASK_SELECTOR: string;
|
|
8
|
+
export type Rect = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
w: number;
|
|
12
|
+
h: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function isMasked(el: Element): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Viewport rectangles of every masked element that is on screen, read from
|
|
17
|
+
* the live page (which is never changed).
|
|
18
|
+
*/
|
|
19
|
+
export declare function collectMaskRects(doc: Document, viewport: {
|
|
20
|
+
w: number;
|
|
21
|
+
h: number;
|
|
22
|
+
}, exclude?: Element | null): Rect[];
|
|
23
|
+
/**
|
|
24
|
+
* Applied to each node of the screenshot library's private copy of the page,
|
|
25
|
+
* so typed values never reach the image even before the masks are painted.
|
|
26
|
+
*/
|
|
27
|
+
export declare function maskClone(node: Node): void;
|
|
28
|
+
/** Paints the board's striped "hidden" pattern over each rectangle. */
|
|
29
|
+
export declare function paintMasks(ctx: CanvasRenderingContext2D, rects: Rect[], scale: number): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Rect } from './mask';
|
|
2
|
+
/**
|
|
3
|
+
* AI-12: personal details in the visible page text, found before the
|
|
4
|
+
* screenshot is taken. Everything here only reads the live page; the
|
|
5
|
+
* capture paints over what it finds.
|
|
6
|
+
*/
|
|
7
|
+
export type PrivateHit = {
|
|
8
|
+
node: Text;
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
reason: 'email' | 'phone' | 'name';
|
|
12
|
+
} | {
|
|
13
|
+
element: Element;
|
|
14
|
+
reason: 'name';
|
|
15
|
+
};
|
|
16
|
+
/** Every email, phone number and likely name in the page's text. */
|
|
17
|
+
export declare function findPrivateText(doc: Document, exclude?: Element | null): PrivateHit[];
|
|
18
|
+
/** On-screen rectangles of the hits, in viewport pixels, padded a little. */
|
|
19
|
+
export declare function privateRects(doc: Document, viewport: {
|
|
20
|
+
w: number;
|
|
21
|
+
h: number;
|
|
22
|
+
}, exclude?: Element | null): Rect[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Annotation } from '../types';
|
|
2
|
+
import type { Rect } from './mask';
|
|
3
|
+
export type Tool = 'box' | 'arrow' | 'text' | 'blur';
|
|
4
|
+
/** In image pixels. For an arrow, (x, y) is the tail and (x + w, y + h) the head. */
|
|
5
|
+
export type Shape = {
|
|
6
|
+
type: Tool;
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
w: number;
|
|
10
|
+
h: number;
|
|
11
|
+
text?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const RED = "#FF5C35";
|
|
14
|
+
export declare const LABEL_BG = "#CC3F1B";
|
|
15
|
+
/** A box/blur with any drag direction, as a positive rectangle. */
|
|
16
|
+
export declare function normalise(s: Shape): Shape;
|
|
17
|
+
export declare function lineWidth(imgW: number, imgH: number): number;
|
|
18
|
+
export declare function fontSize(imgW: number, imgH: number): number;
|
|
19
|
+
/** Measures a text label so its box is known before it's drawn. */
|
|
20
|
+
export declare function measureLabel(ctx: CanvasRenderingContext2D, text: string, size: number): {
|
|
21
|
+
w: number;
|
|
22
|
+
h: number;
|
|
23
|
+
pad: number;
|
|
24
|
+
};
|
|
25
|
+
/** Draws one shape. Blur is pixelated from `base`, so nothing underneath survives. */
|
|
26
|
+
export declare function drawShape(ctx: CanvasRenderingContext2D, shape: Shape, base: CanvasImageSource, imgW: number, imgH: number): void;
|
|
27
|
+
/** Shapes as the report's `annotations`: fractions of the sent (cropped) image. */
|
|
28
|
+
export declare function toAnnotations(shapes: Shape[], area: Rect, measure: (s: Shape) => {
|
|
29
|
+
w: number;
|
|
30
|
+
h: number;
|
|
31
|
+
}): Annotation[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type EditorModule = typeof import('./editor');
|
|
2
|
+
export type RecorderModule = typeof import('./replay/recorder');
|
|
3
|
+
export declare function setEditorBase(_scriptSrc: string): void;
|
|
4
|
+
export declare function loadEditor(): Promise<EditorModule>;
|
|
5
|
+
/** AI-15: the session recorder, loaded only on sampled Pro and Agency visits. */
|
|
6
|
+
export declare function loadRecorder(): Promise<RecorderModule>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EditorModule, RecorderModule } from './editor-loader';
|
|
2
|
+
/** Called by embed.ts with the script tag's own src. */
|
|
3
|
+
export declare function setEditorBase(scriptSrc: string): void;
|
|
4
|
+
export declare function loadEditor(): Promise<EditorModule>;
|
|
5
|
+
export declare function loadRecorder(): Promise<RecorderModule>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ConsoleEntry } from './capture/console';
|
|
2
|
+
import type { NetworkEntry } from './capture/network';
|
|
3
|
+
import type { RingBuffer } from './capture/ring';
|
|
4
|
+
import type { FixCheck } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* AI-17 / P-14: after the owner marks a report Fixed, the backend lists its
|
|
7
|
+
* page path and error signature in the config. On a sampled share of page
|
|
8
|
+
* views of that path, the widget watches this page view's console errors
|
|
9
|
+
* and failed requests (from the existing capture) and, if the same
|
|
10
|
+
* signature shows up, says so once with `POST /fix-check`. Nothing else is
|
|
11
|
+
* ever sent: no "all clear", no page views, no log contents.
|
|
12
|
+
*/
|
|
13
|
+
export type FixCheckOptions = {
|
|
14
|
+
checks: FixCheck[];
|
|
15
|
+
/** Already the lower of `fixCheckSampleRate` and `samplingRate`. */
|
|
16
|
+
sampleRate: number;
|
|
17
|
+
console: RingBuffer<ConsoleEntry>;
|
|
18
|
+
network: RingBuffer<NetworkEntry>;
|
|
19
|
+
report: (check: FixCheck) => void;
|
|
20
|
+
path?: () => string;
|
|
21
|
+
random?: () => number;
|
|
22
|
+
now?: () => number;
|
|
23
|
+
};
|
|
24
|
+
/** The signature of a single entry, as if it were the report's only one. */
|
|
25
|
+
export declare function consoleSignature(entry: ConsoleEntry): string | null;
|
|
26
|
+
export declare function networkSignature(entry: NetworkEntry): string | null;
|
|
27
|
+
export declare function watchFixChecks(options: FixCheckOptions): {
|
|
28
|
+
/** Call on every client-side navigation: a new page view. */
|
|
29
|
+
navigated: () => void;
|
|
30
|
+
stop: () => void;
|
|
31
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Breadcrumb } from './capture/breadcrumbs';
|
|
2
|
+
import { type ConsoleEntry } from './capture/console';
|
|
3
|
+
import { type NetworkEntry } from './capture/network';
|
|
4
|
+
import { RingBuffer } from './capture/ring';
|
|
5
|
+
import type { Recorder } from './replay/recorder';
|
|
6
|
+
import type { PinaxOptions, PinaxUser, WidgetConfig } from './types';
|
|
7
|
+
import { WidgetUI } from './ui/widget';
|
|
8
|
+
export type { PinaxOptions, PinaxUser } from './types';
|
|
9
|
+
type State = {
|
|
10
|
+
options: PinaxOptions;
|
|
11
|
+
api: string;
|
|
12
|
+
root: ShadowRoot;
|
|
13
|
+
user?: PinaxUser;
|
|
14
|
+
config?: WidgetConfig;
|
|
15
|
+
ui?: WidgetUI;
|
|
16
|
+
console: RingBuffer<ConsoleEntry>;
|
|
17
|
+
network: RingBuffer<NetworkEntry>;
|
|
18
|
+
/** AI-11 */
|
|
19
|
+
breadcrumbs: RingBuffer<Breadcrumb>;
|
|
20
|
+
/** AI-15: only on sampled Pro and Agency visits, once loaded. */
|
|
21
|
+
recorder?: Recorder;
|
|
22
|
+
/** C-10: stops console or network capture when the site has it off. */
|
|
23
|
+
stopCapture: {
|
|
24
|
+
console?: () => void;
|
|
25
|
+
network?: () => void;
|
|
26
|
+
};
|
|
27
|
+
/** Run on every client-side navigation (one history watcher for all). */
|
|
28
|
+
onNavigate: Array<() => void>;
|
|
29
|
+
cleanups: Array<() => void>;
|
|
30
|
+
openWhenReady: boolean;
|
|
31
|
+
alive: boolean;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Starts the widget: context capture right away (C-04, C-05), then the
|
|
35
|
+
* button once settings arrive. Safe to call more than once; later calls are
|
|
36
|
+
* ignored.
|
|
37
|
+
*/
|
|
38
|
+
export declare function init(options: PinaxOptions): void;
|
|
39
|
+
/** Opens the feedback panel from the site's own link or button (S-05). */
|
|
40
|
+
export declare function open(): void;
|
|
41
|
+
/** Attaches the signed-in visitor to future reports (C-09). Can be called before init(). */
|
|
42
|
+
export declare function identify(user: PinaxUser | null): void;
|
|
43
|
+
/** Removes the widget and stops all capture. Mainly for tests and SPA teardown. */
|
|
44
|
+
export declare function destroy(): void;
|
|
45
|
+
/** @internal */
|
|
46
|
+
export declare function getState(): Readonly<State> | null;
|
package/dist/mount.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the widget's host element with an (open-mode) Shadow DOM so the
|
|
3
|
+
* widget's styles never leak into, or inherit from, the host page (W-14).
|
|
4
|
+
* Open mode keeps it reachable for the editor and tests; it is not a privacy
|
|
5
|
+
* boundary. Idempotent: a second call returns the existing root.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mountRoot(doc?: Document): ShadowRoot;
|
|
8
|
+
export declare function unmountRoot(doc?: Document): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function watchNavigation(onChange: () => void, target?: Window): () => void;
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** True when `pathname` matches `pattern` (see the rules above). */
|
|
2
|
+
export declare function pathMatches(pattern: string, pathname: string): boolean;
|
|
3
|
+
/** True when the button should be hidden on `pathname`. */
|
|
4
|
+
export declare function isHiddenPath(patterns: readonly string[] | undefined, pathname: string): boolean;
|