g-comments 0.0.0-stage → 0.2.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/CHANGELOG.md +48 -0
- package/LICENSE +21 -0
- package/README.md +389 -2
- package/dist/chunk-IBNNENCZ.cjs +3089 -0
- package/dist/chunk-IBNNENCZ.cjs.map +1 -0
- package/dist/chunk-WACVXJHD.js +3068 -0
- package/dist/chunk-WACVXJHD.js.map +1 -0
- package/dist/element.d.ts +823 -0
- package/dist/element.js +1230 -0
- package/dist/element.js.map +1 -0
- package/dist/index.cjs +68 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/messages-C9bMljl3.d.cts +699 -0
- package/dist/messages-C9bMljl3.d.ts +699 -0
- package/dist/react.cjs +2754 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +335 -0
- package/dist/react.d.ts +335 -0
- package/dist/react.js +2745 -0
- package/dist/react.js.map +1 -0
- package/package.json +90 -4
- package/styles/styles.css +1145 -0
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
/** Public sort names (`DiscourseComments::Sort`). */
|
|
2
|
+
type Sort = "best" | "hot" | "newest" | "oldest";
|
|
3
|
+
/** Report reasons accepted by `POST …/comments/:id/flag` (`Comment::Flag::FLAG_TYPES`). */
|
|
4
|
+
type FlagType = "off_topic" | "inappropriate" | "spam" | "illegal" | "notify_moderators";
|
|
5
|
+
/** `discourse_comments_thread_creation` setting values. */
|
|
6
|
+
type ThreadCreation = "first_view" | "first_comment";
|
|
7
|
+
/** `CommenterSerializer`: the signed-in reader, mention results, exchange/me responses. */
|
|
8
|
+
interface Commenter {
|
|
9
|
+
id: number;
|
|
10
|
+
username: string;
|
|
11
|
+
name: string | null;
|
|
12
|
+
avatar_url: string;
|
|
13
|
+
title: string | null;
|
|
14
|
+
staff: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** `ClientConfig#auth_providers` entry. */
|
|
17
|
+
interface AuthProvider {
|
|
18
|
+
name: string;
|
|
19
|
+
title: string;
|
|
20
|
+
}
|
|
21
|
+
interface ForumInfo {
|
|
22
|
+
name: string;
|
|
23
|
+
base_url: string;
|
|
24
|
+
locale: string;
|
|
25
|
+
}
|
|
26
|
+
interface Features {
|
|
27
|
+
nested: boolean;
|
|
28
|
+
hot_sort: boolean;
|
|
29
|
+
mentions: boolean;
|
|
30
|
+
uploads: boolean;
|
|
31
|
+
/** `null` when the reactions plugin is not installed. */
|
|
32
|
+
reactions: boolean | null;
|
|
33
|
+
}
|
|
34
|
+
interface Limits {
|
|
35
|
+
min_post_length: number;
|
|
36
|
+
max_post_length: number;
|
|
37
|
+
roots_per_page: number;
|
|
38
|
+
children_per_page: number;
|
|
39
|
+
max_image_size_kb: number;
|
|
40
|
+
/** Pipe-separated list, as stored in the `authorized_extensions` site setting. */
|
|
41
|
+
authorized_extensions: string;
|
|
42
|
+
}
|
|
43
|
+
/** `ConfigSerializer` (`GET /comments/config`). */
|
|
44
|
+
interface Config {
|
|
45
|
+
forum: ForumInfo;
|
|
46
|
+
features: Features;
|
|
47
|
+
limits: Limits;
|
|
48
|
+
auth_providers: AuthProvider[];
|
|
49
|
+
sorts: Sort[];
|
|
50
|
+
default_sort: Sort;
|
|
51
|
+
thread_creation: ThreadCreation;
|
|
52
|
+
guidelines_url: string | null;
|
|
53
|
+
scope_version: number;
|
|
54
|
+
/** Present only when the request carried a valid user API key. */
|
|
55
|
+
current_user?: Commenter;
|
|
56
|
+
/** Set when the forum served the request anonymously under load; the key is still valid. */
|
|
57
|
+
degraded?: true;
|
|
58
|
+
}
|
|
59
|
+
/** `ThreadSerializer`: a page's thread once its topic exists. */
|
|
60
|
+
interface Thread {
|
|
61
|
+
/** Opaque handle; the topic id as a string. */
|
|
62
|
+
id: string;
|
|
63
|
+
topic_id: number;
|
|
64
|
+
key: string;
|
|
65
|
+
url: string;
|
|
66
|
+
title: string | null;
|
|
67
|
+
count: number;
|
|
68
|
+
closed: boolean;
|
|
69
|
+
listed: boolean;
|
|
70
|
+
can_comment: boolean;
|
|
71
|
+
nested: boolean;
|
|
72
|
+
sorts: Sort[];
|
|
73
|
+
sort: Sort;
|
|
74
|
+
pinned_ids: number[];
|
|
75
|
+
realtime: "live" | "poll";
|
|
76
|
+
message_bus_last_id: number;
|
|
77
|
+
forum_url: string;
|
|
78
|
+
}
|
|
79
|
+
/** `VirtualThreadSerializer`: a page whose topic does not exist yet. */
|
|
80
|
+
interface VirtualThread {
|
|
81
|
+
id: `v:${string}`;
|
|
82
|
+
topic_id: null;
|
|
83
|
+
key: string;
|
|
84
|
+
url: string;
|
|
85
|
+
title: string;
|
|
86
|
+
count: 0;
|
|
87
|
+
closed: false;
|
|
88
|
+
listed: false;
|
|
89
|
+
can_comment: boolean;
|
|
90
|
+
nested: boolean;
|
|
91
|
+
sorts: Sort[];
|
|
92
|
+
sort: Sort;
|
|
93
|
+
pinned_ids: [];
|
|
94
|
+
realtime: "pending";
|
|
95
|
+
/** MessageBus channel where the forum announces `{ topic_id }` on creation. */
|
|
96
|
+
pending_channel: string;
|
|
97
|
+
message_bus_last_id: null;
|
|
98
|
+
forum_url: null;
|
|
99
|
+
}
|
|
100
|
+
type AnyThread = Thread | VirtualThread;
|
|
101
|
+
declare function isVirtualThread(thread: AnyThread): thread is VirtualThread;
|
|
102
|
+
/** `CommentSerializer#author`; `null` on deleted placeholders. */
|
|
103
|
+
interface CommentAuthor {
|
|
104
|
+
id: number | null;
|
|
105
|
+
username: string | null;
|
|
106
|
+
name: string | null;
|
|
107
|
+
avatar_url: string | null;
|
|
108
|
+
title: string | null;
|
|
109
|
+
staff: boolean;
|
|
110
|
+
}
|
|
111
|
+
interface CommentReplyTo {
|
|
112
|
+
number: number;
|
|
113
|
+
username: string | null;
|
|
114
|
+
}
|
|
115
|
+
interface CommentLikes {
|
|
116
|
+
count: number;
|
|
117
|
+
liked: boolean;
|
|
118
|
+
can_like: boolean;
|
|
119
|
+
}
|
|
120
|
+
interface CommentPermissions {
|
|
121
|
+
edit: boolean;
|
|
122
|
+
delete: boolean;
|
|
123
|
+
flag: boolean;
|
|
124
|
+
pin: boolean;
|
|
125
|
+
}
|
|
126
|
+
/** `CommentSerializer`. Treat as immutable: the SDK replaces nodes instead of mutating them. */
|
|
127
|
+
interface Comment {
|
|
128
|
+
id: number;
|
|
129
|
+
number: number;
|
|
130
|
+
author: CommentAuthor | null;
|
|
131
|
+
/** Server-sanitized cooked HTML with absolute links; `""` for deleted placeholders. */
|
|
132
|
+
html: string;
|
|
133
|
+
created_at: string;
|
|
134
|
+
updated_at: string;
|
|
135
|
+
edited: boolean;
|
|
136
|
+
/** `null` for top-level comments. Carries the parent's post number, not its id. */
|
|
137
|
+
reply_to: CommentReplyTo | null;
|
|
138
|
+
children: Comment[];
|
|
139
|
+
direct_reply_count: number;
|
|
140
|
+
total_descendant_count: number;
|
|
141
|
+
pinned: boolean;
|
|
142
|
+
likes: CommentLikes;
|
|
143
|
+
can: CommentPermissions;
|
|
144
|
+
pending: boolean;
|
|
145
|
+
deleted: boolean;
|
|
146
|
+
hidden: boolean;
|
|
147
|
+
url: string | null;
|
|
148
|
+
}
|
|
149
|
+
/** `{ pending }` from `POST …/comments` when the comment went to the review queue. */
|
|
150
|
+
interface Pending {
|
|
151
|
+
reviewable_id: number | null;
|
|
152
|
+
message: string | null;
|
|
153
|
+
pending_count: number | null;
|
|
154
|
+
}
|
|
155
|
+
/** `UploadSerializer` as returned by `POST …/attachments`. */
|
|
156
|
+
interface Upload {
|
|
157
|
+
id: number;
|
|
158
|
+
url: string;
|
|
159
|
+
original_filename: string;
|
|
160
|
+
filesize: number;
|
|
161
|
+
width: number | null;
|
|
162
|
+
height: number | null;
|
|
163
|
+
thumbnail_width: number | null;
|
|
164
|
+
thumbnail_height: number | null;
|
|
165
|
+
extension: string;
|
|
166
|
+
short_url: string;
|
|
167
|
+
short_path: string;
|
|
168
|
+
retain_hours: number | null;
|
|
169
|
+
human_filesize: string;
|
|
170
|
+
dominant_color: string | null;
|
|
171
|
+
}
|
|
172
|
+
/** Comment counts keyed by encoded ref (`url:…`, `id:<host>/<identifier>`, `t:<topic_id>`). */
|
|
173
|
+
type Counts = Record<string, number>;
|
|
174
|
+
type CountRef = {
|
|
175
|
+
url: string;
|
|
176
|
+
} | {
|
|
177
|
+
identifier: string;
|
|
178
|
+
host?: string;
|
|
179
|
+
} | {
|
|
180
|
+
topicId: number;
|
|
181
|
+
};
|
|
182
|
+
interface ThreadResponse {
|
|
183
|
+
thread: AnyThread;
|
|
184
|
+
}
|
|
185
|
+
interface RootsPageResponse {
|
|
186
|
+
thread: Thread;
|
|
187
|
+
comments: Comment[];
|
|
188
|
+
has_more: boolean;
|
|
189
|
+
page: number;
|
|
190
|
+
sort: Sort;
|
|
191
|
+
}
|
|
192
|
+
interface CommentsByIdsResponse {
|
|
193
|
+
thread: Thread;
|
|
194
|
+
comments: Comment[];
|
|
195
|
+
}
|
|
196
|
+
interface ChildrenPageResponse {
|
|
197
|
+
comments: Comment[];
|
|
198
|
+
has_more: boolean;
|
|
199
|
+
page: number;
|
|
200
|
+
}
|
|
201
|
+
interface CommentContextResponse {
|
|
202
|
+
ancestors: Comment[];
|
|
203
|
+
ancestors_truncated: boolean;
|
|
204
|
+
comment: Comment;
|
|
205
|
+
}
|
|
206
|
+
type CreateCommentResponse = {
|
|
207
|
+
comment: Comment;
|
|
208
|
+
thread: Thread;
|
|
209
|
+
} | {
|
|
210
|
+
pending: Pending;
|
|
211
|
+
};
|
|
212
|
+
interface CommentResponse {
|
|
213
|
+
comment: Comment;
|
|
214
|
+
}
|
|
215
|
+
interface PinsResponse {
|
|
216
|
+
pinned_ids: number[];
|
|
217
|
+
}
|
|
218
|
+
interface RawResponse {
|
|
219
|
+
id: number;
|
|
220
|
+
raw: string;
|
|
221
|
+
}
|
|
222
|
+
interface CountsResponse {
|
|
223
|
+
counts: Counts;
|
|
224
|
+
}
|
|
225
|
+
interface MentionsResponse {
|
|
226
|
+
users: Commenter[];
|
|
227
|
+
}
|
|
228
|
+
interface PreviewResponse {
|
|
229
|
+
html: string;
|
|
230
|
+
}
|
|
231
|
+
interface ExchangeResponse {
|
|
232
|
+
key: string;
|
|
233
|
+
client_id: string;
|
|
234
|
+
expires_at: string | null;
|
|
235
|
+
user: Commenter;
|
|
236
|
+
}
|
|
237
|
+
/** Server error body (`create_errors_json`). */
|
|
238
|
+
interface ErrorResponse {
|
|
239
|
+
errors?: string[];
|
|
240
|
+
error_type?: string;
|
|
241
|
+
extras?: Record<string, unknown>;
|
|
242
|
+
degraded?: boolean;
|
|
243
|
+
}
|
|
244
|
+
/** One palette of a theme (`ForumTheme#scheme_payload`). */
|
|
245
|
+
interface ForumColorScheme {
|
|
246
|
+
id: number;
|
|
247
|
+
name: string;
|
|
248
|
+
type: "light" | "dark";
|
|
249
|
+
/**
|
|
250
|
+
* Every custom property the forum's colour definitions emit, without the
|
|
251
|
+
* `--` prefix and with references already resolved: `primary`, `secondary`,
|
|
252
|
+
* `tertiary`, `primary-low`, `primary-medium`, `danger`, …
|
|
253
|
+
*/
|
|
254
|
+
colors: Record<string, string>;
|
|
255
|
+
}
|
|
256
|
+
/** A `@font-face` source the forum serves. */
|
|
257
|
+
interface ForumFontFace {
|
|
258
|
+
family: string;
|
|
259
|
+
/** A CSS `src` value, e.g. `url("https://forum/fonts/x.woff2") format("woff2")`. */
|
|
260
|
+
src: string;
|
|
261
|
+
weight: number | string;
|
|
262
|
+
}
|
|
263
|
+
interface ForumFont {
|
|
264
|
+
key: string;
|
|
265
|
+
name: string;
|
|
266
|
+
/** A CSS `font-family` value. */
|
|
267
|
+
stack: string;
|
|
268
|
+
/** Empty for fonts the forum expects to be installed. */
|
|
269
|
+
faces: ForumFontFace[];
|
|
270
|
+
}
|
|
271
|
+
/** `GET /comments/theme`: the forum's look as data. */
|
|
272
|
+
interface ForumTheme {
|
|
273
|
+
theme: {
|
|
274
|
+
id: number;
|
|
275
|
+
name: string;
|
|
276
|
+
} | null;
|
|
277
|
+
schemes: {
|
|
278
|
+
light: ForumColorScheme;
|
|
279
|
+
/** Present when the theme has a dark palette. */
|
|
280
|
+
dark: ForumColorScheme | null;
|
|
281
|
+
};
|
|
282
|
+
fonts: {
|
|
283
|
+
body: ForumFont | null;
|
|
284
|
+
heading: ForumFont | null;
|
|
285
|
+
monospace: ForumFont | null;
|
|
286
|
+
};
|
|
287
|
+
/** The theme's "Embedded CSS" stylesheets, absolute URLs. */
|
|
288
|
+
stylesheets: string[];
|
|
289
|
+
/** Changes whenever any of the above changes. */
|
|
290
|
+
version: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
type CommentsErrorType = "invalid_key" | "not_logged_in" | "invalid_access" | "invalid_parameters" | "not_found" | "rate_limit" | "thread_conflict" | "page_not_allowed" | "invalid_url" | "no_category" | "thread_not_created" | "invalid_grant" | "user_not_allowed" | "submission_rejected" | "not_a_root_comment" | "pin_limit_reached" | "cancelled" | "storage_unavailable" | "unreachable" | "unknown";
|
|
294
|
+
interface CommentsErrorOptions {
|
|
295
|
+
status?: number;
|
|
296
|
+
errors?: string[];
|
|
297
|
+
extras?: Record<string, unknown>;
|
|
298
|
+
waitSeconds?: number;
|
|
299
|
+
hint?: string;
|
|
300
|
+
serverType?: string;
|
|
301
|
+
cause?: unknown;
|
|
302
|
+
}
|
|
303
|
+
declare class CommentsError extends Error {
|
|
304
|
+
readonly name = "CommentsError";
|
|
305
|
+
readonly type: CommentsErrorType;
|
|
306
|
+
/** HTTP status, or 0 when no response was received. */
|
|
307
|
+
readonly status: number;
|
|
308
|
+
/** Human-readable messages from the server, safe to show inline. */
|
|
309
|
+
readonly errors: string[];
|
|
310
|
+
readonly extras: Record<string, unknown> | undefined;
|
|
311
|
+
/** Seconds until a rate-limited action may be retried. */
|
|
312
|
+
readonly waitSeconds: number | undefined;
|
|
313
|
+
/** Developer-facing explanation of the probable cause (setup problems). */
|
|
314
|
+
readonly hint: string | undefined;
|
|
315
|
+
/** The raw `error_type` from the server, if it sent one. */
|
|
316
|
+
readonly serverType: string | undefined;
|
|
317
|
+
constructor(type: CommentsErrorType, message: string, options?: CommentsErrorOptions);
|
|
318
|
+
}
|
|
319
|
+
declare function isCommentsError(error: unknown): error is CommentsError;
|
|
320
|
+
|
|
321
|
+
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
322
|
+
|
|
323
|
+
/** The subset of the Web Storage API the client needs. */
|
|
324
|
+
interface StorageLike {
|
|
325
|
+
getItem(key: string): string | null;
|
|
326
|
+
setItem(key: string, value: string): void;
|
|
327
|
+
removeItem(key: string): void;
|
|
328
|
+
/**
|
|
329
|
+
* `false` when values do not survive a page navigation (memory). Redirect
|
|
330
|
+
* sign-in needs storage that does; anything else is assumed durable.
|
|
331
|
+
*/
|
|
332
|
+
readonly durable?: boolean;
|
|
333
|
+
}
|
|
334
|
+
declare function memoryStorage(): StorageLike;
|
|
335
|
+
|
|
336
|
+
type AuthStatus = "anonymous" | "signing-in" | "signed-in";
|
|
337
|
+
interface AuthState {
|
|
338
|
+
readonly status: AuthStatus;
|
|
339
|
+
readonly user: Commenter | null;
|
|
340
|
+
/** The forum served a request anonymously under load. The key is kept; treat the UI as read-only. */
|
|
341
|
+
readonly degraded: boolean;
|
|
342
|
+
/** Why the last sign-in failed or the session ended (`invalid_key` means it expired). */
|
|
343
|
+
readonly error: CommentsError | null;
|
|
344
|
+
}
|
|
345
|
+
/** What is persisted under `gc:<forum>:auth`. */
|
|
346
|
+
interface StoredAuth {
|
|
347
|
+
key: string;
|
|
348
|
+
client_id: string;
|
|
349
|
+
expires_at: string | null;
|
|
350
|
+
user: Commenter;
|
|
351
|
+
}
|
|
352
|
+
interface SignInOptions {
|
|
353
|
+
/** An `auth_providers[].name` to go straight to that provider. */
|
|
354
|
+
provider?: string;
|
|
355
|
+
/** Defaults to `popup`; a blocked popup falls back to `redirect`. */
|
|
356
|
+
mode?: "popup" | "redirect";
|
|
357
|
+
}
|
|
358
|
+
interface CommentsAuth {
|
|
359
|
+
readonly status: AuthStatus;
|
|
360
|
+
readonly user: Commenter | null;
|
|
361
|
+
/** From `config().auth_providers`; empty until the config has loaded. */
|
|
362
|
+
readonly providers: readonly AuthProvider[];
|
|
363
|
+
/** Per-browser id the forum's user API key is bound to. */
|
|
364
|
+
readonly clientId: string;
|
|
365
|
+
/** Immutable, referentially stable while nothing changes. */
|
|
366
|
+
state(): AuthState;
|
|
367
|
+
onChange(listener: (state: AuthState) => void): () => void;
|
|
368
|
+
/** Resolves with the user (popup) or `null` once a redirect has started. */
|
|
369
|
+
signIn(options?: SignInOptions): Promise<Commenter | null>;
|
|
370
|
+
/** Abandons a pending popup sign-in. */
|
|
371
|
+
cancelSignIn(): void;
|
|
372
|
+
signOut(): Promise<void>;
|
|
373
|
+
/** Completes a redirect sign-in from `#gc_code=…&gc_nonce=…`; runs once per page load. */
|
|
374
|
+
handleRedirect(): Promise<Commenter | null>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
interface ComposerTiming {
|
|
378
|
+
typingDurationMsecs: number;
|
|
379
|
+
composerOpenDurationMsecs: number;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Measures what the forum's fast-typer check needs (`typing_duration_msecs`,
|
|
383
|
+
* `composer_open_duration_msecs`). Without it, a new account's first comments
|
|
384
|
+
* land in the review queue.
|
|
385
|
+
*/
|
|
386
|
+
interface ComposerTimer {
|
|
387
|
+
readonly started: boolean;
|
|
388
|
+
/** Call when the composer opens or gains focus; repeated calls are no-ops. */
|
|
389
|
+
start(): void;
|
|
390
|
+
/** Call on every input event. */
|
|
391
|
+
typing(): void;
|
|
392
|
+
measure(): ComposerTiming;
|
|
393
|
+
/** Returns the measurement and resets the timer. */
|
|
394
|
+
stop(): ComposerTiming;
|
|
395
|
+
}
|
|
396
|
+
declare function createComposerTimer(now?: () => number): ComposerTimer;
|
|
397
|
+
|
|
398
|
+
type ThreadStatus = "idle" | "loading" | "ready" | "error";
|
|
399
|
+
/** How updates arrive: MessageBus, the interval fallback, or not at all. */
|
|
400
|
+
type ConnectionMode = "live" | "interval" | "off";
|
|
401
|
+
interface BranchState {
|
|
402
|
+
/** Last children page loaded for this comment (-1 = only the preloaded ones). */
|
|
403
|
+
readonly page: number;
|
|
404
|
+
readonly loading: boolean;
|
|
405
|
+
readonly hasMore: boolean;
|
|
406
|
+
}
|
|
407
|
+
interface ThreadSnapshot {
|
|
408
|
+
readonly thread: AnyThread;
|
|
409
|
+
/** True until the page's topic exists (`thread.id` starts with `v:`). */
|
|
410
|
+
readonly virtual: boolean;
|
|
411
|
+
/** Top-level comments with their loaded replies nested in `children`. */
|
|
412
|
+
readonly comments: readonly Comment[];
|
|
413
|
+
readonly sort: Sort;
|
|
414
|
+
readonly status: ThreadStatus;
|
|
415
|
+
readonly error: CommentsError | null;
|
|
416
|
+
readonly page: number;
|
|
417
|
+
readonly hasMore: boolean;
|
|
418
|
+
readonly loadingMore: boolean;
|
|
419
|
+
/** Top-level comments that arrived live and wait for `showPending()`. */
|
|
420
|
+
readonly pendingNew: number;
|
|
421
|
+
readonly branches: Readonly<Record<number, BranchState>>;
|
|
422
|
+
/** The reader's own comments that went to the review queue. */
|
|
423
|
+
readonly awaitingApproval: readonly Pending[];
|
|
424
|
+
readonly connection: ConnectionMode;
|
|
425
|
+
}
|
|
426
|
+
type ThreadEvent = {
|
|
427
|
+
type: "change";
|
|
428
|
+
snapshot: ThreadSnapshot;
|
|
429
|
+
} | {
|
|
430
|
+
type: "comment.posted";
|
|
431
|
+
comment: Comment;
|
|
432
|
+
} | {
|
|
433
|
+
type: "comment.pending";
|
|
434
|
+
pending: Pending;
|
|
435
|
+
} | {
|
|
436
|
+
type: "comment.edited";
|
|
437
|
+
comment: Comment;
|
|
438
|
+
} | {
|
|
439
|
+
type: "comment.removed";
|
|
440
|
+
id: number;
|
|
441
|
+
} | {
|
|
442
|
+
type: "comment.received";
|
|
443
|
+
comment: Comment;
|
|
444
|
+
buffered: boolean;
|
|
445
|
+
} | {
|
|
446
|
+
type: "thread.created";
|
|
447
|
+
thread: Thread;
|
|
448
|
+
};
|
|
449
|
+
interface PostOptions {
|
|
450
|
+
body: string;
|
|
451
|
+
/** Id of the comment being replied to (never a post number). */
|
|
452
|
+
replyTo?: number;
|
|
453
|
+
typingDurationMsecs?: number;
|
|
454
|
+
composerOpenDurationMsecs?: number;
|
|
455
|
+
/** Timer to read when the durations are not passed; defaults to `thread.composer`. */
|
|
456
|
+
composer?: ComposerTimer;
|
|
457
|
+
}
|
|
458
|
+
type PostResult = {
|
|
459
|
+
type: "created";
|
|
460
|
+
comment: Comment;
|
|
461
|
+
} | {
|
|
462
|
+
type: "pending";
|
|
463
|
+
pending: Pending;
|
|
464
|
+
};
|
|
465
|
+
interface FlagOptions {
|
|
466
|
+
type: FlagType;
|
|
467
|
+
message?: string;
|
|
468
|
+
}
|
|
469
|
+
interface CommentContext {
|
|
470
|
+
ancestors: Comment[];
|
|
471
|
+
ancestorsTruncated: boolean;
|
|
472
|
+
comment: Comment;
|
|
473
|
+
}
|
|
474
|
+
interface ThreadHandle {
|
|
475
|
+
/** Current handle; changes from `v:…` to the topic id once the thread is created. */
|
|
476
|
+
readonly id: string;
|
|
477
|
+
/** Default timer for the thread's main composer. */
|
|
478
|
+
readonly composer: ComposerTimer;
|
|
479
|
+
/** Immutable; the same object is returned until something changes. */
|
|
480
|
+
snapshot(): ThreadSnapshot;
|
|
481
|
+
subscribe(listener: (event: ThreadEvent) => void): () => void;
|
|
482
|
+
load(): Promise<void>;
|
|
483
|
+
loadMore(): Promise<void>;
|
|
484
|
+
loadReplies(commentId: number): Promise<void>;
|
|
485
|
+
setSort(sort: Sort): Promise<void>;
|
|
486
|
+
post(options: PostOptions): Promise<PostResult>;
|
|
487
|
+
edit(commentId: number, body: string): Promise<Comment>;
|
|
488
|
+
remove(commentId: number): Promise<void>;
|
|
489
|
+
like(commentId: number): Promise<Comment>;
|
|
490
|
+
unlike(commentId: number): Promise<Comment>;
|
|
491
|
+
flag(commentId: number, options: FlagOptions): Promise<void>;
|
|
492
|
+
pin(commentId: number): Promise<void>;
|
|
493
|
+
unpin(commentId: number): Promise<void>;
|
|
494
|
+
/** Markdown source, for editing. */
|
|
495
|
+
raw(commentId: number): Promise<string>;
|
|
496
|
+
/** Moves live-arrived top-level comments into `comments`. */
|
|
497
|
+
showPending(): void;
|
|
498
|
+
/** Loads a comment with its ancestors into the tree (deep links). */
|
|
499
|
+
context(commentId: number): Promise<CommentContext>;
|
|
500
|
+
dispose(): void;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
interface CreateClientOptions {
|
|
504
|
+
/** The forum's base URL, e.g. `https://forum.example.com` (a trailing slash is fine). */
|
|
505
|
+
forum: string;
|
|
506
|
+
/** Custom fetch (SSR, tests, proxies). Defaults to the global fetch. */
|
|
507
|
+
fetch?: FetchLike;
|
|
508
|
+
/** Extra headers for comments API requests, e.g. `Authorization` for a forum behind basic auth. */
|
|
509
|
+
headers?: Record<string, string>;
|
|
510
|
+
/** Where the key and client id are kept. Defaults to `localStorage` (memory if unavailable). */
|
|
511
|
+
storage?: StorageLike;
|
|
512
|
+
/** Fixed client id (`[\w-]{8,64}`) instead of a generated per-browser one. */
|
|
513
|
+
clientId?: string;
|
|
514
|
+
/** Sent as `Accept-Language` so server messages match the widget. */
|
|
515
|
+
locale?: string;
|
|
516
|
+
/** Logs requests and realtime activity to the console. */
|
|
517
|
+
debug?: boolean;
|
|
518
|
+
}
|
|
519
|
+
interface OpenThreadOptions {
|
|
520
|
+
/** Defaults to `<link rel="canonical">`, else `location.href`. */
|
|
521
|
+
url?: string;
|
|
522
|
+
/** Defaults to `document.title`. */
|
|
523
|
+
title?: string;
|
|
524
|
+
/** Optional stable key, namespaced by the page's canonical host on the forum. */
|
|
525
|
+
identifier?: string;
|
|
526
|
+
/** Initial sort; defaults to the forum's default. */
|
|
527
|
+
sort?: Sort;
|
|
528
|
+
}
|
|
529
|
+
interface UploadOptions {
|
|
530
|
+
filename?: string;
|
|
531
|
+
/** Marks the file as pasted from the clipboard. */
|
|
532
|
+
pasted?: boolean;
|
|
533
|
+
}
|
|
534
|
+
interface CommentsClient {
|
|
535
|
+
/** Normalized forum base URL. */
|
|
536
|
+
readonly forum: string;
|
|
537
|
+
readonly forumOrigin: string;
|
|
538
|
+
readonly locale: string | undefined;
|
|
539
|
+
readonly auth: CommentsAuth;
|
|
540
|
+
readonly threads: {
|
|
541
|
+
/** Resolves (or creates) the page's thread and loads its first page of comments. */
|
|
542
|
+
open(options?: OpenThreadOptions): Promise<ThreadHandle>;
|
|
543
|
+
};
|
|
544
|
+
readonly attachments: {
|
|
545
|
+
upload(threadId: string, file: Blob, options?: UploadOptions): Promise<Upload>;
|
|
546
|
+
};
|
|
547
|
+
/** Cached; refetched after sign-in and sign-out. */
|
|
548
|
+
config(): Promise<Config>;
|
|
549
|
+
/** The forum's palettes, fonts and theme stylesheets; cached for the client's lifetime. */
|
|
550
|
+
theme(): Promise<ForumTheme>;
|
|
551
|
+
/** Batched per tick, at most 50 refs per request. Keys are encoded refs (see `encodeRef`). */
|
|
552
|
+
counts(refs: readonly CountRef[]): Promise<Counts>;
|
|
553
|
+
count(ref: CountRef): Promise<number>;
|
|
554
|
+
/** Cooks markdown into sanitized HTML (signed-in only). */
|
|
555
|
+
preview(body: string): Promise<string>;
|
|
556
|
+
/** User search for @mentions (signed-in only). */
|
|
557
|
+
mentions(query: string, threadId?: string): Promise<Commenter[]>;
|
|
558
|
+
/** Stops realtime, timers and listeners for every thread of this client. */
|
|
559
|
+
dispose(): void;
|
|
560
|
+
}
|
|
561
|
+
declare function createClient(options: CreateClientOptions): CommentsClient;
|
|
562
|
+
|
|
563
|
+
interface PluralMessage {
|
|
564
|
+
/** Used for a count of exactly 0 when present. */
|
|
565
|
+
zero?: string;
|
|
566
|
+
one: string;
|
|
567
|
+
two?: string;
|
|
568
|
+
few?: string;
|
|
569
|
+
many?: string;
|
|
570
|
+
other: string;
|
|
571
|
+
}
|
|
572
|
+
type MessageValue = string | PluralMessage;
|
|
573
|
+
declare const en: {
|
|
574
|
+
readonly signIn: "Sign in";
|
|
575
|
+
readonly signInWith: "Continue with {provider}";
|
|
576
|
+
readonly signOut: "Sign out";
|
|
577
|
+
readonly signingIn: "Finish signing in in the window that opened.";
|
|
578
|
+
readonly signInCancel: "Cancel";
|
|
579
|
+
readonly sessionExpired: "Your session expired. Sign in again to comment.";
|
|
580
|
+
readonly joinConversation: "Join the conversation";
|
|
581
|
+
readonly joinConversationBody: "Sign in with your {forum} account to comment.";
|
|
582
|
+
readonly composerPlaceholder: "Add a comment…";
|
|
583
|
+
readonly post: "Post";
|
|
584
|
+
readonly posting: "Posting…";
|
|
585
|
+
readonly reply: "Reply";
|
|
586
|
+
readonly replyingTo: "Replying to @{username}";
|
|
587
|
+
readonly cancel: "Cancel";
|
|
588
|
+
readonly save: "Save";
|
|
589
|
+
readonly edit: "Edit";
|
|
590
|
+
readonly edited: "Edited";
|
|
591
|
+
readonly delete: "Delete";
|
|
592
|
+
readonly deleteConfirm: "Delete this comment?";
|
|
593
|
+
readonly deleted: "This comment was deleted.";
|
|
594
|
+
readonly hidden: "This comment was hidden after reports from the community.";
|
|
595
|
+
readonly like: "Like";
|
|
596
|
+
readonly unlike: "Remove like";
|
|
597
|
+
readonly likeCount: {
|
|
598
|
+
readonly zero: "No likes";
|
|
599
|
+
readonly one: "{count} like";
|
|
600
|
+
readonly other: "{count} likes";
|
|
601
|
+
};
|
|
602
|
+
readonly report: "Report";
|
|
603
|
+
readonly reportTitle: "Why are you reporting this comment?";
|
|
604
|
+
readonly "report.off_topic": "Off-topic";
|
|
605
|
+
readonly "report.inappropriate": "Inappropriate";
|
|
606
|
+
readonly "report.spam": "Spam";
|
|
607
|
+
readonly "report.illegal": "Illegal";
|
|
608
|
+
readonly "report.notify_moderators": "Something else";
|
|
609
|
+
readonly reportMessagePlaceholder: "Tell the moderators more (optional)";
|
|
610
|
+
readonly reportSent: "Thanks, the moderators will take a look.";
|
|
611
|
+
readonly pin: "Feature";
|
|
612
|
+
readonly unpin: "Unfeature";
|
|
613
|
+
readonly pinned: "Featured";
|
|
614
|
+
readonly pending: "Your comment was submitted and is awaiting approval.";
|
|
615
|
+
readonly loadMore: "Load more comments";
|
|
616
|
+
readonly loadReplies: {
|
|
617
|
+
readonly one: "View {count} reply";
|
|
618
|
+
readonly other: "View {count} replies";
|
|
619
|
+
};
|
|
620
|
+
readonly continueThread: "Continue this thread";
|
|
621
|
+
readonly newComments: {
|
|
622
|
+
readonly one: "{count} new comment";
|
|
623
|
+
readonly other: "{count} new comments";
|
|
624
|
+
};
|
|
625
|
+
readonly commentCount: {
|
|
626
|
+
readonly zero: "No comments yet";
|
|
627
|
+
readonly one: "{count} comment";
|
|
628
|
+
readonly other: "{count} comments";
|
|
629
|
+
};
|
|
630
|
+
readonly sortBy: "Sort by";
|
|
631
|
+
readonly "sort.best": "Best";
|
|
632
|
+
readonly "sort.hot": "Hot";
|
|
633
|
+
readonly "sort.newest": "Newest";
|
|
634
|
+
readonly "sort.oldest": "Oldest";
|
|
635
|
+
readonly closed: "Comments are closed.";
|
|
636
|
+
readonly readOnly: "Comments are read-only right now.";
|
|
637
|
+
readonly guidelines: "Community guidelines";
|
|
638
|
+
readonly preview: "Preview";
|
|
639
|
+
readonly write: "Write";
|
|
640
|
+
readonly unavailable: "Comments are unavailable right now.";
|
|
641
|
+
readonly retry: "Try again";
|
|
642
|
+
readonly rateLimited: {
|
|
643
|
+
readonly one: "You're doing that too often. Try again in {count} second.";
|
|
644
|
+
readonly other: "You're doing that too often. Try again in {count} seconds.";
|
|
645
|
+
};
|
|
646
|
+
readonly genericError: "Something went wrong. Please try again.";
|
|
647
|
+
readonly copyLink: "Copy link";
|
|
648
|
+
readonly linkCopied: "Link copied";
|
|
649
|
+
readonly viewOnForum: "View on {forum}";
|
|
650
|
+
readonly staff: "Staff";
|
|
651
|
+
readonly upload: "Attach image";
|
|
652
|
+
readonly uploading: "Uploading…";
|
|
653
|
+
readonly comments: "Comments";
|
|
654
|
+
readonly signedInAs: "Signed in as {username}";
|
|
655
|
+
readonly loadMoreReplies: {
|
|
656
|
+
readonly one: "View {count} more reply";
|
|
657
|
+
readonly other: "View {count} more replies";
|
|
658
|
+
};
|
|
659
|
+
readonly backToAll: "Back to all comments";
|
|
660
|
+
readonly ancestorsTruncated: "Some earlier replies are not shown.";
|
|
661
|
+
readonly moreActions: "More actions";
|
|
662
|
+
readonly commentBy: "Comment by {name}";
|
|
663
|
+
readonly deletedComment: "Deleted comment";
|
|
664
|
+
readonly repliesTo: "Replies to {name}";
|
|
665
|
+
readonly replyPlaceholder: "Write a reply…";
|
|
666
|
+
readonly editing: "Editing your comment";
|
|
667
|
+
readonly tooShort: "Comments need at least {min} characters.";
|
|
668
|
+
readonly tooLong: "Comments can have at most {max} characters.";
|
|
669
|
+
readonly formatting: "Formatting";
|
|
670
|
+
readonly bold: "Bold";
|
|
671
|
+
readonly italic: "Italic";
|
|
672
|
+
readonly insertLink: "Insert link";
|
|
673
|
+
readonly code: "Code";
|
|
674
|
+
readonly quote: "Quote";
|
|
675
|
+
readonly mentionSuggestions: "Mention suggestions";
|
|
676
|
+
readonly previewEmpty: "Nothing to preview yet.";
|
|
677
|
+
readonly loading: "Loading…";
|
|
678
|
+
readonly cannotComment: "You can't comment on this page.";
|
|
679
|
+
readonly reportMessageRequired: "Tell the moderators what's wrong";
|
|
680
|
+
readonly close: "Close";
|
|
681
|
+
};
|
|
682
|
+
type MessageKey = keyof typeof en;
|
|
683
|
+
/** A full table: plural entries stay plural in every locale. */
|
|
684
|
+
type Messages = {
|
|
685
|
+
[K in MessageKey]: (typeof en)[K] extends string ? string : PluralMessage;
|
|
686
|
+
};
|
|
687
|
+
declare const ptBR: Messages;
|
|
688
|
+
declare const locales: {
|
|
689
|
+
readonly en: Messages;
|
|
690
|
+
readonly "pt-BR": Messages;
|
|
691
|
+
};
|
|
692
|
+
type SupportedLocale = keyof typeof locales;
|
|
693
|
+
type MessageVars = Record<string, string | number>;
|
|
694
|
+
type Translator = (key: MessageKey, vars?: MessageVars) => string;
|
|
695
|
+
/** Closest bundled locale: any `pt*` maps to `pt-BR`, everything else to `en`. */
|
|
696
|
+
declare function resolveLocale(locale: string | undefined): SupportedLocale;
|
|
697
|
+
declare function createTranslator(locale?: string, overrides?: Partial<Messages>): Translator;
|
|
698
|
+
|
|
699
|
+
export { type RawResponse as $, type AnyThread as A, type BranchState as B, type CreateClientOptions as C, type ExchangeResponse as D, type ErrorResponse as E, type Features as F, type FetchLike as G, type FlagOptions as H, type FlagType as I, type ForumColorScheme as J, type ForumFont as K, type ForumFontFace as L, type ForumInfo as M, type ForumTheme as N, type Limits as O, type MentionsResponse as P, type MessageKey as Q, type MessageValue as R, type MessageVars as S, type Messages as T, type OpenThreadOptions as U, type Pending as V, type PinsResponse as W, type PluralMessage as X, type PostOptions as Y, type PostResult as Z, type PreviewResponse as _, type CommentsClient as a, type RootsPageResponse as a0, type SignInOptions as a1, type Sort as a2, type StorageLike as a3, type StoredAuth as a4, type SupportedLocale as a5, type Thread as a6, type ThreadCreation as a7, type ThreadEvent as a8, type ThreadHandle as a9, type ThreadResponse as aa, type ThreadSnapshot as ab, type ThreadStatus as ac, type Translator as ad, type Upload as ae, type UploadOptions as af, type VirtualThread as ag, createClient as ah, createComposerTimer as ai, createTranslator as aj, en as ak, isCommentsError as al, isVirtualThread as am, locales as an, memoryStorage as ao, ptBR as ap, resolveLocale as aq, type CountRef as b, type AuthProvider as c, type AuthState as d, type AuthStatus as e, type ChildrenPageResponse as f, type Comment as g, type CommentAuthor as h, type CommentContext as i, type CommentContextResponse as j, type CommentLikes as k, type CommentPermissions as l, type CommentReplyTo as m, type CommentResponse as n, type Commenter as o, type CommentsAuth as p, type CommentsByIdsResponse as q, CommentsError as r, type CommentsErrorType as s, type ComposerTimer as t, type ComposerTiming as u, type Config as v, type ConnectionMode as w, type Counts as x, type CountsResponse as y, type CreateCommentResponse as z };
|