@devaccel-labs/app-comments-toolbar 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -2
- package/dist/react.d.ts +1 -0
- package/dist/react.js +193 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +87 -0
- package/dist/types/compiler/annotate.d.ts +10 -0
- package/dist/types/compiler/enabled.d.ts +14 -0
- package/dist/types/compiler/index.d.ts +2 -0
- package/dist/types/core/index.d.ts +2 -0
- package/dist/types/core/resolve.d.ts +29 -0
- package/dist/types/core/types.d.ts +65 -0
- package/dist/types/react.d.ts +2 -0
- package/dist/types/runtime/attachments.d.ts +73 -0
- package/dist/types/runtime/build-id.d.ts +6 -0
- package/dist/types/runtime/capture-ui.d.ts +35 -0
- package/dist/types/runtime/capture.d.ts +30 -0
- package/dist/types/runtime/client.d.ts +220 -0
- package/dist/types/runtime/dashboard.d.ts +28 -0
- package/dist/types/runtime/details.d.ts +43 -0
- package/dist/types/runtime/dock.d.ts +38 -0
- package/dist/types/runtime/edits.d.ts +20 -0
- package/dist/types/runtime/format.d.ts +15 -0
- package/dist/types/runtime/freshness.d.ts +18 -0
- package/dist/types/runtime/handoff.d.ts +15 -0
- package/dist/types/runtime/icons.d.ts +40 -0
- package/dist/types/runtime/idle.d.ts +6 -0
- package/dist/types/runtime/index.d.ts +2 -0
- package/dist/types/runtime/loader.d.ts +6 -0
- package/dist/types/runtime/members.d.ts +11 -0
- package/dist/types/runtime/mentions.d.ts +17 -0
- package/dist/types/runtime/page-image.d.ts +70 -0
- package/dist/types/runtime/placement.d.ts +43 -0
- package/dist/types/runtime/record.d.ts +33 -0
- package/dist/types/runtime/redact.d.ts +10 -0
- package/dist/types/runtime/related.d.ts +11 -0
- package/dist/types/runtime/render-page.d.ts +1 -0
- package/dist/types/runtime/replay.d.ts +42 -0
- package/dist/types/runtime/runtime-version.d.ts +7 -0
- package/dist/types/runtime/runtime.d.ts +14 -0
- package/dist/types/runtime/screenshot.d.ts +14 -0
- package/dist/types/runtime/session.d.ts +25 -0
- package/dist/types/runtime/styles.d.ts +5 -0
- package/dist/types/runtime/sync.d.ts +25 -0
- package/dist/types/runtime/toolbar.d.ts +22 -0
- package/dist/types/runtime/update.d.ts +13 -0
- package/dist/types/server/index.d.ts +14 -0
- package/dist/types/server.d.ts +2 -0
- package/dist/types/vite/plugin.d.ts +20 -0
- package/dist/types/vite.d.ts +1 -0
- package/dist/vite.d.ts +1 -0
- package/dist/vite.js +173 -0
- package/package.json +52 -4
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { Anchor } from "../core/index.js";
|
|
2
|
+
import { type Session } from "./session.js";
|
|
3
|
+
export type Mentioned = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
/** Where a page screenshot was taken. */
|
|
8
|
+
export type Capture = {
|
|
9
|
+
url: string;
|
|
10
|
+
viewport?: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
browser?: string;
|
|
15
|
+
};
|
|
16
|
+
export type Attachment = {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
contentType: string;
|
|
20
|
+
size: number;
|
|
21
|
+
url: string;
|
|
22
|
+
capture?: Capture;
|
|
23
|
+
};
|
|
24
|
+
/** An earlier version of a message. `createdAt` is when that version was written. */
|
|
25
|
+
export type Revision = {
|
|
26
|
+
body: string;
|
|
27
|
+
mentions?: Mentioned[];
|
|
28
|
+
createdAt: string;
|
|
29
|
+
};
|
|
30
|
+
export type CommentMessage = {
|
|
31
|
+
id: string;
|
|
32
|
+
body: string;
|
|
33
|
+
author: string;
|
|
34
|
+
/** Missing on messages written before author ids were stored. */
|
|
35
|
+
authorId?: string | null;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
editedAt?: string | null;
|
|
38
|
+
mentions?: Mentioned[];
|
|
39
|
+
attachments?: Attachment[];
|
|
40
|
+
/** Earlier versions, oldest first. The current body is the version after the last one. */
|
|
41
|
+
revisions?: Revision[];
|
|
42
|
+
};
|
|
43
|
+
export type Member = {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
email: string;
|
|
47
|
+
};
|
|
48
|
+
export type Delivery = {
|
|
49
|
+
emailed: Mentioned[];
|
|
50
|
+
skipped: Array<Mentioned & {
|
|
51
|
+
reason: "rate_limited" | "muted" | "disabled" | string;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
export type Profile = {
|
|
55
|
+
name: string;
|
|
56
|
+
email: string | null;
|
|
57
|
+
mentionEmails: boolean;
|
|
58
|
+
};
|
|
59
|
+
export type PostExtras = {
|
|
60
|
+
mentions?: string[];
|
|
61
|
+
screenshot?: string | null;
|
|
62
|
+
attachments?: string[];
|
|
63
|
+
};
|
|
64
|
+
export type FieldDef = {
|
|
65
|
+
id: string;
|
|
66
|
+
label: string;
|
|
67
|
+
};
|
|
68
|
+
export type FieldValue = FieldDef & {
|
|
69
|
+
value: string;
|
|
70
|
+
};
|
|
71
|
+
export type Settings = {
|
|
72
|
+
roles: {
|
|
73
|
+
enabled: boolean;
|
|
74
|
+
options: string[];
|
|
75
|
+
};
|
|
76
|
+
fields: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
items: FieldDef[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/** Role and custom field values for a new or edited comment, keyed by field id. */
|
|
82
|
+
export type Details = {
|
|
83
|
+
role: string | null;
|
|
84
|
+
fields: Record<string, string>;
|
|
85
|
+
};
|
|
86
|
+
export type Thread = {
|
|
87
|
+
id: string;
|
|
88
|
+
/** The environment the comment was left in. Differs from the session's for comments from a linked environment. */
|
|
89
|
+
environment?: string;
|
|
90
|
+
route: string;
|
|
91
|
+
/** The page the comment was left on, including query and hash. */
|
|
92
|
+
url: string;
|
|
93
|
+
role: string | null;
|
|
94
|
+
fields: FieldValue[];
|
|
95
|
+
anchor: Anchor;
|
|
96
|
+
status: "open" | "resolved";
|
|
97
|
+
createdBy: string;
|
|
98
|
+
createdAt: string;
|
|
99
|
+
updatedAt: string;
|
|
100
|
+
messages: CommentMessage[];
|
|
101
|
+
/** Set when the team turned this comment into a support ticket in MyStatusFlow. */
|
|
102
|
+
ticket?: TicketBadge;
|
|
103
|
+
};
|
|
104
|
+
/** A comment's support ticket, as the commenter sees it: number and status only. */
|
|
105
|
+
export type TicketBadge = {
|
|
106
|
+
number: string;
|
|
107
|
+
status: string;
|
|
108
|
+
label: string;
|
|
109
|
+
};
|
|
110
|
+
export declare class ApiError extends Error {
|
|
111
|
+
readonly status: number;
|
|
112
|
+
readonly code: string;
|
|
113
|
+
constructor(status: number, code: string, message: string);
|
|
114
|
+
}
|
|
115
|
+
export declare function storageKey(session: Pick<Session, "apiUrl" | "project" | "environment">): string;
|
|
116
|
+
/** The team passphrase, kept on this device after unlocking with it so it can be shared from the dashboard. */
|
|
117
|
+
export declare function passphraseKey(session: Pick<Session, "apiUrl" | "project" | "environment">): string;
|
|
118
|
+
/**
|
|
119
|
+
* When the comment service moves to a new URL, the saved sign-in is under the old URL's key. Copy the newest current
|
|
120
|
+
* one for this project and environment to the new key, and leave the old key in place so going back also works.
|
|
121
|
+
*/
|
|
122
|
+
export declare function adoptStoredMember(session: Pick<Session, "apiUrl" | "project" | "environment">, storage?: Storage): void;
|
|
123
|
+
export declare function memberIsCurrent(token: string): boolean;
|
|
124
|
+
/** Sign in with the team passphrase, or with an invite link token instead. */
|
|
125
|
+
export declare function unlock(session: Session, displayName: string, email: string, secret: {
|
|
126
|
+
passphrase: string;
|
|
127
|
+
} | {
|
|
128
|
+
invite: string;
|
|
129
|
+
}): Promise<string>;
|
|
130
|
+
/** How people can sign in here. Services that predate MyStatusFlow sign-in answer 404: passphrase only. */
|
|
131
|
+
export type SignInOptions = {
|
|
132
|
+
passphrase: boolean;
|
|
133
|
+
msf: {
|
|
134
|
+
authorizeUrl: string;
|
|
135
|
+
label: string;
|
|
136
|
+
} | null;
|
|
137
|
+
};
|
|
138
|
+
export declare function signInOptions(session: Session): Promise<SignInOptions>;
|
|
139
|
+
/**
|
|
140
|
+
* An OAuth 2.0 authorization request with PKCE (S256). The verifier never leaves this page. The sign-in window
|
|
141
|
+
* comes back to `returnTo` (this page, on this origin) with the code, so a code only ever reaches the app it
|
|
142
|
+
* was issued for.
|
|
143
|
+
*/
|
|
144
|
+
export declare function msfAuthorization(authorizeUrl: string, returnTo: string): Promise<{
|
|
145
|
+
url: string;
|
|
146
|
+
state: string;
|
|
147
|
+
verifier: string;
|
|
148
|
+
}>;
|
|
149
|
+
/** Trades the authorization code for a member token. */
|
|
150
|
+
export declare function finishMsfSignIn(session: Session, input: {
|
|
151
|
+
state: string;
|
|
152
|
+
verifier: string;
|
|
153
|
+
code: string;
|
|
154
|
+
}): Promise<string>;
|
|
155
|
+
/** What the sign-in window hands back to the app (a code, or an error such as access_denied). */
|
|
156
|
+
export type MsfHandoff = {
|
|
157
|
+
state: string;
|
|
158
|
+
code?: string;
|
|
159
|
+
error?: string;
|
|
160
|
+
at: number;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* The sign-in window ends on this app's page with the result in the address hash (when it couldn't message the
|
|
164
|
+
* page that opened it). Pass it to that page through same-origin storage and a broadcast, and clean the address.
|
|
165
|
+
*/
|
|
166
|
+
export declare function relayMsfHandoff(): boolean;
|
|
167
|
+
/** Calls `onHandoff` when a sign-in window hands a result back, by message, broadcast or storage. */
|
|
168
|
+
export declare function listenForMsfHandoff(apiOrigin: string, onHandoff: (handoff: MsfHandoff) => void): () => void;
|
|
169
|
+
export declare function createInvite(session: Session, memberToken: string): Promise<{
|
|
170
|
+
invite: string;
|
|
171
|
+
invitedBy: string;
|
|
172
|
+
expiresAt: string;
|
|
173
|
+
}>;
|
|
174
|
+
export declare function getProfile(session: Session, memberToken: string): Promise<Profile>;
|
|
175
|
+
export declare function updateProfile(session: Session, memberToken: string, patch: {
|
|
176
|
+
displayName?: string;
|
|
177
|
+
email?: string;
|
|
178
|
+
mentionEmails?: boolean;
|
|
179
|
+
}): Promise<Profile & {
|
|
180
|
+
token: string;
|
|
181
|
+
}>;
|
|
182
|
+
export declare function uploadAttachment(session: Session, memberToken: string, file: Blob, name: string, environment?: string, capture?: Capture): Promise<Attachment>;
|
|
183
|
+
export declare function listMembers(session: Session, memberToken: string, environment?: string): Promise<Member[]>;
|
|
184
|
+
/** "view": this person may read but not comment. Older services don't send it, which means "edit". */
|
|
185
|
+
export type Access = "edit" | "view";
|
|
186
|
+
export declare function listThreads(session: Session, memberToken: string): Promise<{
|
|
187
|
+
threads: Thread[];
|
|
188
|
+
settings: Settings;
|
|
189
|
+
environment?: string | null;
|
|
190
|
+
linked?: string[];
|
|
191
|
+
access?: Access;
|
|
192
|
+
}>;
|
|
193
|
+
/** A socket that says "changed" whenever comments change in this environment or one it links to. */
|
|
194
|
+
export declare function openLive(session: Session, memberToken: string): WebSocket;
|
|
195
|
+
export declare function saveSettings(session: Session, memberToken: string, settings: Settings): Promise<Settings>;
|
|
196
|
+
export declare function createThread(session: Session, memberToken: string, anchor: Anchor, body: string, extras?: PostExtras & Partial<Details> & {
|
|
197
|
+
url?: string;
|
|
198
|
+
}): Promise<{
|
|
199
|
+
thread: Thread;
|
|
200
|
+
delivery?: Delivery;
|
|
201
|
+
}>;
|
|
202
|
+
export declare function replyToThread(session: Session, memberToken: string, threadId: string, body: string, extras?: PostExtras): Promise<{
|
|
203
|
+
message: CommentMessage;
|
|
204
|
+
delivery?: Delivery;
|
|
205
|
+
}>;
|
|
206
|
+
export declare function updateThread(session: Session, memberToken: string, threadId: string, patch: {
|
|
207
|
+
status?: "open" | "resolved";
|
|
208
|
+
anchor?: Anchor;
|
|
209
|
+
url?: string;
|
|
210
|
+
} & Partial<Details>): Promise<{
|
|
211
|
+
id: string;
|
|
212
|
+
}>;
|
|
213
|
+
export declare function editMessage(session: Session, memberToken: string, threadId: string, messageId: string, body: string, mentions: string[]): Promise<{
|
|
214
|
+
message: {
|
|
215
|
+
id: string;
|
|
216
|
+
body: string;
|
|
217
|
+
editedAt: string | null;
|
|
218
|
+
};
|
|
219
|
+
delivery: Delivery | null;
|
|
220
|
+
}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { Member, Thread } from "./client.js";
|
|
3
|
+
import { type PlacedStatus } from "./format.js";
|
|
4
|
+
import type { Session } from "./session.js";
|
|
5
|
+
type TableRow = {
|
|
6
|
+
thread: Thread;
|
|
7
|
+
status: PlacedStatus;
|
|
8
|
+
};
|
|
9
|
+
export declare function Dashboard({ rows, route, environment, memberName, memberId, roles, members, session, memberToken, envFilter, profile, settingsPane, onClose, onOpen, onRefresh, readOnly, }: {
|
|
10
|
+
rows: TableRow[];
|
|
11
|
+
route: string;
|
|
12
|
+
environment: string;
|
|
13
|
+
memberName: string;
|
|
14
|
+
memberId?: string;
|
|
15
|
+
roles: string[];
|
|
16
|
+
members: Member[];
|
|
17
|
+
session: Session;
|
|
18
|
+
memberToken: string;
|
|
19
|
+
envFilter: ReactNode;
|
|
20
|
+
profile: ReactNode;
|
|
21
|
+
settingsPane: ReactNode;
|
|
22
|
+
onClose: () => void;
|
|
23
|
+
onOpen: (row: TableRow) => void;
|
|
24
|
+
onRefresh: () => Promise<unknown> | unknown;
|
|
25
|
+
/** View-only access: no replies and no status changes. */
|
|
26
|
+
readOnly?: boolean;
|
|
27
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
2
|
+
import type { Details, FieldValue, Settings, Thread } from "./client.js";
|
|
3
|
+
export declare const noDetails: Details;
|
|
4
|
+
export declare function detailsEnabled(settings: Settings | null): boolean;
|
|
5
|
+
export declare function hasFieldValues(details: Details): boolean;
|
|
6
|
+
/** Only sends what is turned on, so a disabled feature never rejects a post. */
|
|
7
|
+
export declare function detailsPayload(settings: Settings | null, details: Details): Partial<Details>;
|
|
8
|
+
export declare function DetailsInput({ settings, value, onChange, onKeyDown, }: {
|
|
9
|
+
settings: Settings | null;
|
|
10
|
+
value: Details;
|
|
11
|
+
onChange: (next: Details) => void;
|
|
12
|
+
onKeyDown?: (event: ReactKeyboardEvent<HTMLTextAreaElement>) => void;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
14
|
+
export declare function RoleBadge({ role }: {
|
|
15
|
+
role: string | null;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
17
|
+
/** "Ticket I00012 · In progress": the team turned this comment into a support ticket. */
|
|
18
|
+
export declare function TicketBadge({ ticket, }: {
|
|
19
|
+
ticket?: {
|
|
20
|
+
number: string;
|
|
21
|
+
status: string;
|
|
22
|
+
label: string;
|
|
23
|
+
};
|
|
24
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
25
|
+
export declare function FieldValues({ fields, clamp }: {
|
|
26
|
+
fields: FieldValue[];
|
|
27
|
+
clamp?: boolean;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
29
|
+
/** Inline editor for a posted comment's role and fields. Values for fields that were since removed are kept by the server. */
|
|
30
|
+
export declare function DetailsEditor({ settings, thread, onSave, onClose, }: {
|
|
31
|
+
settings: Settings;
|
|
32
|
+
thread: Thread;
|
|
33
|
+
onSave: (patch: Partial<Details>) => Promise<void>;
|
|
34
|
+
onClose: () => void;
|
|
35
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
/** Shared per environment: anyone signed in can change it, and existing comments keep their values. */
|
|
37
|
+
export declare function SettingsPanel({ settings, onChange, onBack, error, }: {
|
|
38
|
+
settings: Settings | null;
|
|
39
|
+
onChange: (next: Settings) => void;
|
|
40
|
+
/** Without a back button the panel renders inline, e.g. inside the dashboard. */
|
|
41
|
+
onBack?: () => void;
|
|
42
|
+
error: string;
|
|
43
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the comment toolbar sits after someone drags it.
|
|
3
|
+
*
|
|
4
|
+
* Same gesture as React Grab's toolbar: a press that moves less than `dockSlop` is still a click,
|
|
5
|
+
* a longer press follows the pointer, and release snaps to the nearest screen edge. A flick is
|
|
6
|
+
* projected `dockFlickMs` ahead so the bar lands on the edge it was heading toward. An equal
|
|
7
|
+
* distance prefers top, then left, then right, then bottom.
|
|
8
|
+
*/
|
|
9
|
+
export type DockEdge = "top" | "right" | "bottom" | "left";
|
|
10
|
+
export type Dock = {
|
|
11
|
+
edge: DockEdge;
|
|
12
|
+
ratio: number;
|
|
13
|
+
};
|
|
14
|
+
export type DockPoint = {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
export type DockSize = {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
export type DockBounds = {
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
};
|
|
26
|
+
/** Inset from the screen edge once the bar has snapped, in px. */
|
|
27
|
+
export declare const dockMargin = 12;
|
|
28
|
+
/** Movement below this, in px, stays a click on a tool. */
|
|
29
|
+
export declare const dockSlop = 4;
|
|
30
|
+
/** How far ahead a flick is projected, in ms. */
|
|
31
|
+
export declare const dockFlickMs = 150;
|
|
32
|
+
export declare function readDock(value: unknown): Dock | undefined;
|
|
33
|
+
/** `ratio` is 0 at the start of the edge and 1 at the end. */
|
|
34
|
+
export declare function placeDock(dock: Dock, bounds: DockBounds, size: DockSize, margin?: number): DockPoint;
|
|
35
|
+
/** Keep the whole bar on screen while it follows the pointer. */
|
|
36
|
+
export declare function clampBar(point: DockPoint, bounds: DockBounds, size: DockSize): DockPoint;
|
|
37
|
+
/** `velocity` is px/ms. The returned point is the snapped top-left. */
|
|
38
|
+
export declare function snapDock(origin: DockPoint, velocity: DockPoint, bounds: DockBounds, size: DockSize, margin?: number): Dock & DockPoint;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CommentMessage, Member } from "./client.js";
|
|
2
|
+
export declare function isMine(message: CommentMessage, memberId: string | undefined, memberName: string): boolean;
|
|
3
|
+
export declare function MessageTools({ message, mine, historyOpen, onHistory, onEdit, }: {
|
|
4
|
+
message: CommentMessage;
|
|
5
|
+
mine: boolean;
|
|
6
|
+
historyOpen: boolean;
|
|
7
|
+
onHistory: () => void;
|
|
8
|
+
onEdit: () => void;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
export declare function MessageHistory({ message }: {
|
|
11
|
+
message: CommentMessage;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function MessageEditor({ message, members, selfEmail, allowEmpty, onSave, onCancel, }: {
|
|
14
|
+
message: CommentMessage;
|
|
15
|
+
members: Member[];
|
|
16
|
+
selfEmail?: string;
|
|
17
|
+
allowEmpty: boolean;
|
|
18
|
+
onSave: (body: string, mentions: string[]) => Promise<unknown>;
|
|
19
|
+
onCancel: () => void;
|
|
20
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Anchor } from "../core/index.js";
|
|
2
|
+
import type { Delivery } from "./client.js";
|
|
3
|
+
export declare function initials(name: string): string;
|
|
4
|
+
export declare function colorFor(name: string): string;
|
|
5
|
+
export declare function ago(iso: string): string;
|
|
6
|
+
export declare function targetName(anchor: Anchor): string;
|
|
7
|
+
/** "pending": on this page, but not looked up yet. */
|
|
8
|
+
export type PlacedStatus = "pinned" | "drifted" | "hidden" | "ambiguous" | "unavailable" | "resolved" | "pending";
|
|
9
|
+
/** Plain-language reason a thread isn't pinned, or null when there's nothing to explain. */
|
|
10
|
+
export declare function statusNote(status: PlacedStatus, anchor: Anchor): {
|
|
11
|
+
text: string;
|
|
12
|
+
tone: string;
|
|
13
|
+
} | null;
|
|
14
|
+
/** One quiet line about who was emailed for a message, or null when nobody was mentioned. */
|
|
15
|
+
export declare function deliveryText(delivery: Delivery | undefined): string | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Longest wait between threads refetches while no live socket is open and something on screen needs them. */
|
|
2
|
+
export declare const MAX_POLL_MS = 60000;
|
|
3
|
+
export type PollState = {
|
|
4
|
+
/** The live socket is open, so changes are pushed. */
|
|
5
|
+
live: boolean;
|
|
6
|
+
visible: boolean;
|
|
7
|
+
/** Something on screen shows threads: the comments panel, a draft, or this page's pins and count. */
|
|
8
|
+
wanted: boolean;
|
|
9
|
+
/** Refetches in a row that changed nothing. */
|
|
10
|
+
quiet: number;
|
|
11
|
+
pollMs: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Milliseconds until the next threads refetch, or null for none: the live socket pushes changes, the tab is hidden,
|
|
15
|
+
* or the toolbar is collapsed with nothing on this page to keep current. Each refetch that changed nothing doubles
|
|
16
|
+
* the wait, up to MAX_POLL_MS (or `pollMs`, if that is longer).
|
|
17
|
+
*/
|
|
18
|
+
export declare function nextPoll({ live, visible, wanted, quiet, pollMs }: PollState): number | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Handoff = {
|
|
2
|
+
invite?: string;
|
|
3
|
+
thread?: string;
|
|
4
|
+
at: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function readHandoff(): Handoff | null;
|
|
7
|
+
/**
|
|
8
|
+
* Saves invite and thread links for this tab before the app's router or sign-in flow rewrites the URL.
|
|
9
|
+
* Runs when the module loads, which is before hydration, and again when the toolbar mounts.
|
|
10
|
+
*/
|
|
11
|
+
export declare function captureHandoff(): void;
|
|
12
|
+
export declare function dropHandoff(part: "invite" | "thread"): void;
|
|
13
|
+
/** Removes the link params from the address bar once they're saved. */
|
|
14
|
+
export declare function stripHandoffFromUrl(): void;
|
|
15
|
+
export declare function linkWith(href: string, name: "cs-invite" | "cs-thread", value: string): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const AddComment: () => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const Inbox: () => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare const Eye: () => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare const EyeOff: () => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare const Lock: ({ size }: {
|
|
6
|
+
size?: number;
|
|
7
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const Close: () => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const Send: () => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const Check: () => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const Target: () => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare const Relink: () => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const Undo: () => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const Pencil: () => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const Cursor: () => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare const Paperclip: () => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const Record: () => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare const Camera: () => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const FileIcon: () => import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare const Back: () => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare const Ellipsis: () => import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare const ChevronLeft: () => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare const ChevronRight: () => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export declare const Play: () => import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare const Plane: () => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare const Bubble: () => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
/** A pin that a slash draws through when `hidden`; the slash also cuts a gap in the pin behind it. */
|
|
28
|
+
export declare function Pins({ hidden }: {
|
|
29
|
+
hidden: boolean;
|
|
30
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export declare const User: () => import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export declare const Gear: () => import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare const Copy: () => import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
export declare const Clock: () => import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare const Board: () => import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export declare const ChevronDown: () => import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export declare const Search: () => import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
export declare const ZoomOut: () => import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare const ZoomIn: () => import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export declare const Reply: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type IdleDeadline = {
|
|
2
|
+
didTimeout: boolean;
|
|
3
|
+
timeRemaining(): number;
|
|
4
|
+
};
|
|
5
|
+
/** Runs `task` when the browser is next idle, or after `timeout` ms at the latest. Returns a cancel function. */
|
|
6
|
+
export declare function whenIdle(task: (deadline: IdleDeadline) => void, timeout: number): () => void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolbarProps } from "./toolbar.js";
|
|
2
|
+
/**
|
|
3
|
+
* Renders nothing and does nothing during render. Once the page is idle it asks the session route, once per page
|
|
4
|
+
* load, whether comments are on, and only then loads and mounts the toolbar.
|
|
5
|
+
*/
|
|
6
|
+
export declare function CommentToolbar({ sessionPath, pollMs, position, theme, navigate, }: ToolbarProps): null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Member } from "./client.js";
|
|
2
|
+
import type { Session } from "./session.js";
|
|
3
|
+
type LoadMembers = (session: Session, memberToken: string, environment?: string) => Promise<Member[]>;
|
|
4
|
+
/** One request per environment, shared by every caller and kept for MEMBERS_TTL_MS. A failed request isn't kept. */
|
|
5
|
+
export declare function membersCache(load: LoadMembers, now?: () => number): LoadMembers;
|
|
6
|
+
/**
|
|
7
|
+
* People who can be @mentioned in `environment` (this one when omitted), fetched only while `wanted`, which the
|
|
8
|
+
* toolbar sets when a composer or reply box can be typed in.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useMembers(session: Session | null, memberToken: string | null, environment: string | undefined, wanted: boolean): Member[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type TextareaHTMLAttributes } from "react";
|
|
2
|
+
import type { Member, Mentioned } from "./client.js";
|
|
3
|
+
type Props = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "onChange"> & {
|
|
4
|
+
value: string;
|
|
5
|
+
onValueChange: (next: string) => void;
|
|
6
|
+
members: Member[];
|
|
7
|
+
selfEmail?: string;
|
|
8
|
+
onMention: (member: Member) => void;
|
|
9
|
+
};
|
|
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
|
+
export declare function MessageText({ body, mentions }: {
|
|
13
|
+
body: string;
|
|
14
|
+
mentions?: Mentioned[];
|
|
15
|
+
}): 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 {};
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
* What is not carried over: cross-origin iframes (drawn blank), `<video>` frames (blank), styles that depend on
|
|
12
|
+
* `:hover`/`:focus` (dropped, which is what a screenshot should show anyway).
|
|
13
|
+
*/
|
|
14
|
+
export type PageImageOptions = {
|
|
15
|
+
/** Device pixels per CSS pixel in the output. */
|
|
16
|
+
scale: number;
|
|
17
|
+
/** Upper bound on the whole capture. */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
/** Subtrees to leave out (the toolbar). */
|
|
20
|
+
exclude?: (element: Element) => boolean;
|
|
21
|
+
/** Painted behind the page. */
|
|
22
|
+
backgroundColor?: string;
|
|
23
|
+
/** CSS width/height of the captured area; defaults to the viewport. */
|
|
24
|
+
width?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
/** Window scroll offsets the page is shifted by. */
|
|
27
|
+
scrollX?: number;
|
|
28
|
+
scrollY?: number;
|
|
29
|
+
};
|
|
30
|
+
type Viewport = {
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
};
|
|
34
|
+
/** A data URL for `url`, or null when it can't be fetched in time. Never rejects. */
|
|
35
|
+
export declare function assetDataUrl(url: string, timeout?: number): Promise<string | null>;
|
|
36
|
+
/** Replaces every `url()` in `cssText` with a data URL. Unfetchable ones are left as they are. */
|
|
37
|
+
export declare function inlineCssUrls(cssText: string, baseHref: string): Promise<string>;
|
|
38
|
+
type StyleSource = {
|
|
39
|
+
key: string;
|
|
40
|
+
ownHref: string;
|
|
41
|
+
text: string | null;
|
|
42
|
+
href: string | null;
|
|
43
|
+
};
|
|
44
|
+
/** The sheets that style the page, read in document order. */
|
|
45
|
+
export declare function collectStyleSources(doc: Document): StyleSource[];
|
|
46
|
+
/** Every page style as one block of CSS with assets embedded. Cached; refreshed when a sheet is added or grows. */
|
|
47
|
+
export declare function pageStyleText(doc?: Document): Promise<string>;
|
|
48
|
+
/** Fetches fonts and CSS assets now, so the first capture pays nothing on the network. */
|
|
49
|
+
export declare function prewarmPageImage(doc?: Document): void;
|
|
50
|
+
/**
|
|
51
|
+
* The elements that paint inside the viewport, plus every ancestor of one (so the tree above them survives).
|
|
52
|
+
* Elements without a box (`display: none`, `<option>`, SVG `<defs>`, `display: contents` wrappers) are not
|
|
53
|
+
* listed: they neither show nor hide anything by themselves, so the clone keeps them under a kept parent.
|
|
54
|
+
*/
|
|
55
|
+
export declare function visibleSet(root: Element, viewport: Viewport): Set<Element>;
|
|
56
|
+
export type ClonedPage = {
|
|
57
|
+
root: Element;
|
|
58
|
+
waits: Promise<unknown>[];
|
|
59
|
+
elements: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* A copy of `root` ready to serialize: offscreen and non-painting subtrees removed, live form values, scroll
|
|
63
|
+
* offsets, canvases and images carried over. `waits` resolve when every image has its pixels.
|
|
64
|
+
*/
|
|
65
|
+
export declare function clonePage(root: Element, viewport: Viewport, exclude: (element: Element) => boolean, view?: Window): ClonedPage;
|
|
66
|
+
/** The SVG document that draws `clone` with `css` applied, `width`×`height` CSS pixels. */
|
|
67
|
+
export declare function pageSvg(clone: Element, css: string, width: number, height: number): string;
|
|
68
|
+
/** The visible page as a canvas. Throws when the page can't be drawn in time. */
|
|
69
|
+
export declare function drawPage(options: PageImageOptions): Promise<HTMLCanvasElement>;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Thread } from "./client.js";
|
|
2
|
+
import type { PlacedStatus } from "./format.js";
|
|
3
|
+
export type Box = {
|
|
4
|
+
top: number;
|
|
5
|
+
left: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
export type Placed = {
|
|
10
|
+
thread: Thread;
|
|
11
|
+
status: PlacedStatus;
|
|
12
|
+
top?: number;
|
|
13
|
+
left?: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function overlayBounds(host: HTMLElement): {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function placeAgainst(element: Element, host: HTMLElement): Box | null;
|
|
20
|
+
/**
|
|
21
|
+
* The open <dialog> the toolbar renders inside, since a modal's top layer covers everything else, or <body>.
|
|
22
|
+
* Instead of a MutationObserver on the whole page: showModal() moves focus into the dialog synchronously, so
|
|
23
|
+
* focusin moves the toolbar before the click that opened it returns; toggle/close cover the rest, and clicks and
|
|
24
|
+
* keys re-check once their handlers have run.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useOverlayHost(enabled: boolean): HTMLElement | null;
|
|
27
|
+
/**
|
|
28
|
+
* 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 route, in idle-time slices. The page
|
|
30
|
+
* is watched with a MutationObserver only while the panel is open or the page is small; otherwise clicks, keys,
|
|
31
|
+
* focus and a slow re-check trigger re-resolves. Scrolling and resizing only re-measure elements already found.
|
|
32
|
+
*/
|
|
33
|
+
export declare function usePlacement({ threads, route, host, active, panelOpen, visible, }: {
|
|
34
|
+
threads: Thread[];
|
|
35
|
+
route: string;
|
|
36
|
+
host: HTMLElement;
|
|
37
|
+
active: boolean;
|
|
38
|
+
panelOpen: boolean;
|
|
39
|
+
visible: boolean;
|
|
40
|
+
}): {
|
|
41
|
+
placed: Placed[];
|
|
42
|
+
elementFor: (threadId: string) => Element | null;
|
|
43
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const FRAME_WIDTH = 1920;
|
|
2
|
+
export declare const FRAME_HEIGHT = 1080;
|
|
3
|
+
export declare function recordingMime(): string | null;
|
|
4
|
+
export declare function formatClock(ms: number): string;
|
|
5
|
+
type FeaturePolicy = {
|
|
6
|
+
allowsFeature(feature: string): boolean;
|
|
7
|
+
};
|
|
8
|
+
/** The document's capture policy. Chrome still exposes this as `featurePolicy`. */
|
|
9
|
+
export declare function pageCapturePolicy(doc: {
|
|
10
|
+
permissionsPolicy?: FeaturePolicy | null;
|
|
11
|
+
featurePolicy?: FeaturePolicy | null;
|
|
12
|
+
} | null | undefined): FeaturePolicy | null;
|
|
13
|
+
/** True when this document may call getDisplayMedia. A missing policy counts as allowed. */
|
|
14
|
+
export declare function displayCaptureAllowed(policy: FeaturePolicy | null | undefined): boolean;
|
|
15
|
+
/** Fit a source rectangle inside a 1920×1080 frame, scaling by at most 2. */
|
|
16
|
+
export declare function fitFrame(sourceWidth: number, sourceHeight: number): {
|
|
17
|
+
scale: number;
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
export type TabTake = {
|
|
24
|
+
stop: () => void;
|
|
25
|
+
cancel: () => void;
|
|
26
|
+
finished: Promise<Blob | null>;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Films this tab. When the page allows screen capture, the browser asks first.
|
|
30
|
+
* When the page forbids it, the visible page is filmed instead, so the record button still works.
|
|
31
|
+
*/
|
|
32
|
+
export declare function startTabRecording(onReady?: (take: TabTake) => void): Promise<TabTake>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Strips tokens and control characters from text that may be stored with a comment. */
|
|
2
|
+
export declare function redactText(value: string): string;
|
|
3
|
+
/** True for password fields and controls whose name, id, label, or autocomplete looks like a credential. */
|
|
4
|
+
export declare function fieldIsSensitive(field: {
|
|
5
|
+
type?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
autocomplete?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
}): boolean;
|