@devaccel-labs/app-comments-toolbar 1.0.0 → 1.3.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/README.md +152 -18
- package/dist/react.js +516 -61
- package/dist/server.js +342 -14
- package/dist/types/capture/api.d.ts +30 -0
- package/dist/types/capture/editor.d.ts +10 -0
- package/dist/types/capture/ignore.d.ts +19 -0
- package/dist/types/capture/index.d.ts +1 -0
- package/dist/types/capture/picker.d.ts +9 -0
- package/dist/types/capture/picture.d.ts +29 -0
- package/dist/types/capture/surface.d.ts +39 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/resolve.d.ts +2 -1
- package/dist/types/core/types.d.ts +11 -0
- package/dist/types/debug-context.d.ts +7 -0
- package/dist/types/dictation.d.ts +9 -0
- package/dist/types/embed/controller.d.ts +61 -0
- package/dist/types/embed/index.d.ts +1 -0
- package/dist/types/embed/options.d.ts +35 -0
- package/dist/types/help/index.d.ts +1 -0
- package/dist/types/help/panel-index.d.ts +1 -0
- package/dist/types/help/panel-styles.d.ts +1 -0
- package/dist/types/help/panel.d.ts +4 -0
- package/dist/types/help/pill-styles.d.ts +11 -0
- package/dist/types/help/pill.d.ts +44 -0
- package/dist/types/help/react.d.ts +12 -0
- package/dist/types/help/requests.d.ts +2 -0
- package/dist/types/help/types.d.ts +119 -0
- package/dist/types/help/wire.d.ts +61 -0
- package/dist/types/native-wire.d.ts +63 -0
- package/dist/types/native.d.ts +43 -0
- package/dist/types/react.d.ts +8 -1
- package/dist/types/replay.d.ts +6 -0
- package/dist/types/runtime/api.d.ts +250 -0
- package/dist/types/runtime/attachment-model.d.ts +16 -0
- package/dist/types/runtime/attachments.d.ts +43 -7
- package/dist/types/runtime/boot.d.ts +39 -0
- package/dist/types/runtime/browser-label.d.ts +5 -0
- package/dist/types/runtime/build-id.d.ts +7 -0
- package/dist/types/runtime/capture-ui.d.ts +45 -21
- package/dist/types/runtime/clarity-model.d.ts +260 -0
- package/dist/types/runtime/clarity-ui.d.ts +62 -0
- package/dist/types/runtime/client.d.ts +6 -197
- package/dist/types/runtime/composer-size.d.ts +30 -0
- package/dist/types/runtime/dashboard.d.ts +17 -2
- package/dist/types/runtime/debug-context-core.d.ts +88 -0
- package/dist/types/runtime/debug-context-host.d.ts +39 -0
- package/dist/types/runtime/debug-context-model.d.ts +352 -0
- package/dist/types/runtime/debug-context-redact.d.ts +48 -0
- package/dist/types/runtime/debug-context.d.ts +36 -0
- package/dist/types/runtime/details-model.d.ts +7 -0
- package/dist/types/runtime/details.d.ts +7 -5
- package/dist/types/runtime/dictation-engine.d.ts +98 -0
- package/dist/types/runtime/dictation-model.d.ts +28 -0
- package/dist/types/runtime/dictation-ui.d.ts +56 -0
- package/dist/types/runtime/dictation.d.ts +66 -0
- package/dist/types/runtime/draft-dock.d.ts +32 -0
- package/dist/types/runtime/draft-model.d.ts +104 -0
- package/dist/types/runtime/draft-store.d.ts +15 -0
- package/dist/types/runtime/edits.d.ts +1 -1
- package/dist/types/runtime/font.d.ts +16 -0
- package/dist/types/runtime/format.d.ts +30 -1
- package/dist/types/runtime/host-watch.d.ts +20 -0
- package/dist/types/runtime/icons.d.ts +16 -2
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/ink.d.ts +8 -0
- package/dist/types/runtime/kind-model.d.ts +14 -0
- package/dist/types/runtime/live-model.d.ts +25 -0
- package/dist/types/runtime/loader.d.ts +5 -11
- package/dist/types/runtime/markup-editor.d.ts +54 -0
- package/dist/types/runtime/markup-model.d.ts +126 -0
- package/dist/types/runtime/markup.d.ts +69 -0
- package/dist/types/runtime/mention-parse.d.ts +18 -0
- package/dist/types/runtime/mentions.d.ts +4 -4
- package/dist/types/runtime/page-background.d.ts +9 -0
- package/dist/types/runtime/page-image.d.ts +123 -0
- package/dist/types/runtime/placement.d.ts +6 -3
- package/dist/types/runtime/record.d.ts +18 -2
- package/dist/types/runtime/related.d.ts +1 -1
- package/dist/types/runtime/replay-model.d.ts +80 -0
- package/dist/types/runtime/replay.d.ts +5 -38
- package/dist/types/runtime/runtime.d.ts +6 -0
- package/dist/types/runtime/screen.d.ts +47 -0
- package/dist/types/runtime/screenshot.d.ts +60 -6
- package/dist/types/runtime/session-source.d.ts +45 -0
- package/dist/types/runtime/session.d.ts +20 -18
- package/dist/types/runtime/shield.d.ts +9 -0
- package/dist/types/runtime/shot.d.ts +45 -0
- package/dist/types/runtime/sketch.d.ts +26 -0
- package/dist/types/runtime/styles.d.ts +1 -1
- package/dist/types/runtime/sync.d.ts +1 -0
- package/dist/types/runtime/thread-model.d.ts +18 -0
- package/dist/types/runtime/ticket-form-model.d.ts +57 -0
- package/dist/types/runtime/ticket-form.d.ts +9 -0
- package/dist/types/runtime/toolbar.d.ts +37 -1
- package/dist/types/runtime/ui-scale.d.ts +17 -0
- package/dist/types/runtime/update.d.ts +37 -0
- package/dist/types/runtime/use-debug-context.d.ts +27 -0
- package/dist/types/server/index.d.ts +22 -0
- package/dist/types/server/support.d.ts +20 -0
- package/dist/types/server.d.ts +2 -0
- package/dist/types/vite/plugin.d.ts +15 -0
- package/dist/vite.js +121 -41
- package/package.json +26 -13
- package/src/capture/api.ts +128 -0
- package/src/capture/editor.tsx +178 -0
- package/src/capture/ignore.ts +38 -0
- package/src/capture/index.ts +27 -0
- package/src/capture/picker.tsx +171 -0
- package/src/capture/picture.ts +90 -0
- package/src/capture/surface.tsx +185 -0
- package/src/compiler/annotate.ts +202 -0
- package/src/compiler/babel.d.ts +11 -0
- package/src/compiler/enabled.ts +55 -0
- package/src/compiler/index.ts +2 -0
- package/src/core/index.ts +8 -0
- package/src/core/resolve.ts +307 -0
- package/src/core/types.ts +58 -0
- package/src/debug-context.ts +47 -0
- package/src/dictation.ts +17 -0
- package/src/embed/controller.ts +215 -0
- package/src/embed/index.ts +68 -0
- package/src/embed/options.ts +100 -0
- package/src/help/index.ts +38 -0
- package/src/help/panel-index.ts +17 -0
- package/src/help/panel-styles.ts +123 -0
- package/src/help/panel.ts +840 -0
- package/src/help/pill-styles.ts +75 -0
- package/src/help/pill.ts +338 -0
- package/src/help/react.tsx +143 -0
- package/src/help/requests.ts +111 -0
- package/src/help/types.ts +124 -0
- package/src/help/wire.ts +55 -0
- package/src/native-wire.ts +124 -0
- package/src/native.ts +282 -0
- package/src/react.ts +27 -0
- package/src/replay.ts +13 -0
- package/src/runtime/api.ts +531 -0
- package/src/runtime/attachment-model.ts +42 -0
- package/src/runtime/attachments.tsx +1260 -0
- package/src/runtime/boot.ts +95 -0
- package/src/runtime/browser-label.ts +31 -0
- package/src/runtime/build-id.ts +10 -0
- package/src/runtime/capture-ui.tsx +306 -0
- package/src/runtime/capture.ts +508 -0
- package/src/runtime/clarity-model.ts +634 -0
- package/src/runtime/clarity-ui.tsx +454 -0
- package/src/runtime/client.ts +151 -0
- package/src/runtime/composer-size.tsx +146 -0
- package/src/runtime/dashboard.tsx +1130 -0
- package/src/runtime/debug-context-core.ts +681 -0
- package/src/runtime/debug-context-host.ts +56 -0
- package/src/runtime/debug-context-model.ts +1319 -0
- package/src/runtime/debug-context-redact.ts +259 -0
- package/src/runtime/debug-context.ts +613 -0
- package/src/runtime/details-model.ts +30 -0
- package/src/runtime/details.tsx +395 -0
- package/src/runtime/dictation-engine.ts +616 -0
- package/src/runtime/dictation-model.ts +79 -0
- package/src/runtime/dictation-ui.tsx +363 -0
- package/src/runtime/dictation.ts +271 -0
- package/src/runtime/dock.ts +108 -0
- package/src/runtime/draft-dock.tsx +124 -0
- package/src/runtime/draft-model.ts +251 -0
- package/src/runtime/draft-store.ts +149 -0
- package/src/runtime/edits.tsx +165 -0
- package/src/runtime/font.ts +42 -0
- package/src/runtime/format.ts +129 -0
- package/src/runtime/freshness.ts +23 -0
- package/src/runtime/handoff.ts +76 -0
- package/src/runtime/host-watch.ts +64 -0
- package/src/runtime/icons.tsx +295 -0
- package/src/runtime/idle.ts +20 -0
- package/src/runtime/index.ts +11 -0
- package/src/runtime/ink.ts +6 -0
- package/src/runtime/kind-model.ts +20 -0
- package/src/runtime/live-model.ts +32 -0
- package/src/runtime/loader.tsx +87 -0
- package/src/runtime/markup-editor.tsx +1333 -0
- package/src/runtime/markup-model.ts +485 -0
- package/src/runtime/markup.ts +382 -0
- package/src/runtime/members.ts +58 -0
- package/src/runtime/mention-parse.ts +56 -0
- package/src/runtime/mentions.tsx +205 -0
- package/src/runtime/page-background.ts +51 -0
- package/src/runtime/page-image.ts +1592 -0
- package/src/runtime/placement.ts +336 -0
- package/src/runtime/record.ts +402 -0
- package/src/runtime/redact.ts +30 -0
- package/src/runtime/related.ts +34 -0
- package/src/runtime/render-page.ts +2 -0
- package/src/runtime/replay-model.ts +232 -0
- package/src/runtime/replay.ts +173 -0
- package/src/runtime/runtime-version.ts +7 -0
- package/src/runtime/runtime.tsx +51 -0
- package/src/runtime/screen.ts +182 -0
- package/src/runtime/screenshot.ts +260 -0
- package/src/runtime/session-source.ts +112 -0
- package/src/runtime/session.ts +108 -0
- package/src/runtime/shield.ts +34 -0
- package/src/runtime/shot.tsx +422 -0
- package/src/runtime/sketch.ts +139 -0
- package/src/runtime/styles.ts +1541 -0
- package/src/runtime/sync.ts +300 -0
- package/src/runtime/thread-model.ts +71 -0
- package/src/runtime/ticket-form-model.ts +128 -0
- package/src/runtime/ticket-form.tsx +342 -0
- package/src/runtime/toolbar.tsx +4427 -0
- package/src/runtime/ui-scale.ts +75 -0
- package/src/runtime/update.ts +98 -0
- package/src/runtime/use-debug-context.ts +81 -0
- package/src/server/index.ts +133 -0
- package/src/server/support.ts +499 -0
- package/src/server.ts +4 -0
- package/src/vite/plugin.ts +102 -0
- package/src/vite.ts +1 -0
- package/dist/chunks/chunk-CSKXLF33.js +0 -153
- package/dist/chunks/chunk-HUM5FQII.js +0 -160
- package/dist/chunks/render-page-76MLIT6M.js +0 -1421
- package/dist/chunks/runtime-JX75TKER.js +0 -9267
- package/dist/chunks/screenshot-66ARTBGR.js +0 -12
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ReactNode, type Ref } from "react";
|
|
2
|
+
import { type MarkupShape } from "./markup.js";
|
|
3
|
+
export type MarkupResult = {
|
|
4
|
+
/** The image with the markup flattened in. */
|
|
5
|
+
blob: Blob;
|
|
6
|
+
/** File name for `blob` (`flattenedName` of the original). */
|
|
7
|
+
name: string;
|
|
8
|
+
/** The shapes, kept so the image can be marked up again from the original. */
|
|
9
|
+
shapes: MarkupShape[];
|
|
10
|
+
}
|
|
11
|
+
/** Every shape was removed: the attachment goes back to the original file. */
|
|
12
|
+
| {
|
|
13
|
+
blob: null;
|
|
14
|
+
name: string;
|
|
15
|
+
shapes: [];
|
|
16
|
+
};
|
|
17
|
+
/** What an inline editor's owner can ask of it (see `MarkupEditorProps.mode`). */
|
|
18
|
+
export type MarkupEditorHandle = {
|
|
19
|
+
/**
|
|
20
|
+
* Flattens what has been drawn since the editor opened or last saved. Resolves null when nothing changed, and
|
|
21
|
+
* rejects when the image can't be flattened (the reason is shown in the editor).
|
|
22
|
+
*/
|
|
23
|
+
save(): Promise<MarkupResult | null>;
|
|
24
|
+
};
|
|
25
|
+
export type MarkupEditorProps = {
|
|
26
|
+
/**
|
|
27
|
+
* `dialog` (default): a header with Cancel and Done; Done hands the result over once and the owner closes the
|
|
28
|
+
* editor. `inline`: no header, the editor stays open after a save, its owner saves through `ref` (`save`) and
|
|
29
|
+
* Cmd+Enter calls `onSubmit`. Escape with nothing drawn still calls `onCancel`.
|
|
30
|
+
*/
|
|
31
|
+
mode?: "dialog" | "inline";
|
|
32
|
+
/** Inline mode's handle on the editor (`save`), for an owner that flattens on its own schedule. */
|
|
33
|
+
handle?: Ref<MarkupEditorHandle>;
|
|
34
|
+
/** URL of the original, unmarked image (a blob: URL from the draft tray). */
|
|
35
|
+
src: string;
|
|
36
|
+
/** The original's file name, shown in the header and used to name the result. */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Shapes from an earlier markup of this image; the editor starts with them so they stay editable. */
|
|
39
|
+
shapes: MarkupShape[];
|
|
40
|
+
/** Largest file the result may be (the upload limit). */
|
|
41
|
+
maxBytes: number;
|
|
42
|
+
/** Leaves without changing the attachment. */
|
|
43
|
+
onCancel: () => void;
|
|
44
|
+
/** Called once with the flattened image. The editor stays mounted until the parent closes it. */
|
|
45
|
+
onSave: (result: MarkupResult) => void;
|
|
46
|
+
/** Inline mode's Cmd+Enter: the result of saving first (null when nothing changed), then the owner's turn. */
|
|
47
|
+
onSubmit?: (result: MarkupResult | null) => void;
|
|
48
|
+
/** Every change to the shapes, as they stand, so the owner can keep them somewhere safe. */
|
|
49
|
+
onChange?: (shapes: MarkupShape[]) => void;
|
|
50
|
+
/** Inline mode's ribbon draws its own groups; the owner's groups (captures, narration) go after them. */
|
|
51
|
+
ribbon?: ReactNode;
|
|
52
|
+
};
|
|
53
|
+
/** Markup mode for an image in the media pane: replaces the pane's header and stage while open. */
|
|
54
|
+
export declare function MarkupEditor({ mode, handle, src, name, shapes: initialShapes, maxBytes, onCancel, onSave, onSubmit, onChange: onChangeProp, ribbon, }: MarkupEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The markup model: shapes in the image's own pixel space, sizes, colours, label layout, hit-testing, moves and
|
|
3
|
+
* undo history. DOM-free, so native clients draw the same shapes with their own renderer; the web toolbar's
|
|
4
|
+
* canvas renderer and flattening are in `./markup.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import { type Point } from "./ink.js";
|
|
7
|
+
export type { Point };
|
|
8
|
+
export type Box = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
w: number;
|
|
12
|
+
h: number;
|
|
13
|
+
};
|
|
14
|
+
export type MarkupTool = "select" | "pen" | "highlight" | "arrow" | "rect" | "ellipse" | "text";
|
|
15
|
+
export type MarkupSize = "s" | "m" | "l";
|
|
16
|
+
type ShapeBase = {
|
|
17
|
+
id: string;
|
|
18
|
+
color: string;
|
|
19
|
+
size: MarkupSize;
|
|
20
|
+
};
|
|
21
|
+
export type StrokeShape = ShapeBase & {
|
|
22
|
+
kind: "pen" | "highlight";
|
|
23
|
+
points: Point[];
|
|
24
|
+
};
|
|
25
|
+
export type ArrowShape = ShapeBase & {
|
|
26
|
+
kind: "arrow";
|
|
27
|
+
from: Point;
|
|
28
|
+
to: Point;
|
|
29
|
+
};
|
|
30
|
+
export type BoxShape = ShapeBase & {
|
|
31
|
+
kind: "rect" | "ellipse";
|
|
32
|
+
box: Box;
|
|
33
|
+
};
|
|
34
|
+
export type TextShape = ShapeBase & {
|
|
35
|
+
kind: "text";
|
|
36
|
+
at: Point;
|
|
37
|
+
text: string;
|
|
38
|
+
};
|
|
39
|
+
export type MarkupShape = StrokeShape | ArrowShape | BoxShape | TextShape;
|
|
40
|
+
/** Colours offered in the editor. The first is the toolbar's sketch ink. */
|
|
41
|
+
export declare const MARKUP_COLORS: ReadonlyArray<{
|
|
42
|
+
name: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}>;
|
|
45
|
+
export declare const DEFAULT_INK = "#ff3b30";
|
|
46
|
+
export declare const DEFAULT_HIGHLIGHT = "#ffcc00";
|
|
47
|
+
/** Highlighter strokes are drawn at this opacity so the page under them stays readable. */
|
|
48
|
+
export declare const HIGHLIGHT_ALPHA = 0.38;
|
|
49
|
+
/** Most undo steps kept. */
|
|
50
|
+
export declare const HISTORY_LIMIT = 100;
|
|
51
|
+
/**
|
|
52
|
+
* Sizes in image pixels for one image. `unit` is max(1, longest side / 1000), so markup looks the same on a 1x
|
|
53
|
+
* screenshot and a 3x one. Pen and arrow strokes: s 2u, m 4u, l 8u. Highlighter: 5x the pen width. Text font:
|
|
54
|
+
* s 14u, m 20u, l 30u.
|
|
55
|
+
*/
|
|
56
|
+
export type MarkupMetrics = {
|
|
57
|
+
unit: number;
|
|
58
|
+
stroke: (size: MarkupSize) => number;
|
|
59
|
+
highlight: (size: MarkupSize) => number;
|
|
60
|
+
font: (size: MarkupSize) => number;
|
|
61
|
+
};
|
|
62
|
+
/** Width in px of one line of text at `fontPx`, set in `markupFont(fontPx)`. */
|
|
63
|
+
export type MeasureText = (text: string, fontPx: number) => number;
|
|
64
|
+
/** Laid-out text label: the filled box in image px, its lines, and where each line's top-left sits. */
|
|
65
|
+
export type TextLayout = {
|
|
66
|
+
box: Box;
|
|
67
|
+
lines: string[];
|
|
68
|
+
font: number;
|
|
69
|
+
pad: number;
|
|
70
|
+
lineHeight: number;
|
|
71
|
+
};
|
|
72
|
+
export declare function markupMetrics(width: number, height: number): MarkupMetrics;
|
|
73
|
+
/** CSS font for labels: `600 <px>px` in the system UI stack. */
|
|
74
|
+
export declare function markupFont(px: number): string;
|
|
75
|
+
/**
|
|
76
|
+
* Label text for a fill: `#ffffff` while it keeps at least 3:1 contrast (WCAG AA for large bold text, which
|
|
77
|
+
* labels are), else whichever of white and `#1d1d1f` reads better. Red, blue, and purple keep white text;
|
|
78
|
+
* orange, green, yellow, and white get dark text.
|
|
79
|
+
*/
|
|
80
|
+
export declare function contrastText(color: string): string;
|
|
81
|
+
/** Lays a label out: lines split on "\n", line height 1.25 em, padding 0.4 em, top-left at `shape.at`. */
|
|
82
|
+
export declare function textLayout(shape: TextShape, metrics: MarkupMetrics, measure: MeasureText): TextLayout;
|
|
83
|
+
/** The area a shape paints, stroke width included, in image px. */
|
|
84
|
+
export declare function shapeBounds(shape: MarkupShape, metrics: MarkupMetrics, measure: MeasureText): Box;
|
|
85
|
+
/**
|
|
86
|
+
* Id of the topmost shape under `point` (later shapes are on top), or null. Strokes and arrows hit within half
|
|
87
|
+
* their width plus `tolerance` of the line; rectangles and ellipses within that of their outline (not their
|
|
88
|
+
* inside, so a box drawn around something never hides it); text anywhere in its label box.
|
|
89
|
+
*/
|
|
90
|
+
export declare function hitTest(shapes: MarkupShape[], point: Point, tolerance: number, metrics: MarkupMetrics, measure: MeasureText): string | null;
|
|
91
|
+
/** The same shape moved by (dx, dy); a new object, the input is left alone. */
|
|
92
|
+
export declare function moveShape<T extends MarkupShape>(shape: T, dx: number, dy: number): T;
|
|
93
|
+
/** The box between two drag points, any direction. `square` makes it w = h = the larger side, kept on the drag side. */
|
|
94
|
+
export declare function normalizeBox(a: Point, b: Point, square: boolean): Box;
|
|
95
|
+
/** `to` moved onto the nearest 45° ray from `from`, keeping its distance. */
|
|
96
|
+
export declare function snapAngle(from: Point, to: Point): Point;
|
|
97
|
+
/**
|
|
98
|
+
* Whether a just-drawn shape is worth keeping: a stroke must span `minSize` px in at least one direction, an
|
|
99
|
+
* arrow must be at least `minSize` long, a box must be at least `minSize` both ways, and text must have
|
|
100
|
+
* non-blank text.
|
|
101
|
+
*/
|
|
102
|
+
export declare function isMeaningful(shape: MarkupShape, minSize: number): boolean;
|
|
103
|
+
/** The original's base name with `.png` or `.jpg` for the flattened type ("shot.jpeg" → "shot.png"). */
|
|
104
|
+
export declare function flattenedName(name: string, type: "image/png" | "image/jpeg"): string;
|
|
105
|
+
/** Undo history. `present` is what is on screen. */
|
|
106
|
+
export type MarkupHistory = {
|
|
107
|
+
past: MarkupShape[][];
|
|
108
|
+
present: MarkupShape[];
|
|
109
|
+
future: MarkupShape[][];
|
|
110
|
+
};
|
|
111
|
+
export declare function createHistory(shapes: MarkupShape[]): MarkupHistory;
|
|
112
|
+
/** Pushes `next` as a new step and clears redo; returns `history` itself when `next === history.present`. */
|
|
113
|
+
export declare function commitHistory(history: MarkupHistory, next: MarkupShape[]): MarkupHistory;
|
|
114
|
+
/** Returns `history` itself when there is nothing to undo. */
|
|
115
|
+
export declare function undoHistory(history: MarkupHistory): MarkupHistory;
|
|
116
|
+
/** Returns `history` itself when there is nothing to redo. */
|
|
117
|
+
export declare function redoHistory(history: MarkupHistory): MarkupHistory;
|
|
118
|
+
/** Width a stroke paints, before any hit tolerance. Highlighter is wider by design. */
|
|
119
|
+
export declare function strokeWidth(shape: StrokeShape, metrics: MarkupMetrics): number;
|
|
120
|
+
/** Arrow head length and half width, both from the shaft width and the image's unit. */
|
|
121
|
+
export declare function arrowHead(lineWidth: number, unit: number): {
|
|
122
|
+
length: number;
|
|
123
|
+
halfWidth: number;
|
|
124
|
+
};
|
|
125
|
+
/** A box as left/top with non-negative sides, so negative widths from odd input still draw. */
|
|
126
|
+
export declare function toRect(box: Box): Box;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markup drawn over an attached image before it is sent: pen, highlighter, arrows, boxes, ellipses, and text
|
|
3
|
+
* labels. Shapes live in the image's own pixel space, so the same list draws the on-screen overlay (scaled to
|
|
4
|
+
* the pane) and the flattened file (at full size). This file is the canvas renderer and flattening; the
|
|
5
|
+
* DOM-free shape model is `./markup-model.ts`, re-exported here. Everything takes its canvas as an argument, so
|
|
6
|
+
* it runs under unit tests without a real canvas.
|
|
7
|
+
*/
|
|
8
|
+
import { type Box, type MarkupMetrics, type MarkupShape, type MeasureText } from "./markup-model.js";
|
|
9
|
+
export * from "./markup-model.js";
|
|
10
|
+
/** Canvas area browsers reliably allocate (Safari's cap is 16 777 216 px); a bigger image is flattened smaller. */
|
|
11
|
+
export declare const MAX_CANVAS_PIXELS = 16000000;
|
|
12
|
+
/** The 2D context calls the renderer uses. A real CanvasRenderingContext2D satisfies it; tests pass a recorder. */
|
|
13
|
+
export type MarkupContext = Pick<CanvasRenderingContext2D, "save" | "restore" | "beginPath" | "closePath" | "moveTo" | "lineTo" | "quadraticCurveTo" | "arcTo" | "ellipse" | "rect" | "stroke" | "fill" | "fillText" | "measureText" | "drawImage" | "scale" | "setLineDash"> & {
|
|
14
|
+
strokeStyle: CanvasRenderingContext2D["strokeStyle"];
|
|
15
|
+
fillStyle: CanvasRenderingContext2D["fillStyle"];
|
|
16
|
+
lineWidth: number;
|
|
17
|
+
lineCap: CanvasLineCap;
|
|
18
|
+
lineJoin: CanvasLineJoin;
|
|
19
|
+
globalAlpha: number;
|
|
20
|
+
globalCompositeOperation: GlobalCompositeOperation;
|
|
21
|
+
font: string;
|
|
22
|
+
textBaseline: CanvasTextBaseline;
|
|
23
|
+
textAlign: CanvasTextAlign;
|
|
24
|
+
};
|
|
25
|
+
export type FlattenCanvas = {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
getContext(kind: "2d"): MarkupContext | null;
|
|
29
|
+
toBlob(callback: (blob: Blob | null) => void, type?: string, quality?: number): void;
|
|
30
|
+
};
|
|
31
|
+
export type FlattenInput = {
|
|
32
|
+
/** The original image, already decoded. */
|
|
33
|
+
image: CanvasImageSource;
|
|
34
|
+
/** The image's natural size; shapes are in this space. */
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
shapes: MarkupShape[];
|
|
38
|
+
/** File name of the original; the result keeps its base name with the new extension. */
|
|
39
|
+
name: string;
|
|
40
|
+
maxBytes: number;
|
|
41
|
+
/** Test seam; defaults to a detached `<canvas>`. */
|
|
42
|
+
createCanvas?: (width: number, height: number) => FlattenCanvas;
|
|
43
|
+
};
|
|
44
|
+
/** Measures with `ctx.measureText`, or estimates 0.6 em per character when there is no canvas (jsdom). */
|
|
45
|
+
export declare function createMeasure(ctx: Pick<MarkupContext, "measureText" | "font"> | null): MeasureText;
|
|
46
|
+
/**
|
|
47
|
+
* Draws shapes in order on a context already scaled to image px. Pen strokes are smoothed through quadratic
|
|
48
|
+
* midpoints with round caps and joins; a one-point stroke draws a dot. Highlighter strokes draw the same way at
|
|
49
|
+
* `HIGHLIGHT_ALPHA` with normal ("source-over") compositing, so the overlay on screen matches the file. Arrows
|
|
50
|
+
* draw a shaft and a filled head sized from the stroke width (head length 4x width, min 10u), with the shaft
|
|
51
|
+
* stopping at the head's base.
|
|
52
|
+
* Rectangles have rounded corners (radius 1.5x width, clamped to half the short side). Text draws a filled
|
|
53
|
+
* rounded label (radius 0.3 em) in the shape colour with `contrastText` lines. Each shape restores the context.
|
|
54
|
+
*/
|
|
55
|
+
export declare function drawShapes(ctx: MarkupContext, shapes: MarkupShape[], metrics: MarkupMetrics): void;
|
|
56
|
+
/** A dashed outline around `box` (padded by 4 screen px / `scale`), for the selected shape on screen. */
|
|
57
|
+
export declare function drawSelection(ctx: MarkupContext, box: Box, scale: number): void;
|
|
58
|
+
/**
|
|
59
|
+
* Draws the image and its shapes into one file. Tries PNG at full size (scaled down to fit
|
|
60
|
+
* `MAX_CANVAS_PIXELS`); if that is over `maxBytes`, JPEG at quality 0.92, 0.85, 0.75 on a white background;
|
|
61
|
+
* then halves the size and tries again, up to three times. Rejects with a readable Error when the canvas has no
|
|
62
|
+
* 2D context, `toBlob` gives null, or nothing fits.
|
|
63
|
+
*/
|
|
64
|
+
export declare function flattenMarkup(input: FlattenInput): Promise<{
|
|
65
|
+
blob: Blob;
|
|
66
|
+
name: string;
|
|
67
|
+
}>;
|
|
68
|
+
/** Decodes an image URL. Rejects with a readable Error when it does not load. */
|
|
69
|
+
export declare function loadImage(url: string): Promise<HTMLImageElement>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Mention parsing for the composer and message text. DOM-free, so native clients share it. */
|
|
2
|
+
import type { Member, Mentioned } from "./api.js";
|
|
3
|
+
/** The `@query` being typed just before `caret`, and where its `@` is, or null. */
|
|
4
|
+
export declare function mentionQueryAt(text: string, caret: number): {
|
|
5
|
+
query: string;
|
|
6
|
+
start: number;
|
|
7
|
+
} | null;
|
|
8
|
+
/** Members whose name contains, or whose email starts with, `query` (any case); never the writer. */
|
|
9
|
+
export declare function mentionMatches(members: readonly Member[], query: string, selfEmail: string | null | undefined): Member[];
|
|
10
|
+
/** `text` with the `@query` at `start` (up to `caret`) replaced by `@Name `, and the caret after it. */
|
|
11
|
+
export declare function insertMention(text: string, start: number, caret: number, member: Member): {
|
|
12
|
+
text: string;
|
|
13
|
+
caret: number;
|
|
14
|
+
};
|
|
15
|
+
/** Members picked while writing whose `@Name` is still in the text. */
|
|
16
|
+
export declare function mentionsIn(text: string, picked: Map<string, Member>): Member[];
|
|
17
|
+
/** Splits a message body around its mentions: odd entries are the `@Name` parts. */
|
|
18
|
+
export declare function mentionParts(body: string, mentions: readonly Mentioned[] | undefined): string[];
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { type TextareaHTMLAttributes } from "react";
|
|
2
2
|
import type { Member, Mentioned } from "./client.js";
|
|
3
|
+
export { mentionParts, mentionQueryAt, mentionsIn } from "./mention-parse.js";
|
|
3
4
|
type Props = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "onChange"> & {
|
|
4
5
|
value: string;
|
|
5
6
|
onValueChange: (next: string) => void;
|
|
6
7
|
members: Member[];
|
|
7
8
|
selfEmail?: string;
|
|
8
9
|
onMention: (member: Member) => void;
|
|
10
|
+
/** Grow the field to its text. The composer uses this so a long draft stays visible. */
|
|
11
|
+
grow?: boolean;
|
|
9
12
|
};
|
|
10
|
-
/** Members picked while writing whose `@Name` is still in the text. */
|
|
11
|
-
export declare function mentionsIn(text: string, picked: Map<string, Member>): Member[];
|
|
12
13
|
export declare function MessageText({ body, mentions }: {
|
|
13
14
|
body: string;
|
|
14
15
|
mentions?: Mentioned[];
|
|
15
16
|
}): import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
export declare function MentionTextarea({ value, onValueChange, members, selfEmail, onMention, onKeyDown, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
|
|
17
|
-
export {};
|
|
17
|
+
export declare function MentionTextarea({ value, onValueChange, members, selfEmail, onMention, onKeyDown, grow, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The colour behind the page, as the browser paints it: the root element's background, else the body's (which
|
|
3
|
+
* the browser carries out to the whole window), else the window's own colour for the page's colour scheme
|
|
4
|
+
* (white, or dark under `color-scheme: dark`). A partly transparent one is laid over the window's colour.
|
|
5
|
+
*
|
|
6
|
+
* Never transparent. Snapshots and recording frames are JPEG and video, which have no transparency: a
|
|
7
|
+
* transparent background comes out black, with the page's black text on it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pageBackground(): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws the visible page onto a canvas without reading a computed style per element.
|
|
3
|
+
*
|
|
4
|
+
* The page is cloned natively (`cloneNode(true)`, a few milliseconds for thousands of nodes), the page's own
|
|
5
|
+
* stylesheets are inlined once (cached per page, with fonts and `url()` assets embedded as data URLs), and the
|
|
6
|
+
* result is rasterized through an SVG `<foreignObject>`. The previous renderer (modern-screenshot) copied the
|
|
7
|
+
* diff of ~400 computed properties for every element and its pseudo-elements, which cost about a second of
|
|
8
|
+
* main-thread time per capture on an ordinary page and grew with the page. This one spends the main thread on
|
|
9
|
+
* the clone walk and on the browser's own SVG layout, and nothing on the network once warmed.
|
|
10
|
+
*
|
|
11
|
+
* The copy is built to land every element exactly where the person sees it, whatever is scrolled:
|
|
12
|
+
*
|
|
13
|
+
* - A scrolled element's children are shifted by its scroll offset, the window's included, so the root is never
|
|
14
|
+
* transformed and never clips the part of the page that scrolled into view.
|
|
15
|
+
* - `position: fixed` and `position: sticky` elements are pinned at their on-screen box, since neither survives a
|
|
16
|
+
* scroll offset expressed as a translation (a transform re-anchors fixed descendants; a static image has nothing
|
|
17
|
+
* to stick to). Every box is measured while the page is copied, never when it is drawn: drawing waits for
|
|
18
|
+
* fonts and pictures, and a page that scrolled on in the meantime would put its header wherever it had got to.
|
|
19
|
+
* - What is in the browser's top layer (a modal dialog with its backdrop, an open popover) is drawn open, over
|
|
20
|
+
* everything else.
|
|
21
|
+
* - What is moving (a CSS animation or transition, a script animation, one driven by scrolling) is drawn at the
|
|
22
|
+
* frame it was on.
|
|
23
|
+
* - Replaced elements (images, video, canvas, iframes, plugins) keep their on-screen box and their own look
|
|
24
|
+
* (border, margin, fit) whatever happens to their pixels, so a slow or refused asset never reflows what
|
|
25
|
+
* follows it.
|
|
26
|
+
* - A classic (non-overlay) scrollbar's gutter is kept as padding, so content beside one is not laid out wider.
|
|
27
|
+
*
|
|
28
|
+
* What is not carried over: what the page itself may not read (a frame's or plugin's content, a closed shadow
|
|
29
|
+
* tree, a picture or video from another origin that sends no CORS header, a WebGL canvas that does not keep
|
|
30
|
+
* its drawing buffer): a blank box of the same size. And styles that depend on `:hover`/`:focus` (dropped,
|
|
31
|
+
* which is what a screenshot should show anyway).
|
|
32
|
+
*/
|
|
33
|
+
export type PageImageOptions = {
|
|
34
|
+
/** Device pixels per CSS pixel in the output. */
|
|
35
|
+
scale: number;
|
|
36
|
+
/** Upper bound on the whole capture. */
|
|
37
|
+
timeout?: number;
|
|
38
|
+
/** Subtrees to leave out (the toolbar). */
|
|
39
|
+
exclude?: (element: Element) => boolean;
|
|
40
|
+
/** Painted behind the page. */
|
|
41
|
+
backgroundColor?: string;
|
|
42
|
+
/** CSS width/height of the captured area; defaults to the viewport. */
|
|
43
|
+
width?: number;
|
|
44
|
+
height?: number;
|
|
45
|
+
/** Window scroll offsets the page is shifted by. */
|
|
46
|
+
scrollX?: number;
|
|
47
|
+
scrollY?: number;
|
|
48
|
+
};
|
|
49
|
+
type Viewport = {
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
};
|
|
53
|
+
type Scroll = {
|
|
54
|
+
left: number;
|
|
55
|
+
top: number;
|
|
56
|
+
};
|
|
57
|
+
/** A data URL for `url`, or null when it can't be fetched in time. Never rejects. */
|
|
58
|
+
export declare function assetDataUrl(url: string, timeout?: number): Promise<string | null>;
|
|
59
|
+
/** Replaces every `url()` in `cssText` with a data URL. Unfetchable ones are left as they are. */
|
|
60
|
+
export declare function inlineCssUrls(cssText: string, baseHref: string): Promise<string>;
|
|
61
|
+
type StyleSource = {
|
|
62
|
+
key: string;
|
|
63
|
+
ownHref: string;
|
|
64
|
+
text: string | null;
|
|
65
|
+
href: string | null;
|
|
66
|
+
};
|
|
67
|
+
/** The sheets that style the page, read in document order. */
|
|
68
|
+
export declare function collectStyleSources(doc: Document): StyleSource[];
|
|
69
|
+
/** Every page style as one block of CSS with assets embedded. Cached; refreshed when a sheet is added or grows. */
|
|
70
|
+
export declare function pageStyleText(doc?: Document): Promise<string>;
|
|
71
|
+
/** Fetches fonts and CSS assets now, so the first capture pays nothing on the network. */
|
|
72
|
+
export declare function prewarmPageImage(doc?: Document): void;
|
|
73
|
+
/**
|
|
74
|
+
* The elements that paint inside the viewport, plus every ancestor of one. Elements without a box
|
|
75
|
+
* (`display: none`, `<option>`, SVG `<defs>`, `display: contents` wrappers) are not listed.
|
|
76
|
+
*/
|
|
77
|
+
export declare function visibleSet(root: Element, viewport: Viewport, exclude?: (element: Element) => boolean): Set<Element>;
|
|
78
|
+
export type ClonedPage = {
|
|
79
|
+
root: Element;
|
|
80
|
+
waits: Promise<unknown>[];
|
|
81
|
+
elements: number;
|
|
82
|
+
/** Rules that belong after the page's own styles (see `renderSnapshot`). Empty for most pages. */
|
|
83
|
+
css: string;
|
|
84
|
+
/** Pins fixed and sticky elements where they are on screen. Call once `waits` have settled; runs once. */
|
|
85
|
+
finish: () => void;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* A copy of `root` ready to serialize: `display: none` subtrees removed, offscreen images held by same-size
|
|
89
|
+
* placeholders, live form values, scroll offsets, canvases and visible images carried over. `waits` resolve
|
|
90
|
+
* when every visible image has its pixels. `scroll` is the window's offset, applied to the root's children.
|
|
91
|
+
*
|
|
92
|
+
* Everything the copy needs from the live page is read here, in one go. `finish` only rearranges the copy.
|
|
93
|
+
*/
|
|
94
|
+
export declare function clonePage(root: Element, viewport: Viewport, exclude: (element: Element) => boolean, view?: Window, scroll?: Scroll): ClonedPage;
|
|
95
|
+
/**
|
|
96
|
+
* The SVG document that draws `clone` with `css` applied, `width`×`height` CSS pixels.
|
|
97
|
+
*
|
|
98
|
+
* Inside the image the `<svg>` is the root element, not the cloned `<html>`, so `rem` resolves against the
|
|
99
|
+
* svg's font size. `rootFontSize` (the live root's computed size) is set on it: a page that scales itself by
|
|
100
|
+
* a root font size set from an attribute or class on `<html>` would otherwise lay out at the browser default
|
|
101
|
+
* while fixed and sticky boxes stay pinned at their live, differently scaled coordinates.
|
|
102
|
+
*/
|
|
103
|
+
export declare function pageSvg(clone: Element, css: string, width: number, height: number, rootFontSize?: string): string;
|
|
104
|
+
/** The page copied at one instant, with everything a later render needs. */
|
|
105
|
+
export type PageSnapshot = {
|
|
106
|
+
page: ClonedPage;
|
|
107
|
+
css: Promise<string>;
|
|
108
|
+
width: number;
|
|
109
|
+
height: number;
|
|
110
|
+
/** The live root's computed font size, which `rem` resolves against inside the image too. */
|
|
111
|
+
rootFontSize: string;
|
|
112
|
+
backgroundColor?: string | undefined;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Copies the visible page now, synchronously, so the picture shows the page exactly as it was at this moment
|
|
116
|
+
* however long drawing it then takes. Rendering is `renderSnapshot`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function snapshotPage(options: Omit<PageImageOptions, "scale" | "timeout">): PageSnapshot;
|
|
119
|
+
/** Draws a snapshot onto a canvas at `scale` device pixels per CSS pixel. Throws when it can't be done in time. */
|
|
120
|
+
export declare function renderSnapshot(snapshot: PageSnapshot, scale: number, timeout?: number): Promise<HTMLCanvasElement>;
|
|
121
|
+
/** The visible page as a canvas. Throws when the page can't be drawn in time. */
|
|
122
|
+
export declare function drawPage(options: PageImageOptions): Promise<HTMLCanvasElement>;
|
|
123
|
+
export {};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { Thread } from "./client.js";
|
|
2
2
|
import type { PlacedStatus } from "./format.js";
|
|
3
|
+
import type { Here } from "./screen.js";
|
|
3
4
|
export type Box = {
|
|
4
5
|
top: number;
|
|
5
6
|
left: number;
|
|
6
7
|
width: number;
|
|
7
8
|
height: number;
|
|
8
9
|
};
|
|
10
|
+
/** `box` is the anchored element's box, set while the thread is placed. */
|
|
9
11
|
export type Placed = {
|
|
10
12
|
thread: Thread;
|
|
11
13
|
status: PlacedStatus;
|
|
12
14
|
top?: number;
|
|
13
15
|
left?: number;
|
|
16
|
+
box?: Box;
|
|
14
17
|
};
|
|
15
18
|
export declare function overlayBounds(host: HTMLElement): {
|
|
16
19
|
width: number;
|
|
@@ -26,13 +29,13 @@ export declare function placeAgainst(element: Element, host: HTMLElement): Box |
|
|
|
26
29
|
export declare function useOverlayHost(enabled: boolean): HTMLElement | null;
|
|
27
30
|
/**
|
|
28
31
|
* Where each thread's anchor is on this page. Anchors are resolved only while `active` (the panel is open or this
|
|
29
|
-
* page's pins are shown) and the tab is visible, only for open threads on this
|
|
32
|
+
* page's pins are shown) and the tab is visible, only for open threads on this screen, in idle-time slices. The page
|
|
30
33
|
* is watched with a MutationObserver only while the panel is open or the page is small; otherwise clicks, keys,
|
|
31
34
|
* focus and a slow re-check trigger re-resolves. Scrolling and resizing only re-measure elements already found.
|
|
32
35
|
*/
|
|
33
|
-
export declare function usePlacement({ threads,
|
|
36
|
+
export declare function usePlacement({ threads, here, host, active, panelOpen, visible, }: {
|
|
34
37
|
threads: Thread[];
|
|
35
|
-
|
|
38
|
+
here: Here;
|
|
36
39
|
host: HTMLElement;
|
|
37
40
|
active: boolean;
|
|
38
41
|
panelOpen: boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
/** A recording stops itself after this long. */
|
|
2
|
+
export declare const RECORD_MAX_MS = 75000;
|
|
1
3
|
export declare const FRAME_WIDTH = 1920;
|
|
2
4
|
export declare const FRAME_HEIGHT = 1080;
|
|
3
|
-
export declare function recordingMime(): string | null;
|
|
5
|
+
export declare function recordingMime(sound?: boolean): string | null;
|
|
4
6
|
export declare function formatClock(ms: number): string;
|
|
5
7
|
type FeaturePolicy = {
|
|
6
8
|
allowsFeature(feature: string): boolean;
|
|
@@ -24,10 +26,24 @@ export type TabTake = {
|
|
|
24
26
|
stop: () => void;
|
|
25
27
|
cancel: () => void;
|
|
26
28
|
finished: Promise<Blob | null>;
|
|
29
|
+
/** The mic in the take; mute it with `enabled`. Null when sound was off or the mic was refused. */
|
|
30
|
+
microphone: MediaStreamTrack | null;
|
|
31
|
+
};
|
|
32
|
+
/** Mute or unmute the mic in this take. Returns whether it is now on. */
|
|
33
|
+
export declare function toggleMicrophone(microphone: MediaStreamTrack): boolean;
|
|
34
|
+
export type RecordOptions = {
|
|
35
|
+
/** Record the mic too. */
|
|
36
|
+
sound: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Every second of the recording as it lands, in order; the first chunk carries the file's header, so the
|
|
39
|
+
* chunks so far always make a playable file. Lets a take be kept somewhere safe while it runs.
|
|
40
|
+
*/
|
|
41
|
+
onChunk?: (chunk: Blob, index: number, mime: string) => void;
|
|
27
42
|
};
|
|
28
43
|
/**
|
|
29
44
|
* Films this tab. When the page allows screen capture, the browser asks first.
|
|
30
45
|
* When the page forbids it, the visible page is filmed instead, so the record button still works.
|
|
46
|
+
* With `sound`, the mic is asked for after the screen (the screen prompt needs the click's activation).
|
|
31
47
|
*/
|
|
32
|
-
export declare function startTabRecording(onReady?: (take: TabTake) => void): Promise<TabTake>;
|
|
48
|
+
export declare function startTabRecording(options: RecordOptions, onReady?: (take: TabTake) => void): Promise<TabTake>;
|
|
33
49
|
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type ReplayDraft = {
|
|
2
|
+
type: "navigate";
|
|
3
|
+
path: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: "click";
|
|
6
|
+
label: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "input";
|
|
9
|
+
label: string;
|
|
10
|
+
redacted: boolean;
|
|
11
|
+
value?: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: "console";
|
|
14
|
+
level: "error" | "warn";
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
/** A request the page made that failed: `status` 0 means it never got an answer. */
|
|
18
|
+
| {
|
|
19
|
+
type: "network";
|
|
20
|
+
method: string;
|
|
21
|
+
path: string;
|
|
22
|
+
status: number;
|
|
23
|
+
}
|
|
24
|
+
/** Words the commenter said while a clip was recording, placed when that piece was spoken. */
|
|
25
|
+
| {
|
|
26
|
+
type: "say";
|
|
27
|
+
text: string;
|
|
28
|
+
};
|
|
29
|
+
export type ReplayEvent = ReplayDraft & {
|
|
30
|
+
t: number;
|
|
31
|
+
};
|
|
32
|
+
/** When the file goes with a recording: the clip's name and clock, so events sit on its timeline. */
|
|
33
|
+
export type ReplayClip = {
|
|
34
|
+
name: string;
|
|
35
|
+
startedAt: number;
|
|
36
|
+
durationMs: number;
|
|
37
|
+
};
|
|
38
|
+
export type ReplayFile = {
|
|
39
|
+
v: 1;
|
|
40
|
+
kind: "instant-replay";
|
|
41
|
+
url: string;
|
|
42
|
+
events: ReplayEvent[];
|
|
43
|
+
clip?: ReplayClip;
|
|
44
|
+
};
|
|
45
|
+
export type ReplayBufferOptions = {
|
|
46
|
+
now?: () => number;
|
|
47
|
+
/** How far back events are kept. */
|
|
48
|
+
windowMs?: number;
|
|
49
|
+
maxEvents?: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* What the commenter did, as a rolling window. The default minute backs the instant replay; a recording keeps a
|
|
53
|
+
* wider one for the clip's whole length. Stays in memory until it is attached.
|
|
54
|
+
*/
|
|
55
|
+
export declare class ReplayBuffer {
|
|
56
|
+
private items;
|
|
57
|
+
private readonly now;
|
|
58
|
+
private readonly windowMs;
|
|
59
|
+
private readonly maxEvents;
|
|
60
|
+
constructor(options?: ReplayBufferOptions | (() => number));
|
|
61
|
+
push(event: ReplayDraft): void;
|
|
62
|
+
/** An event that happened at `t`, not now: narration lands when it was spoken, not when it was written. */
|
|
63
|
+
pushAt(event: ReplayDraft, t: number): void;
|
|
64
|
+
/** `windowMs` keeps only the events of that last stretch. */
|
|
65
|
+
snapshot(url: string, clip?: ReplayClip, windowMs?: number): ReplayFile | null;
|
|
66
|
+
/** Every event still in the window, oldest first. */
|
|
67
|
+
events(): ReplayEvent[];
|
|
68
|
+
clear(): void;
|
|
69
|
+
get size(): number;
|
|
70
|
+
}
|
|
71
|
+
/** Accepts only the replay shape the toolbar writes. Anything else is dropped. */
|
|
72
|
+
export declare function parseReplay(value: unknown): ReplayFile | null;
|
|
73
|
+
/** The clip name a replay file goes with, by the stamp both carry: `recording-<stamp>.webm` ↔ `replay-<stamp>.json`. */
|
|
74
|
+
export declare function replayStamp(name: string): string | null;
|
|
75
|
+
/** One line of a replay event, as the viewers show it. */
|
|
76
|
+
export declare function replayLine(event: ReplayEvent): string;
|
|
77
|
+
/** Replaces secret query values in place. The page URL stored with a comment uses this too. */
|
|
78
|
+
export declare function redactSecretSearchParams(url: URL): void;
|
|
79
|
+
/** A console call's arguments as one redacted line. */
|
|
80
|
+
export declare function consoleText(args: unknown[]): string;
|
|
@@ -1,42 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
path: string;
|
|
4
|
-
} | {
|
|
5
|
-
type: "click";
|
|
6
|
-
label: string;
|
|
7
|
-
} | {
|
|
8
|
-
type: "input";
|
|
9
|
-
label: string;
|
|
10
|
-
redacted: boolean;
|
|
11
|
-
value?: string;
|
|
12
|
-
} | {
|
|
13
|
-
type: "console";
|
|
14
|
-
level: "error" | "warn";
|
|
15
|
-
message: string;
|
|
16
|
-
};
|
|
17
|
-
export type ReplayEvent = ReplayDraft & {
|
|
18
|
-
t: number;
|
|
19
|
-
};
|
|
20
|
-
export type ReplayFile = {
|
|
21
|
-
v: 1;
|
|
22
|
-
kind: "instant-replay";
|
|
23
|
-
url: string;
|
|
24
|
-
events: ReplayEvent[];
|
|
25
|
-
};
|
|
26
|
-
/** Rolling minute of what the commenter did. Stays in memory until they attach it. */
|
|
27
|
-
export declare class ReplayBuffer {
|
|
28
|
-
private now;
|
|
29
|
-
private events;
|
|
30
|
-
constructor(now?: () => number);
|
|
31
|
-
push(event: ReplayDraft): void;
|
|
32
|
-
snapshot(url: string): ReplayFile | null;
|
|
33
|
-
clear(): void;
|
|
34
|
-
get size(): number;
|
|
35
|
-
}
|
|
36
|
-
/** Accepts only the replay shape the toolbar writes. Anything else is dropped. */
|
|
37
|
-
export declare function parseReplay(value: unknown): ReplayFile | null;
|
|
1
|
+
import { type ReplayBuffer } from "./replay-model.js";
|
|
2
|
+
export * from "./replay-model.js";
|
|
38
3
|
/**
|
|
39
4
|
* Records clicks, field changes, navigations, and console errors on this page.
|
|
40
5
|
* Ignores the toolbar. Does not read network traffic. Call the return to stop and drop the buffer.
|
|
41
6
|
*/
|
|
42
|
-
export declare function watchActivity(buffer: ReplayBuffer
|
|
7
|
+
export declare function watchActivity(buffer: ReplayBuffer,
|
|
8
|
+
/** Requests that are the toolbar's own (the comment service): never part of what the person did. */
|
|
9
|
+
ignoreUrl?: (url: string) => boolean): () => void;
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { type RuntimeProps } from "./toolbar.js";
|
|
2
|
+
export { runtimeBuild } from "./build-id.js";
|
|
2
3
|
export { runtimeVersion } from "./runtime-version.js";
|
|
3
4
|
export type { RuntimeProps } from "./toolbar.js";
|
|
4
5
|
export type ToolbarHandle = {
|
|
5
6
|
update(props: RuntimeProps): void;
|
|
6
7
|
unmount(): void;
|
|
7
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Mounts the toolbar. The handle stays valid across a live update: when the worker starts serving a newer build,
|
|
11
|
+
* the toolbar loads it, unmounts this one and mounts the new one in the same container with the same props, and
|
|
12
|
+
* the handle the host holds forwards to it.
|
|
13
|
+
*/
|
|
8
14
|
export declare function mount(container: HTMLElement, props: RuntimeProps): ToolbarHandle;
|