@nuniapp/widget 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -0
- package/dist/index.js +228 -59
- package/dist/nuni.global.js +13 -5
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,11 @@ declare const EN: {
|
|
|
49
49
|
yourName: string;
|
|
50
50
|
leaveComment: string;
|
|
51
51
|
notYou: string;
|
|
52
|
+
signIn: string;
|
|
53
|
+
signInGithub: string;
|
|
54
|
+
signOut: string;
|
|
55
|
+
signedInAs: string;
|
|
56
|
+
signedInGithub: string;
|
|
52
57
|
cancel: string;
|
|
53
58
|
post: string;
|
|
54
59
|
posting: string;
|
|
@@ -122,6 +127,8 @@ declare const EN: {
|
|
|
122
127
|
copied: string;
|
|
123
128
|
copyFailed: string;
|
|
124
129
|
signedIn: string;
|
|
130
|
+
commenterSignedIn: string;
|
|
131
|
+
signedOut: string;
|
|
125
132
|
linkGone: string;
|
|
126
133
|
enterName: string;
|
|
127
134
|
writeFirst: string;
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,7 @@ function buildCommentPrompt(comment, options = {}) {
|
|
|
176
176
|
}
|
|
177
177
|
//#endregion
|
|
178
178
|
//#region src/config.ts
|
|
179
|
-
const VERSION = "0.1.
|
|
179
|
+
const VERSION = "0.1.11";
|
|
180
180
|
function resolveConfig(options) {
|
|
181
181
|
return {
|
|
182
182
|
project: options.project,
|
|
@@ -5285,10 +5285,10 @@ var NuniApi = class {
|
|
|
5285
5285
|
}, (comments) => cb(comments), onError);
|
|
5286
5286
|
}
|
|
5287
5287
|
/** The visitor's own comments on every page, live. */
|
|
5288
|
-
onMine(
|
|
5288
|
+
onMine(author, cb) {
|
|
5289
5289
|
return this.client.onUpdate(api.comments.listMine, {
|
|
5290
5290
|
publicId: this.config.project,
|
|
5291
|
-
|
|
5291
|
+
...author
|
|
5292
5292
|
}, (comments) => cb(comments), () => {});
|
|
5293
5293
|
}
|
|
5294
5294
|
onPages(cb) {
|
|
@@ -5300,6 +5300,24 @@ var NuniApi = class {
|
|
|
5300
5300
|
sessionToken
|
|
5301
5301
|
}, cb, () => {});
|
|
5302
5302
|
}
|
|
5303
|
+
/** Who a stored commenter sign-in belongs to, live (invalid once revoked). */
|
|
5304
|
+
onCommenter(commenterToken, cb) {
|
|
5305
|
+
return this.client.onUpdate(api.commenters.validate, {
|
|
5306
|
+
publicId: this.config.project,
|
|
5307
|
+
commenterToken
|
|
5308
|
+
}, cb, () => {});
|
|
5309
|
+
}
|
|
5310
|
+
/** Right after signing in: this browser's guest comments become the account's. */
|
|
5311
|
+
linkBrowser(authorSecret, commenterToken) {
|
|
5312
|
+
return this.run(this.client.mutation(api.commenters.linkBrowser, {
|
|
5313
|
+
publicId: this.config.project,
|
|
5314
|
+
authorSecret,
|
|
5315
|
+
commenterToken
|
|
5316
|
+
}));
|
|
5317
|
+
}
|
|
5318
|
+
signOutCommenter(commenterToken) {
|
|
5319
|
+
return this.client.mutation(api.commenters.signOut, { commenterToken }).catch(() => null);
|
|
5320
|
+
}
|
|
5303
5321
|
/** A comment with the owner-only context (screenshot, console, DOM). */
|
|
5304
5322
|
onOwnerComment(id, sessionToken, cb) {
|
|
5305
5323
|
return this.client.onUpdate(api.comments.getForOwner, {
|
|
@@ -5360,21 +5378,22 @@ var NuniApi = class {
|
|
|
5360
5378
|
return data.id;
|
|
5361
5379
|
}
|
|
5362
5380
|
/** Attach the element's screenshot (for the owner) to a just-posted comment. */
|
|
5363
|
-
uploadScreenshot(id,
|
|
5364
|
-
return this.upload("screenshot", id,
|
|
5381
|
+
uploadScreenshot(id, author, image) {
|
|
5382
|
+
return this.upload("screenshot", id, author, image);
|
|
5365
5383
|
}
|
|
5366
5384
|
/** An image for everyone: picked, pasted or a marked-up screenshot. */
|
|
5367
|
-
uploadImage(id,
|
|
5368
|
-
return this.upload("image", id,
|
|
5385
|
+
uploadImage(id, author, image) {
|
|
5386
|
+
return this.upload("image", id, author, image);
|
|
5369
5387
|
}
|
|
5370
|
-
async upload(kind, id,
|
|
5388
|
+
async upload(kind, id, author, image) {
|
|
5371
5389
|
const response = await fetch(`${this.config.convexSiteUrl}/widget/${kind}`, {
|
|
5372
5390
|
method: "POST",
|
|
5373
5391
|
headers: {
|
|
5374
5392
|
"Content-Type": image.type,
|
|
5375
5393
|
"X-Nuni-Project": this.config.project,
|
|
5376
5394
|
"X-Nuni-Comment": id,
|
|
5377
|
-
"X-Nuni-Author": authorSecret
|
|
5395
|
+
"X-Nuni-Author": author.authorSecret,
|
|
5396
|
+
...author.commenterToken ? { "X-Nuni-Commenter": author.commenterToken } : {}
|
|
5378
5397
|
},
|
|
5379
5398
|
body: image
|
|
5380
5399
|
});
|
|
@@ -5390,18 +5409,18 @@ var NuniApi = class {
|
|
|
5390
5409
|
throw new NuniApiError(errorMessage(error), "error");
|
|
5391
5410
|
}
|
|
5392
5411
|
}
|
|
5393
|
-
editOwn(id,
|
|
5412
|
+
editOwn(id, author, body, suggestion) {
|
|
5394
5413
|
return this.run(this.client.mutation(api.comments.editOwn, {
|
|
5395
5414
|
id,
|
|
5396
|
-
|
|
5415
|
+
...author,
|
|
5397
5416
|
body,
|
|
5398
5417
|
suggestion
|
|
5399
5418
|
}));
|
|
5400
5419
|
}
|
|
5401
|
-
deleteOwn(id,
|
|
5420
|
+
deleteOwn(id, author) {
|
|
5402
5421
|
return this.run(this.client.mutation(api.comments.deleteOwn, {
|
|
5403
5422
|
id,
|
|
5404
|
-
|
|
5423
|
+
...author
|
|
5405
5424
|
}));
|
|
5406
5425
|
}
|
|
5407
5426
|
resolve(id, sessionToken, resolution) {
|
|
@@ -5444,17 +5463,17 @@ var NuniApi = class {
|
|
|
5444
5463
|
sessionToken
|
|
5445
5464
|
}));
|
|
5446
5465
|
}
|
|
5447
|
-
editReply(id,
|
|
5466
|
+
editReply(id, author, body) {
|
|
5448
5467
|
return this.run(this.client.mutation(api.replies.editOwn, {
|
|
5449
5468
|
id,
|
|
5450
|
-
|
|
5469
|
+
...author,
|
|
5451
5470
|
body
|
|
5452
5471
|
}));
|
|
5453
5472
|
}
|
|
5454
|
-
deleteReply(id,
|
|
5473
|
+
deleteReply(id, author) {
|
|
5455
5474
|
return this.run(this.client.mutation(api.replies.deleteOwn, {
|
|
5456
5475
|
id,
|
|
5457
|
-
|
|
5476
|
+
...author
|
|
5458
5477
|
}));
|
|
5459
5478
|
}
|
|
5460
5479
|
removeReply(id, sessionToken) {
|
|
@@ -5463,13 +5482,13 @@ var NuniApi = class {
|
|
|
5463
5482
|
sessionToken
|
|
5464
5483
|
}));
|
|
5465
5484
|
}
|
|
5466
|
-
toggleReaction(commentId, targetId, emoji,
|
|
5485
|
+
toggleReaction(commentId, targetId, emoji, author) {
|
|
5467
5486
|
return this.run(this.client.mutation(api.reactions.toggle, {
|
|
5468
5487
|
publicId: this.config.project,
|
|
5469
5488
|
commentId,
|
|
5470
5489
|
targetId,
|
|
5471
5490
|
emoji,
|
|
5472
|
-
|
|
5491
|
+
...author
|
|
5473
5492
|
}));
|
|
5474
5493
|
}
|
|
5475
5494
|
remove(id, sessionToken) {
|
|
@@ -5531,6 +5550,11 @@ const EN = {
|
|
|
5531
5550
|
yourName: "Your name",
|
|
5532
5551
|
leaveComment: "Leave a comment",
|
|
5533
5552
|
notYou: "Not you?",
|
|
5553
|
+
signIn: "Sign in",
|
|
5554
|
+
signInGithub: "Sign in with GitHub to comment as yourself",
|
|
5555
|
+
signOut: "Sign out",
|
|
5556
|
+
signedInAs: "Signed in as {name}",
|
|
5557
|
+
signedInGithub: "Signed in with GitHub",
|
|
5534
5558
|
cancel: "Cancel",
|
|
5535
5559
|
post: "Post",
|
|
5536
5560
|
posting: "Posting…",
|
|
@@ -5604,6 +5628,8 @@ const EN = {
|
|
|
5604
5628
|
copied: "Copied for your coding agent",
|
|
5605
5629
|
copyFailed: "Couldn't copy",
|
|
5606
5630
|
signedIn: "You're signed in as the owner",
|
|
5631
|
+
commenterSignedIn: "You're signed in",
|
|
5632
|
+
signedOut: "Signed out",
|
|
5607
5633
|
linkGone: "That comment was deleted or is on another page",
|
|
5608
5634
|
enterName: "Enter your name",
|
|
5609
5635
|
writeFirst: "Write a comment first",
|
|
@@ -5947,6 +5973,8 @@ const KEYS = {
|
|
|
5947
5973
|
name: "nuni:name",
|
|
5948
5974
|
secret: "nuni:author-secret",
|
|
5949
5975
|
session: (project) => `nuni:session:${project}`,
|
|
5976
|
+
/** A visitor's sign-in to comment under their account. */
|
|
5977
|
+
commenter: (project) => `nuni:commenter:${project}`,
|
|
5950
5978
|
/** Where this visitor dragged the toolbar. */
|
|
5951
5979
|
position: (project) => `nuni:position:${project}`,
|
|
5952
5980
|
/** The comment this visitor opened last, marked in the panel. */
|
|
@@ -6281,6 +6309,10 @@ button { cursor: pointer; }
|
|
|
6281
6309
|
justify-content: center;
|
|
6282
6310
|
flex: none;
|
|
6283
6311
|
}
|
|
6312
|
+
.avatar { position: relative; overflow: hidden; }
|
|
6313
|
+
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
|
|
6314
|
+
.signed-in { display: inline-flex; flex: none; color: var(--n-fg-muted); }
|
|
6315
|
+
.signed-in .icon { width: 13px; height: 13px; }
|
|
6284
6316
|
.author { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
6285
6317
|
.meta { color: var(--n-fg-muted); font-size: 12px; white-space: nowrap; }
|
|
6286
6318
|
.badge {
|
|
@@ -6509,6 +6541,10 @@ textarea.field { resize: vertical; min-height: 72px; max-height: 240px; }
|
|
|
6509
6541
|
white-space: nowrap;
|
|
6510
6542
|
}
|
|
6511
6543
|
.cmp-link:hover { color: var(--n-fg); }
|
|
6544
|
+
.cmp-head-guest { padding-right: 116px; }
|
|
6545
|
+
.cmp-sign-in { position: absolute; top: 13px; right: 44px; height: 32px; display: inline-flex; align-items: center; gap: 4px; }
|
|
6546
|
+
.cmp-sign-in .icon { width: 13px; height: 13px; }
|
|
6547
|
+
.signed-in-as { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
6512
6548
|
.composer .quote, .composer .suggest { margin: 6px 14px 0; }
|
|
6513
6549
|
.suggest {
|
|
6514
6550
|
display: grid;
|
|
@@ -6830,11 +6866,23 @@ function isTypingTarget(target) {
|
|
|
6830
6866
|
const el = target;
|
|
6831
6867
|
return el.isContentEditable || el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT";
|
|
6832
6868
|
}
|
|
6833
|
-
|
|
6834
|
-
|
|
6869
|
+
/** Initials, under the account photo of an author who signed in. */
|
|
6870
|
+
function avatar(name, photo) {
|
|
6871
|
+
const el = h("span", {
|
|
6835
6872
|
class: "avatar",
|
|
6836
6873
|
style: `background:${colorFor(name)}`
|
|
6837
6874
|
}, initials(name));
|
|
6875
|
+
if (photo?.startsWith("https://")) {
|
|
6876
|
+
const img = h("img", {
|
|
6877
|
+
src: photo,
|
|
6878
|
+
alt: "",
|
|
6879
|
+
loading: "lazy",
|
|
6880
|
+
referrerpolicy: "no-referrer",
|
|
6881
|
+
onerror: () => img.remove()
|
|
6882
|
+
});
|
|
6883
|
+
el.append(img);
|
|
6884
|
+
}
|
|
6885
|
+
return el;
|
|
6838
6886
|
}
|
|
6839
6887
|
function describeElement(el) {
|
|
6840
6888
|
const tag = el.tagName.toLowerCase();
|
|
@@ -6913,6 +6961,10 @@ var NuniWidget = class {
|
|
|
6913
6961
|
_defineProperty(this, "secretHash", "");
|
|
6914
6962
|
_defineProperty(this, "ownerToken", void 0);
|
|
6915
6963
|
_defineProperty(this, "ownerName", null);
|
|
6964
|
+
_defineProperty(this, "commenterToken", void 0);
|
|
6965
|
+
_defineProperty(this, "commenter", null);
|
|
6966
|
+
_defineProperty(this, "commenterUnsub", null);
|
|
6967
|
+
_defineProperty(this, "mineToken", null);
|
|
6916
6968
|
_defineProperty(this, "deepLinkId", null);
|
|
6917
6969
|
_defineProperty(this, "extraComment", null);
|
|
6918
6970
|
_defineProperty(this, "ownerDetail", null);
|
|
@@ -7023,6 +7075,7 @@ var NuniWidget = class {
|
|
|
7023
7075
|
}
|
|
7024
7076
|
this.secret = secret;
|
|
7025
7077
|
this.ownerToken = read(KEYS.session(config.project));
|
|
7078
|
+
this.commenterToken = read(KEYS.commenter(config.project));
|
|
7026
7079
|
this.lastViewedId = read(KEYS.lastViewed(config.project));
|
|
7027
7080
|
this.position = oneOf(read(KEYS.position(config.project)) ?? void 0, POSITIONS, config.position);
|
|
7028
7081
|
this.host = document.createElement("div");
|
|
@@ -7100,6 +7153,7 @@ var NuniWidget = class {
|
|
|
7100
7153
|
this.render();
|
|
7101
7154
|
}));
|
|
7102
7155
|
if (this.ownerToken) this.watchSession(this.ownerToken);
|
|
7156
|
+
if (this.commenterToken) this.watchCommenter(this.commenterToken);
|
|
7103
7157
|
this.readDeepLink();
|
|
7104
7158
|
this.subscribePage();
|
|
7105
7159
|
this.cleanups.push(onLocationChange(() => {
|
|
@@ -7172,6 +7226,7 @@ var NuniWidget = class {
|
|
|
7172
7226
|
this.endToolbarDrag?.();
|
|
7173
7227
|
this.pageUnsub?.();
|
|
7174
7228
|
this.sessionUnsub?.();
|
|
7229
|
+
this.commenterUnsub?.();
|
|
7175
7230
|
this.ownerDetailUnsub?.();
|
|
7176
7231
|
this.threadUnsub?.();
|
|
7177
7232
|
this.mineUnsub?.();
|
|
@@ -7228,6 +7283,62 @@ var NuniWidget = class {
|
|
|
7228
7283
|
this.render();
|
|
7229
7284
|
});
|
|
7230
7285
|
}
|
|
7286
|
+
watchCommenter(token) {
|
|
7287
|
+
this.commenterUnsub?.();
|
|
7288
|
+
this.commenterUnsub = this.api.onCommenter(token, (result) => {
|
|
7289
|
+
if (this.commenterToken !== token) return;
|
|
7290
|
+
if (result.valid) {
|
|
7291
|
+
const { name, avatarUrl, authorKey } = result;
|
|
7292
|
+
this.commenter = {
|
|
7293
|
+
name,
|
|
7294
|
+
avatarUrl,
|
|
7295
|
+
authorKey
|
|
7296
|
+
};
|
|
7297
|
+
} else this.forgetCommenter();
|
|
7298
|
+
this.render();
|
|
7299
|
+
});
|
|
7300
|
+
}
|
|
7301
|
+
/** Back to commenting as a guest in this browser. */
|
|
7302
|
+
forgetCommenter() {
|
|
7303
|
+
this.commenterUnsub?.();
|
|
7304
|
+
this.commenterUnsub = null;
|
|
7305
|
+
this.commenterToken = null;
|
|
7306
|
+
this.commenter = null;
|
|
7307
|
+
write(KEYS.commenter(this.config.project), null);
|
|
7308
|
+
this.syncMine();
|
|
7309
|
+
}
|
|
7310
|
+
/** Who is writing, for every comment, reply, reaction and upload. */
|
|
7311
|
+
get author() {
|
|
7312
|
+
return {
|
|
7313
|
+
authorSecret: this.secret,
|
|
7314
|
+
commenterToken: this.commenterToken ?? void 0
|
|
7315
|
+
};
|
|
7316
|
+
}
|
|
7317
|
+
/** The name new comments go out under. */
|
|
7318
|
+
get displayName() {
|
|
7319
|
+
return this.commenter?.name ?? this.name;
|
|
7320
|
+
}
|
|
7321
|
+
/** The key on what this visitor writes now. */
|
|
7322
|
+
get authorKey() {
|
|
7323
|
+
return this.commenter?.authorKey ?? this.secretHash;
|
|
7324
|
+
}
|
|
7325
|
+
/** Signed in or not, this browser's own guest comments stay editable. */
|
|
7326
|
+
ownsKey(key) {
|
|
7327
|
+
return Boolean(key) && (key === this.secretHash || key === this.commenter?.authorKey);
|
|
7328
|
+
}
|
|
7329
|
+
/** An author's photo and name, marked when they wrote it signed in. */
|
|
7330
|
+
authorLine(row) {
|
|
7331
|
+
return [
|
|
7332
|
+
avatar(row.authorName, row.signedIn ? row.avatarUrl : void 0),
|
|
7333
|
+
h("span", { class: "author" }, row.authorName),
|
|
7334
|
+
row.signedIn ? h("span", {
|
|
7335
|
+
class: "signed-in",
|
|
7336
|
+
role: "img",
|
|
7337
|
+
"aria-label": this.t("signedInGithub"),
|
|
7338
|
+
title: this.t("signedInGithub")
|
|
7339
|
+
}, icon(ICONS.github)) : null
|
|
7340
|
+
];
|
|
7341
|
+
}
|
|
7231
7342
|
/** Keep the owner-only details of the open thread subscribed. */
|
|
7232
7343
|
syncOwnerDetail() {
|
|
7233
7344
|
const token = this.ownerToken;
|
|
@@ -7265,7 +7376,7 @@ var NuniWidget = class {
|
|
|
7265
7376
|
return Boolean(this.ownerToken && this.ownerName);
|
|
7266
7377
|
}
|
|
7267
7378
|
isMine(comment) {
|
|
7268
|
-
return
|
|
7379
|
+
return this.ownsKey(comment.authorKeyHash);
|
|
7269
7380
|
}
|
|
7270
7381
|
visibleComments() {
|
|
7271
7382
|
return this.comments.filter((c) => c.status === (this.panelOpen && this.tab === "resolved" ? "resolved" : "open") || this.card?.kind === "thread" && this.card.id === c._id);
|
|
@@ -7692,7 +7803,7 @@ var NuniWidget = class {
|
|
|
7692
7803
|
async attachScreenshot(id, shot) {
|
|
7693
7804
|
try {
|
|
7694
7805
|
const image = await shot;
|
|
7695
|
-
if (image) await this.api.uploadScreenshot(id, this.
|
|
7806
|
+
if (image) await this.api.uploadScreenshot(id, this.author, image);
|
|
7696
7807
|
} catch {}
|
|
7697
7808
|
}
|
|
7698
7809
|
/** One after another, so they keep their order. */
|
|
@@ -7700,7 +7811,7 @@ var NuniWidget = class {
|
|
|
7700
7811
|
const images = draft.images ?? [];
|
|
7701
7812
|
revokeImages(draft);
|
|
7702
7813
|
try {
|
|
7703
|
-
for (const image of images) await this.api.uploadImage(id, this.
|
|
7814
|
+
for (const image of images) await this.api.uploadImage(id, this.author, image.blob);
|
|
7704
7815
|
} catch (error) {
|
|
7705
7816
|
this.showToast(error instanceof NuniApiError ? error.message : this.t("imageFailed"));
|
|
7706
7817
|
}
|
|
@@ -7983,6 +8094,29 @@ var NuniWidget = class {
|
|
|
7983
8094
|
url.searchParams.set("origin", location.origin);
|
|
7984
8095
|
if (!window.open(url.toString(), "nuni-claim", "popup,width=480,height=680")) location.href = url.toString();
|
|
7985
8096
|
}
|
|
8097
|
+
/** Sign in to comment under an account, in a popup on the dashboard. */
|
|
8098
|
+
openCommenterSignIn() {
|
|
8099
|
+
const url = new URL(`${this.config.appUrl}/dashboard/comment-sign-in`);
|
|
8100
|
+
url.searchParams.set("project", this.config.project);
|
|
8101
|
+
url.searchParams.set("origin", location.origin);
|
|
8102
|
+
if (!window.open(url.toString(), "nuni-comment-sign-in", "popup,width=480,height=680")) location.href = url.toString();
|
|
8103
|
+
}
|
|
8104
|
+
async onCommenterSignedIn(token) {
|
|
8105
|
+
this.commenterToken = token;
|
|
8106
|
+
write(KEYS.commenter(this.config.project), token);
|
|
8107
|
+
await this.api.linkBrowser(this.secret, token).catch(() => null);
|
|
8108
|
+
if (this.commenterToken !== token) return;
|
|
8109
|
+
this.watchCommenter(token);
|
|
8110
|
+
this.syncMine();
|
|
8111
|
+
this.showToast(this.t("commenterSignedIn"));
|
|
8112
|
+
}
|
|
8113
|
+
signOutCommenter() {
|
|
8114
|
+
const token = this.commenterToken;
|
|
8115
|
+
if (!token) return;
|
|
8116
|
+
this.forgetCommenter();
|
|
8117
|
+
this.api.signOutCommenter(token);
|
|
8118
|
+
this.showToast(this.t("signedOut"));
|
|
8119
|
+
}
|
|
7986
8120
|
onMessage(e) {
|
|
7987
8121
|
let appOrigin;
|
|
7988
8122
|
try {
|
|
@@ -7992,7 +8126,12 @@ var NuniWidget = class {
|
|
|
7992
8126
|
}
|
|
7993
8127
|
if (e.origin !== appOrigin) return;
|
|
7994
8128
|
const data = e.data;
|
|
7995
|
-
if (!data || data.
|
|
8129
|
+
if (!data || data.project !== this.config.project) return;
|
|
8130
|
+
if (data.type === "nuni:commenter" && typeof data.token === "string" && data.token.startsWith("nuni_c_")) {
|
|
8131
|
+
this.onCommenterSignedIn(data.token);
|
|
8132
|
+
return;
|
|
8133
|
+
}
|
|
8134
|
+
if (data.type !== "nuni:session") return;
|
|
7996
8135
|
if (typeof data.token !== "string" || !data.token.startsWith("nuni_s_")) return;
|
|
7997
8136
|
this.ownerToken = data.token;
|
|
7998
8137
|
write(KEYS.session(this.config.project), data.token);
|
|
@@ -8040,7 +8179,8 @@ var NuniWidget = class {
|
|
|
8040
8179
|
setTimeout(() => this.scheduleLayout(), 400);
|
|
8041
8180
|
}
|
|
8042
8181
|
async submitDraft(draft, body, name) {
|
|
8043
|
-
const
|
|
8182
|
+
const signedIn = Boolean(this.commenter);
|
|
8183
|
+
const cleanName = (this.commenter?.name ?? name).trim().slice(0, LIMITS.nameMaxLength);
|
|
8044
8184
|
const cleanBody = body.trim();
|
|
8045
8185
|
if (!cleanName) return this.setDraftError(draft, this.t("enterName"));
|
|
8046
8186
|
const before = draft.suggesting ? draft.suggestFrom : null;
|
|
@@ -8053,8 +8193,10 @@ var NuniWidget = class {
|
|
|
8053
8193
|
} : void 0;
|
|
8054
8194
|
if (!cleanBody && !suggestion) return this.setDraftError(draft, this.t("writeFirst"));
|
|
8055
8195
|
if (cleanBody.length > LIMITS.bodyMaxLength) return this.setDraftError(draft, this.t("commentTooLong", { count: LIMITS.bodyMaxLength }));
|
|
8056
|
-
|
|
8057
|
-
|
|
8196
|
+
if (!signedIn) {
|
|
8197
|
+
this.name = cleanName;
|
|
8198
|
+
write(KEYS.name, cleanName);
|
|
8199
|
+
}
|
|
8058
8200
|
draft.sending = true;
|
|
8059
8201
|
draft.error = void 0;
|
|
8060
8202
|
this.render();
|
|
@@ -8065,7 +8207,7 @@ var NuniWidget = class {
|
|
|
8065
8207
|
body: cleanBody,
|
|
8066
8208
|
suggestion,
|
|
8067
8209
|
authorName: cleanName,
|
|
8068
|
-
|
|
8210
|
+
...this.author,
|
|
8069
8211
|
page: {
|
|
8070
8212
|
...loc,
|
|
8071
8213
|
path: this.pageKey,
|
|
@@ -8174,7 +8316,7 @@ var NuniWidget = class {
|
|
|
8174
8316
|
async deleteComment(c) {
|
|
8175
8317
|
if (!await this.ask(this.t("confirmDeleteComment"), this.t("delete"))) return;
|
|
8176
8318
|
const token = this.ownerToken;
|
|
8177
|
-
const action = this.isMine(c) ? () => this.api.deleteOwn(c._id, this.
|
|
8319
|
+
const action = this.isMine(c) ? () => this.api.deleteOwn(c._id, this.author) : token ? () => this.api.remove(c._id, token) : null;
|
|
8178
8320
|
if (!action) return;
|
|
8179
8321
|
this.act(c._id, action, this.t("deleted")).then((ok) => {
|
|
8180
8322
|
if (!ok) return;
|
|
@@ -8185,12 +8327,12 @@ var NuniWidget = class {
|
|
|
8185
8327
|
});
|
|
8186
8328
|
}
|
|
8187
8329
|
isMyReply(reply) {
|
|
8188
|
-
return
|
|
8330
|
+
return this.ownsKey(reply.authorKeyHash);
|
|
8189
8331
|
}
|
|
8190
8332
|
async sendReply(comment, body, name) {
|
|
8191
8333
|
const card = this.card;
|
|
8192
8334
|
if (card?.kind !== "thread" || card.id !== comment._id) return;
|
|
8193
|
-
const authorName = (this.isOwner ? this.ownerName : name)?.trim().slice(0, LIMITS.nameMaxLength);
|
|
8335
|
+
const authorName = (this.isOwner ? this.ownerName : this.commenter?.name ?? name)?.trim().slice(0, LIMITS.nameMaxLength);
|
|
8194
8336
|
const clean = body.trim();
|
|
8195
8337
|
if (!clean) return;
|
|
8196
8338
|
if (!authorName) {
|
|
@@ -8203,7 +8345,7 @@ var NuniWidget = class {
|
|
|
8203
8345
|
this.render();
|
|
8204
8346
|
return;
|
|
8205
8347
|
}
|
|
8206
|
-
if (!this.isOwner) {
|
|
8348
|
+
if (!this.isOwner && !this.commenter) {
|
|
8207
8349
|
this.name = authorName;
|
|
8208
8350
|
write(KEYS.name, authorName);
|
|
8209
8351
|
}
|
|
@@ -8215,7 +8357,7 @@ var NuniWidget = class {
|
|
|
8215
8357
|
commentId: comment._id,
|
|
8216
8358
|
body: clean,
|
|
8217
8359
|
authorName,
|
|
8218
|
-
|
|
8360
|
+
...this.author,
|
|
8219
8361
|
sessionToken: this.ownerToken ?? void 0
|
|
8220
8362
|
});
|
|
8221
8363
|
const box = this.uiLayer.querySelector("[data-focus-key=\"reply\"]");
|
|
@@ -8227,7 +8369,7 @@ var NuniWidget = class {
|
|
|
8227
8369
|
this.render();
|
|
8228
8370
|
}
|
|
8229
8371
|
toggleReaction(comment, targetId, emoji) {
|
|
8230
|
-
this.api.toggleReaction(comment._id, targetId, emoji, this.
|
|
8372
|
+
this.api.toggleReaction(comment._id, targetId, emoji, this.author).catch((error) => {
|
|
8231
8373
|
if (this.card?.kind === "thread" && this.card.id === comment._id) {
|
|
8232
8374
|
this.card.error = error instanceof Error ? error.message : this.t("reactFailed");
|
|
8233
8375
|
this.render();
|
|
@@ -8237,11 +8379,11 @@ var NuniWidget = class {
|
|
|
8237
8379
|
async deleteReply(comment, reply) {
|
|
8238
8380
|
if (!await this.ask(this.t("confirmDeleteReply"), this.t("delete"))) return;
|
|
8239
8381
|
const token = this.ownerToken;
|
|
8240
|
-
const action = this.isMyReply(reply) ? () => this.api.deleteReply(reply._id, this.
|
|
8382
|
+
const action = this.isMyReply(reply) ? () => this.api.deleteReply(reply._id, this.author) : token ? () => this.api.removeReply(reply._id, token) : null;
|
|
8241
8383
|
if (action) this.act(comment._id, action, this.t("replyDeleted"));
|
|
8242
8384
|
}
|
|
8243
8385
|
saveReplyEdit(comment, reply, body) {
|
|
8244
|
-
this.act(comment._id, () => this.api.editReply(reply._id, this.
|
|
8386
|
+
this.act(comment._id, () => this.api.editReply(reply._id, this.author, body), this.t("saved")).then((ok) => {
|
|
8245
8387
|
if (ok && this.card?.kind === "thread") {
|
|
8246
8388
|
this.card.replyEditing = void 0;
|
|
8247
8389
|
this.render();
|
|
@@ -8249,7 +8391,7 @@ var NuniWidget = class {
|
|
|
8249
8391
|
});
|
|
8250
8392
|
}
|
|
8251
8393
|
saveEdit(c, body, suggestion) {
|
|
8252
|
-
this.act(c._id, () => this.api.editOwn(c._id, this.
|
|
8394
|
+
this.act(c._id, () => this.api.editOwn(c._id, this.author, body, suggestion), this.t("saved"));
|
|
8253
8395
|
}
|
|
8254
8396
|
/** For the owner: the suggested words, ready to paste into the code. */
|
|
8255
8397
|
copyNewTextButton(text) {
|
|
@@ -8268,13 +8410,22 @@ var NuniWidget = class {
|
|
|
8268
8410
|
}, 2400);
|
|
8269
8411
|
this.render();
|
|
8270
8412
|
}
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
this.panelOpen
|
|
8274
|
-
if (
|
|
8413
|
+
/** Keep Yours subscribed with the current sign-in, once the panel opened. */
|
|
8414
|
+
syncMine() {
|
|
8415
|
+
if (!this.mineUnsub && !this.panelOpen) return;
|
|
8416
|
+
if (this.mineUnsub && this.mineToken === this.commenterToken) return;
|
|
8417
|
+
this.mineUnsub?.();
|
|
8418
|
+
this.mine = null;
|
|
8419
|
+
this.mineToken = this.commenterToken;
|
|
8420
|
+
this.mineUnsub = this.api.onMine(this.author, (mine) => {
|
|
8275
8421
|
this.mine = mine;
|
|
8276
8422
|
this.render();
|
|
8277
8423
|
});
|
|
8424
|
+
}
|
|
8425
|
+
togglePanel(open = !this.panelOpen) {
|
|
8426
|
+
if (open === this.panelOpen) return;
|
|
8427
|
+
this.panelOpen = open;
|
|
8428
|
+
if (open) this.syncMine();
|
|
8278
8429
|
if (open) {
|
|
8279
8430
|
this.keepFocus();
|
|
8280
8431
|
this.focusNext = ".panel [role=\"tab\"][aria-selected=\"true\"]";
|
|
@@ -8370,8 +8521,8 @@ var NuniWidget = class {
|
|
|
8370
8521
|
const pin = h("div", {
|
|
8371
8522
|
class: "pin pin-draft pin-pending",
|
|
8372
8523
|
"aria-hidden": "true"
|
|
8373
|
-
}, this.
|
|
8374
|
-
pin.style.background = this.
|
|
8524
|
+
}, this.displayName ? initials(this.displayName) : "+");
|
|
8525
|
+
pin.style.background = this.displayName ? colorFor(this.displayName) : "var(--n-accent)";
|
|
8375
8526
|
pin.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;
|
|
8376
8527
|
this.pinLayer.append(pin);
|
|
8377
8528
|
}
|
|
@@ -8543,10 +8694,7 @@ var NuniWidget = class {
|
|
|
8543
8694
|
"aria-current": c._id === this.lastViewedId ? "true" : null,
|
|
8544
8695
|
"data-focus-key": `item-${c._id}`,
|
|
8545
8696
|
onclick: () => this.focusComment(c._id)
|
|
8546
|
-
}, h("div", { class: "row" }, h("span", {
|
|
8547
|
-
class: "avatar",
|
|
8548
|
-
style: `background:${colorFor(c.authorName)}`
|
|
8549
|
-
}, initials(c.authorName)), h("span", { class: "author" }, c.authorName), h("span", { class: "meta" }, this.i18n.timeAgo(c.createdAt) + (c.replyCount ? ` · ${this.t("replyCount", { count: c.replyCount })}` : "")), c.page.origin !== location.origin ? h("span", {
|
|
8697
|
+
}, h("div", { class: "row" }, ...this.authorLine(c), h("span", { class: "meta" }, this.i18n.timeAgo(c.createdAt) + (c.replyCount ? ` · ${this.t("replyCount", { count: c.replyCount })}` : "")), c.page.origin !== location.origin ? h("span", {
|
|
8550
8698
|
class: "badge",
|
|
8551
8699
|
title: c.page.origin
|
|
8552
8700
|
}, new URL(c.page.origin).host) : null, c.resolution === "outdated" ? h("span", { class: "badge" }, this.t("outdated")) : null), ...content(c));
|
|
@@ -8633,17 +8781,25 @@ var NuniWidget = class {
|
|
|
8633
8781
|
id: "nuni-panel-list",
|
|
8634
8782
|
role: "tabpanel",
|
|
8635
8783
|
"aria-labelledby": `nuni-tab-${this.tab}`
|
|
8636
|
-
}, ...children), this.
|
|
8784
|
+
}, ...children), this.renderPanelFooter());
|
|
8637
8785
|
}
|
|
8638
8786
|
renderPanelFooter() {
|
|
8639
|
-
|
|
8787
|
+
const claimable = this.status && !this.status.claimed && !this.isOwner;
|
|
8788
|
+
const commenter = this.commenter;
|
|
8789
|
+
if (!claimable && !commenter) return null;
|
|
8790
|
+
return h("div", { class: "panel-foot" }, commenter ? h("div", { class: "row" }, avatar(commenter.name, commenter.avatarUrl), h("span", { class: "meta signed-in-as" }, this.t("signedInAs", { name: commenter.name })), h("span", { class: "spacer" }), h("button", {
|
|
8791
|
+
class: "cmp-link",
|
|
8792
|
+
type: "button",
|
|
8793
|
+
onclick: () => this.signOutCommenter()
|
|
8794
|
+
}, this.t("signOut"))) : null, claimable ? h("div", { class: "row" }, h("span", { class: "meta" }, this.t("ownSite")), h("span", { class: "spacer" }), h("button", {
|
|
8640
8795
|
class: "btn",
|
|
8641
8796
|
type: "button",
|
|
8642
8797
|
onclick: () => this.openClaim()
|
|
8643
|
-
}, icon(ICONS.github), this.t("claim"))));
|
|
8798
|
+
}, icon(ICONS.github), this.t("claim"))) : null);
|
|
8644
8799
|
}
|
|
8645
8800
|
renderComposer(draft, cls) {
|
|
8646
|
-
const
|
|
8801
|
+
const commenter = this.commenter;
|
|
8802
|
+
const needsName = !commenter && !this.name;
|
|
8647
8803
|
const avatar = h("span", {
|
|
8648
8804
|
class: "avatar cmp-avatar",
|
|
8649
8805
|
"aria-hidden": "true"
|
|
@@ -8769,7 +8925,15 @@ var NuniWidget = class {
|
|
|
8769
8925
|
e.preventDefault();
|
|
8770
8926
|
submit();
|
|
8771
8927
|
}
|
|
8772
|
-
}, h("div", { class: "cmp-head" },
|
|
8928
|
+
}, commenter ? h("div", { class: "cmp-head" }, ...this.authorLine({
|
|
8929
|
+
authorName: commenter.name,
|
|
8930
|
+
avatarUrl: commenter.avatarUrl,
|
|
8931
|
+
signedIn: true
|
|
8932
|
+
}), h("span", { class: "spacer" }), h("button", {
|
|
8933
|
+
class: "cmp-link",
|
|
8934
|
+
type: "button",
|
|
8935
|
+
onclick: () => this.signOutCommenter()
|
|
8936
|
+
}, this.t("signOut"))) : h("div", { class: "cmp-head cmp-head-guest" }, avatar, needsName ? nameInput : h("span", { class: "author" }, this.name), h("span", { class: "spacer" }), needsName ? null : h("button", {
|
|
8773
8937
|
class: "cmp-link",
|
|
8774
8938
|
type: "button",
|
|
8775
8939
|
onclick: () => {
|
|
@@ -8818,7 +8982,12 @@ var NuniWidget = class {
|
|
|
8818
8982
|
title: this.t(draft.preparingMarkup ? "preparingScreenshot" : "markUpScreenshot"),
|
|
8819
8983
|
disabled: Boolean(draft.preparingMarkup),
|
|
8820
8984
|
onclick: () => void this.markUpScreenshot(draft)
|
|
8821
|
-
}, icon(ICONS.pen)) : null, fileInput, counter, h("span", { class: "spacer" }), post), h("button", {
|
|
8985
|
+
}, icon(ICONS.pen)) : null, fileInput, counter, h("span", { class: "spacer" }), post), commenter ? null : h("button", {
|
|
8986
|
+
class: "cmp-link cmp-sign-in",
|
|
8987
|
+
type: "button",
|
|
8988
|
+
title: this.t("signInGithub"),
|
|
8989
|
+
onclick: () => this.openCommenterSignIn()
|
|
8990
|
+
}, icon(ICONS.github), this.t("signIn")), h("button", {
|
|
8822
8991
|
class: "icon-btn cmp-close",
|
|
8823
8992
|
type: "button",
|
|
8824
8993
|
"aria-label": this.t("cancel"),
|
|
@@ -8889,7 +9058,7 @@ var NuniWidget = class {
|
|
|
8889
9058
|
].filter((badge) => badge !== null);
|
|
8890
9059
|
const contextPending = owner && !(this.ownerDetailLoaded && this.ownerDetailId === comment._id);
|
|
8891
9060
|
const resolved = comment.status === "resolved";
|
|
8892
|
-
body = h("div", { class: "card-body msg" }, h("div", { class: "card-head" },
|
|
9061
|
+
body = h("div", { class: "card-body msg" }, h("div", { class: "card-head" }, ...this.authorLine(comment), h("span", { class: "meta" }, this.i18n.timeAgo(comment.createdAt) + (comment.editedAt ? ` · ${this.t("edited")}` : "")), h("span", { class: "spacer" }), owner ? h("button", {
|
|
8893
9062
|
class: `icon-btn${resolved ? " is-resolved" : ""}`,
|
|
8894
9063
|
type: "button",
|
|
8895
9064
|
"aria-label": this.t(resolved ? "reopen" : "resolve"),
|
|
@@ -8982,7 +9151,7 @@ var NuniWidget = class {
|
|
|
8982
9151
|
const open = card.picker === targetId;
|
|
8983
9152
|
if (!withAdd && !summaries.length && !open) return null;
|
|
8984
9153
|
return h("div", { class: "reactions" }, ...summaries.map((r) => {
|
|
8985
|
-
const mine = r.authorKeyHashes.includes(this.
|
|
9154
|
+
const mine = r.authorKeyHashes.includes(this.authorKey);
|
|
8986
9155
|
return h("button", {
|
|
8987
9156
|
class: "reaction",
|
|
8988
9157
|
type: "button",
|
|
@@ -9055,7 +9224,7 @@ var NuniWidget = class {
|
|
|
9055
9224
|
return h("div", {
|
|
9056
9225
|
class: "reply msg",
|
|
9057
9226
|
"data-reply": reply._id
|
|
9058
|
-
}, h("div", { class: "card-head" },
|
|
9227
|
+
}, h("div", { class: "card-head" }, ...this.authorLine(reply), reply.isOwner ? h("span", { class: "badge badge-owner" }, this.t("owner")) : null, h("span", { class: "meta" }, this.i18n.timeAgo(reply.createdAt) + (reply.editedAt ? ` · ${this.t("edited")}` : "")), h("span", { class: "spacer" }), h("span", { class: "msg-tools" }, this.reactionAdd(reply._id, "icon-btn"), mine ? h("button", {
|
|
9059
9228
|
class: "icon-btn",
|
|
9060
9229
|
type: "button",
|
|
9061
9230
|
"aria-label": this.t("editReply"),
|
|
@@ -9074,7 +9243,7 @@ var NuniWidget = class {
|
|
|
9074
9243
|
onclick: () => void this.deleteReply(comment, reply)
|
|
9075
9244
|
}, icon(ICONS.trash)) : null)), h("div", { class: "msg-main" }, h("div", { class: "comment-body" }, reply.body), this.renderReactions(comment, reply._id, false)));
|
|
9076
9245
|
});
|
|
9077
|
-
const needsName = !owner && !this.
|
|
9246
|
+
const needsName = !owner && !this.displayName;
|
|
9078
9247
|
const nameInput = h("input", {
|
|
9079
9248
|
class: "reply-name",
|
|
9080
9249
|
placeholder: this.t("yourName"),
|
|
@@ -9167,7 +9336,7 @@ var NuniWidget = class {
|
|
|
9167
9336
|
}
|
|
9168
9337
|
focusComposer() {
|
|
9169
9338
|
requestAnimationFrame(() => {
|
|
9170
|
-
this.uiLayer.querySelector(this.
|
|
9339
|
+
this.uiLayer.querySelector(this.displayName ? "[data-focus-key=\"body\"]" : "[data-focus-key=\"name\"]")?.focus();
|
|
9171
9340
|
});
|
|
9172
9341
|
}
|
|
9173
9342
|
};
|