@nuniapp/widget 0.1.11 → 0.1.12

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 CHANGED
@@ -78,6 +78,8 @@ Using React? Use [`@nuniapp/react`](https://www.npmjs.com/package/@nuniapp/react
78
78
 
79
79
  - Press **C** (or the Comment button), click any element and write your comment. Select text to comment on the words, or drag a box to comment on an area.
80
80
  - Everything works from the keyboard: while adding, the arrow keys move between elements and Enter comments.
81
+ - Press **Shift+C** (Shift and the `hotkey`; or the eye button) to hide the pins and see the page clean. Adding a comment or opening one shows them again.
82
+ - The comments list shows this page first, then every other page's comments grouped under its path. Search looks through all of them, and a comment on another page opens there.
81
83
  - Pins follow their element through layout changes, responsive breakpoints and content edits. If an element is removed, its comment is listed as "Couldn't find on this page" instead of pinned to the wrong thing.
82
84
  - Your name is remembered in this browser. You can edit and delete your own comments.
83
85
  - The site owner claims the project from the widget ("Claim Nuni", GitHub sign-in) and can then resolve, reopen and delete comments.
@@ -51,6 +51,9 @@ const LIMITS = {
51
51
  /** Per page, the widget loads this many newest open and resolved comments. */
52
52
  pageOpenLimit: 1e3,
53
53
  pageResolvedLimit: 200,
54
+ /** The widget's panel lists this many newest comments from every page. */
55
+ siteOpenLimit: 300,
56
+ siteResolvedLimit: 100,
54
57
  /** The newest of a commenter's own comments, across pages. */
55
58
  yoursLimit: 100,
56
59
  /** Most console or network entries kept with one comment. */
package/dist/index.d.ts CHANGED
@@ -15,6 +15,8 @@ declare const EN: {
15
15
  pickElement: string;
16
16
  allComments: string;
17
17
  openCount: string;
18
+ hideComments: string;
19
+ hideCommentsKey: string;
18
20
  commentSelection: string;
19
21
  commentSelectionKey: string;
20
22
  pickHint: string;
@@ -130,6 +132,9 @@ declare const EN: {
130
132
  commenterSignedIn: string;
131
133
  signedOut: string;
132
134
  linkGone: string;
135
+ commentsHidden: string;
136
+ commentsHiddenKey: string;
137
+ commentsShown: string;
133
138
  enterName: string;
134
139
  writeFirst: string;
135
140
  changeTextFirst: string;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as isProjectId, a as initials, c as IMAGE_TYPES, d as CAPTURE_MARK, f as MASK_ATTRIBUTE, g as generateSecret, h as REACTIONS, i as icon, m as LIMITS, n as copyText, o as parseColor, p as MASK_SELECTOR, r as h, s as readableOn, t as colorFor, u as imageFromFile } from "./dom-BWcT-q7S.js";
1
+ import { _ as isProjectId, a as initials, c as IMAGE_TYPES, d as CAPTURE_MARK, f as MASK_ATTRIBUTE, g as generateSecret, h as REACTIONS, i as icon, m as LIMITS, n as copyText, o as parseColor, p as MASK_SELECTOR, r as h, s as readableOn, t as colorFor, u as imageFromFile } from "./dom-YEIbiA4X.js";
2
2
  //#region ../shared/src/path.ts
3
3
  /**
4
4
  * Normalize a URL to the key comments are matched by. Query and hash are
@@ -176,7 +176,7 @@ function buildCommentPrompt(comment, options = {}) {
176
176
  }
177
177
  //#endregion
178
178
  //#region src/config.ts
179
- const VERSION = "0.1.11";
179
+ const VERSION = "0.1.12";
180
180
  function resolveConfig(options) {
181
181
  return {
182
182
  project: options.project,
@@ -5291,6 +5291,10 @@ var NuniApi = class {
5291
5291
  ...author
5292
5292
  }, (comments) => cb(comments), () => {});
5293
5293
  }
5294
+ /** Comments on every page, newest first, live. */
5295
+ onSite(cb) {
5296
+ return this.client.onUpdate(api.comments.listForSite, { publicId: this.config.project }, (comments) => cb(comments), () => {});
5297
+ }
5294
5298
  onPages(cb) {
5295
5299
  return this.client.onUpdate(api.comments.pagesWithComments, { publicId: this.config.project }, cb, () => {});
5296
5300
  }
@@ -5516,6 +5520,8 @@ const EN = {
5516
5520
  pickElement: "Pick an element",
5517
5521
  allComments: "All comments",
5518
5522
  openCount: "Comments on this page: {count} open",
5523
+ hideComments: "Hide comments",
5524
+ hideCommentsKey: "Hide comments (Shift+{key})",
5519
5525
  commentSelection: "Comment on the selected text",
5520
5526
  commentSelectionKey: "Comment on the selected text ({key})",
5521
5527
  pickHint: "Click anything to comment, or drag to mark an area",
@@ -5631,6 +5637,9 @@ const EN = {
5631
5637
  commenterSignedIn: "You're signed in",
5632
5638
  signedOut: "Signed out",
5633
5639
  linkGone: "That comment was deleted or is on another page",
5640
+ commentsHidden: "Comments hidden",
5641
+ commentsHiddenKey: "Comments hidden. Press Shift+{key} to show them",
5642
+ commentsShown: "Comments shown",
5634
5643
  enterName: "Enter your name",
5635
5644
  writeFirst: "Write a comment first",
5636
5645
  changeTextFirst: "Change the text to suggest an edit",
@@ -5704,6 +5713,8 @@ const s = (d) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" wi
5704
5713
  const ICONS = {
5705
5714
  comment: s("<path d=\"M21 12a8 8 0 0 1-11.6 7.1L4 20l1-4.6A8 8 0 1 1 21 12z\"/>"),
5706
5715
  list: s("<path d=\"M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01\"/>"),
5716
+ eye: s("<path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>"),
5717
+ eyeOff: s("<path d=\"M10.7 5.1A10 10 0 0 1 12 5c6.5 0 10 7 10 7a17 17 0 0 1-2.2 3.2M6.6 6.6A17 17 0 0 0 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.4-1.6M9.9 9.9a3 3 0 0 0 4.2 4.2M2 2l20 20\"/>"),
5707
5718
  close: s("<path d=\"M18 6 6 18M6 6l12 12\"/>"),
5708
5719
  check: s("<path d=\"M20 6 9 17l-5-5\"/>"),
5709
5720
  undo: s("<path d=\"M3 7v6h6\"/><path d=\"M21 17a9 9 0 0 0-15-6.7L3 13\"/>"),
@@ -5978,7 +5989,9 @@ const KEYS = {
5978
5989
  /** Where this visitor dragged the toolbar. */
5979
5990
  position: (project) => `nuni:position:${project}`,
5980
5991
  /** The comment this visitor opened last, marked in the panel. */
5981
- lastViewed: (project) => `nuni:last-viewed:${project}`
5992
+ lastViewed: (project) => `nuni:last-viewed:${project}`,
5993
+ /** This visitor hid the pins (Shift+C). */
5994
+ hidden: (project) => `nuni:hidden:${project}`
5982
5995
  };
5983
5996
  //#endregion
5984
5997
  //#region src/styles.ts
@@ -6094,6 +6107,9 @@ button { cursor: pointer; }
6094
6107
  justify-content: center;
6095
6108
  }
6096
6109
  .tb-btn[aria-pressed="true"] .tb-count { background: rgba(255,255,255,0.25); color: inherit; }
6110
+ .tb-icon { width: 36px; padding: 0; justify-content: center; }
6111
+ /* Pins hidden: the count stays, dimmed, so the comments aren't lost. */
6112
+ .toolbar[data-pins-hidden] .tb-count { opacity: 0.55; }
6097
6113
 
6098
6114
  /* Picking */
6099
6115
  .pick-hint {
@@ -6727,6 +6743,8 @@ textarea.field { resize: vertical; min-height: 72px; max-height: 240px; }
6727
6743
  .panel-search .field { padding-left: 32px; }
6728
6744
  .item-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word; }
6729
6745
  .section-label { padding: 12px 10px 4px; font-size: 11px; font-weight: 650; letter-spacing: 0.04em; text-transform: uppercase; color: var(--n-fg-muted); }
6746
+ /* A page's path, as written: paths are case-sensitive. */
6747
+ .section-path { text-transform: none; letter-spacing: 0; font-size: 12px; overflow-wrap: anywhere; }
6730
6748
  .empty { padding: 32px 16px; text-align: center; color: var(--n-fg-muted); }
6731
6749
  .panel-foot { padding: 12px; border-top: 1px solid var(--n-border); display: grid; gap: 8px; font-size: 13px; }
6732
6750
  .link { border: 0; background: none; padding: 0; color: var(--n-accent); font-weight: 600; text-decoration: none; }
@@ -6942,10 +6960,13 @@ var NuniWidget = class {
6942
6960
  _defineProperty(this, "status", null);
6943
6961
  _defineProperty(this, "pages", []);
6944
6962
  _defineProperty(this, "picking", false);
6963
+ _defineProperty(this, "pinsHidden", void 0);
6945
6964
  _defineProperty(this, "panelOpen", false);
6946
6965
  _defineProperty(this, "tab", "open");
6947
6966
  _defineProperty(this, "mine", null);
6948
6967
  _defineProperty(this, "mineUnsub", null);
6968
+ _defineProperty(this, "site", null);
6969
+ _defineProperty(this, "siteUnsub", null);
6949
6970
  _defineProperty(this, "repinId", null);
6950
6971
  _defineProperty(this, "pinReported", /* @__PURE__ */ new Map());
6951
6972
  _defineProperty(this, "pinReportTimer", 0);
@@ -7077,6 +7098,7 @@ var NuniWidget = class {
7077
7098
  this.ownerToken = read(KEYS.session(config.project));
7078
7099
  this.commenterToken = read(KEYS.commenter(config.project));
7079
7100
  this.lastViewedId = read(KEYS.lastViewed(config.project));
7101
+ this.pinsHidden = read(KEYS.hidden(config.project)) === "1";
7080
7102
  this.position = oneOf(read(KEYS.position(config.project)) ?? void 0, POSITIONS, config.position);
7081
7103
  this.host = document.createElement("div");
7082
7104
  this.host.id = "nuni-root";
@@ -7230,6 +7252,7 @@ var NuniWidget = class {
7230
7252
  this.ownerDetailUnsub?.();
7231
7253
  this.threadUnsub?.();
7232
7254
  this.mineUnsub?.();
7255
+ this.siteUnsub?.();
7233
7256
  clearTimeout(this.pinReportTimer);
7234
7257
  this.resolveRun++;
7235
7258
  for (const stop of this.scopeWatchers.values()) stop();
@@ -7378,8 +7401,17 @@ var NuniWidget = class {
7378
7401
  isMine(comment) {
7379
7402
  return this.ownsKey(comment.authorKeyHash);
7380
7403
  }
7404
+ /** The open thread keeps its pin while the rest are hidden. */
7381
7405
  visibleComments() {
7382
- return this.comments.filter((c) => c.status === (this.panelOpen && this.tab === "resolved" ? "resolved" : "open") || this.card?.kind === "thread" && this.card.id === c._id);
7406
+ return this.comments.filter((c) => !this.pinsHidden && c.status === (this.panelOpen && this.tab === "resolved" ? "resolved" : "open") || this.card?.kind === "thread" && this.card.id === c._id);
7407
+ }
7408
+ /** Hide or show the pins, like Figma's Shift+C. */
7409
+ setPinsHidden(hidden, quiet = false) {
7410
+ if (this.pinsHidden === hidden) return;
7411
+ this.pinsHidden = hidden;
7412
+ write(KEYS.hidden(this.config.project), hidden ? "1" : null);
7413
+ if (quiet) return;
7414
+ this.showToast(hidden ? this.withKey("commentsHidden", "commentsHiddenKey") : this.t("commentsShown"));
7383
7415
  }
7384
7416
  scheduleResolve() {
7385
7417
  clearTimeout(this.resolveTimer);
@@ -7626,6 +7658,7 @@ var NuniWidget = class {
7626
7658
  this.dragBox = null;
7627
7659
  if (on) {
7628
7660
  this.card = null;
7661
+ this.setPinsHidden(false, true);
7629
7662
  if (window.matchMedia(MOBILE_QUERY).matches) this.panelOpen = false;
7630
7663
  this.pickDocs = [document, ...frameDocuments(document)];
7631
7664
  for (const doc of this.pickDocs) {
@@ -7746,6 +7779,7 @@ var NuniWidget = class {
7746
7779
  this.movePin(repinId, draft);
7747
7780
  return;
7748
7781
  }
7782
+ this.setPinsHidden(false, true);
7749
7783
  if (this.config.capture.dom) try {
7750
7784
  draft.dom = domContext(element);
7751
7785
  } catch {}
@@ -7938,7 +7972,8 @@ var NuniWidget = class {
7938
7972
  const hotkey = this.config.hotkey;
7939
7973
  if (hotkey && e.key.toLowerCase() === hotkey) {
7940
7974
  e.preventDefault();
7941
- if (!this.picking && this.pageSelection()) this.commentOnSelection();
7975
+ if (e.shiftKey && hotkey !== hotkey.toUpperCase()) this.setPinsHidden(!this.pinsHidden);
7976
+ else if (!this.picking && this.pageSelection()) this.commentOnSelection();
7942
7977
  else this.setPicking(!this.picking);
7943
7978
  }
7944
7979
  }
@@ -8426,6 +8461,10 @@ var NuniWidget = class {
8426
8461
  if (open === this.panelOpen) return;
8427
8462
  this.panelOpen = open;
8428
8463
  if (open) this.syncMine();
8464
+ if (open && !this.siteUnsub) this.siteUnsub = this.api.onSite((site) => {
8465
+ this.site = site;
8466
+ this.render();
8467
+ });
8429
8468
  if (open) {
8430
8469
  this.keepFocus();
8431
8470
  this.focusNext = ".panel [role=\"tab\"][aria-selected=\"true\"]";
@@ -8565,6 +8604,7 @@ var NuniWidget = class {
8565
8604
  role: "toolbar",
8566
8605
  "aria-label": this.t("toolbar"),
8567
8606
  "data-dragging": !!drag,
8607
+ "data-pins-hidden": this.pinsHidden,
8568
8608
  style: drag && `transform: translate(${drag.dx}px, ${drag.dy}px)`,
8569
8609
  onpointerdown: (e) => this.startToolbarDrag(e)
8570
8610
  }, h("button", {
@@ -8583,7 +8623,15 @@ var NuniWidget = class {
8583
8623
  "aria-label": this.t("openCount", { count: open }),
8584
8624
  title: this.t("allComments"),
8585
8625
  onclick: () => this.togglePanel()
8586
- }, icon(ICONS.list), h("span", { class: "tb-count" }, String(open))));
8626
+ }, icon(ICONS.list), h("span", { class: "tb-count" }, String(open))), h("button", {
8627
+ class: "tb-btn tb-icon",
8628
+ type: "button",
8629
+ "aria-pressed": String(this.pinsHidden),
8630
+ "data-focus-key": "tb-hide",
8631
+ "aria-label": this.t("hideComments"),
8632
+ title: this.withKey("hideComments", "hideCommentsKey"),
8633
+ onclick: () => this.setPinsHidden(!this.pinsHidden)
8634
+ }, icon(this.pinsHidden ? ICONS.eyeOff : ICONS.eye)));
8587
8635
  }
8588
8636
  /**
8589
8637
  * Drag the toolbar anywhere; on release it glides to the nearest corner or
@@ -8666,10 +8714,18 @@ var NuniWidget = class {
8666
8714
  const query = this.search.trim().toLocaleLowerCase();
8667
8715
  const matches = (...texts) => !query || texts.some((t) => t?.toLocaleLowerCase().includes(query));
8668
8716
  const mine = this.mine ?? [];
8669
- const list = (this.tab === "open" ? open : this.tab === "resolved" ? resolved : mine).filter((c) => matches(c.body, c.authorName, c.anchor.quote?.exact, c.suggestion?.after, this.tab === "mine" ? c.page.path : void 0));
8717
+ const list = (this.tab === "open" ? open : this.tab === "resolved" ? resolved : mine).filter((c) => matches(c.body, c.authorName, c.anchor.quote?.exact, c.suggestion?.after, c.page.path));
8670
8718
  const placed = list.filter((c) => this.placements.get(c._id)?.element);
8671
8719
  const lost = list.filter((c) => !this.placements.get(c._id)?.element);
8672
- const otherPages = this.pages.filter((p) => p.path !== this.pageKey && matches(p.path));
8720
+ const elsewhere = (this.site ?? []).filter((c) => c.page.path !== this.pageKey);
8721
+ const elsewhereOf = (status) => elsewhere.filter((c) => c.status === status);
8722
+ const groups = /* @__PURE__ */ new Map();
8723
+ for (const c of elsewhereOf(this.tab === "resolved" ? "resolved" : "open")) {
8724
+ if (!matches(c.body, c.authorName, c.anchor.quote?.exact, c.suggestion?.after, c.page.path)) continue;
8725
+ groups.set(c.page.path, [...groups.get(c.page.path) ?? [], c]);
8726
+ }
8727
+ const listed = new Set(elsewhereOf(this.tab === "resolved" ? "resolved" : "open").map((c) => c.page.path));
8728
+ const otherPages = this.pages.filter((p) => p.path !== this.pageKey && !listed.has(p.path) && matches(p.path));
8673
8729
  const content = (c) => [c.suggestion ? renderSuggestionLine(c.suggestion) : c.anchor.quote ? h("div", { class: "item-quote" }, c.anchor.quote.exact) : null, c.body ? h("div", { class: "item-text" }, c.body) : null];
8674
8730
  const mineItem = (c) => {
8675
8731
  const here = c.page.path === this.pageKey;
@@ -8688,12 +8744,14 @@ var NuniWidget = class {
8688
8744
  href
8689
8745
  }, h("div", { class: "row" }, h("span", { class: "author" }, here ? this.t("thisPage") : c.page.path), h("span", { class: "meta" }, this.i18n.timeAgo(c.createdAt)), h("span", { class: "spacer" }), h("span", { class: resolvedBadge ? "badge badge-ok" : "badge" }, resolvedBadge ? icon(ICONS.check) : null, this.t(!resolvedBadge ? "statusOpen" : c.resolution === "outdated" ? "outdated" : "resolved"))), ...content(c));
8690
8746
  };
8691
- const item = (c) => h("button", {
8747
+ const item = (c, href) => h(href === void 0 ? "button" : href === null ? "div" : "a", {
8692
8748
  class: "item",
8693
- type: "button",
8694
8749
  "aria-current": c._id === this.lastViewedId ? "true" : null,
8695
8750
  "data-focus-key": `item-${c._id}`,
8696
- onclick: () => this.focusComment(c._id)
8751
+ ...href === void 0 ? {
8752
+ type: "button",
8753
+ onclick: () => this.focusComment(c._id)
8754
+ } : { href }
8697
8755
  }, 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", {
8698
8756
  class: "badge",
8699
8757
  title: c.page.origin
@@ -8704,14 +8762,20 @@ var NuniWidget = class {
8704
8762
  else if (!list.length) children.push(h("div", { class: "empty" }, query ? this.t("noMatches", { query: this.search.trim() }) : this.t("noYours")));
8705
8763
  else children.push(...list.map(mineItem));
8706
8764
  } else if (!this.loaded) children.push(h("div", { class: "empty" }, this.t("loading")));
8707
- else if (!list.length && query) children.push(h("div", { class: "empty" }, this.t("noMatches", { query: this.search.trim() })));
8708
- else if (!list.length) children.push(h("div", { class: "empty" }, this.tab === "open" ? this.withKey("noOpen", "noOpenKey") : this.t("noResolved")));
8709
8765
  else {
8710
- children.push(...placed.map(item));
8711
- if (lost.length) {
8712
- children.push(h("div", { class: "section-label" }, this.t("notFoundHere")));
8713
- children.push(...lost.map(item));
8766
+ if ((groups.size > 0 || otherPages.length > 0) && (list.length || !query)) children.push(h("div", { class: "section-label" }, this.t("thisPage")));
8767
+ if (list.length) {
8768
+ children.push(...placed.map((c) => item(c)));
8769
+ if (lost.length) {
8770
+ children.push(h("div", { class: "section-label" }, this.t("notFoundHere")));
8771
+ children.push(...lost.map((c) => item(c)));
8772
+ }
8773
+ } else if (!query) children.push(h("div", { class: "empty" }, this.tab === "open" ? this.withKey("noOpen", "noOpenKey") : this.t("noResolved")));
8774
+ for (const [path, comments] of groups) {
8775
+ children.push(h("div", { class: "section-label section-path" }, path));
8776
+ children.push(...comments.map((c) => item(c, commentHref(c, location))));
8714
8777
  }
8778
+ if (!children.length && !otherPages.length) children.push(h("div", { class: "empty" }, this.t("noMatches", { query: this.search.trim() })));
8715
8779
  }
8716
8780
  if (otherPages.length && this.tab !== "mine") {
8717
8781
  children.push(h("div", { class: "section-label" }, this.t("otherPages")));
@@ -8732,7 +8796,7 @@ var NuniWidget = class {
8732
8796
  type: "button",
8733
8797
  "aria-label": this.t("close"),
8734
8798
  onclick: () => this.togglePanel(false)
8735
- }, icon(ICONS.close))), this.comments.length || this.mine?.length ? h("div", { class: "panel-search" }, icon(ICONS.search), h("input", {
8799
+ }, icon(ICONS.close))), this.comments.length || this.mine?.length || this.site?.length ? h("div", { class: "panel-search" }, icon(ICONS.search), h("input", {
8736
8800
  class: "field",
8737
8801
  type: "search",
8738
8802
  value: this.search,
@@ -8776,7 +8840,7 @@ var NuniWidget = class {
8776
8840
  this.tab = tab;
8777
8841
  this.render();
8778
8842
  }
8779
- }, tab === "open" ? this.t("tabOpen", { count: open.length }) : tab === "resolved" ? this.t("tabResolved", { count: resolved.length }) : this.mine ? this.t("tabYours", { count: this.mine.length }) : this.t("yours")))), h("div", {
8843
+ }, tab === "open" ? this.t("tabOpen", { count: open.length + elsewhereOf("open").length }) : tab === "resolved" ? this.t("tabResolved", { count: resolved.length + elsewhereOf("resolved").length }) : this.mine ? this.t("tabYours", { count: this.mine.length }) : this.t("yours")))), h("div", {
8780
8844
  class: "panel-list",
8781
8845
  id: "nuni-panel-list",
8782
8846
  role: "tabpanel",
@@ -9438,7 +9502,7 @@ function unmount(state) {
9438
9502
  * down the widget it was created for.
9439
9503
  */
9440
9504
  function init(options) {
9441
- return mount(options, { loadScreenshot: () => import("./screenshot-tools-Dk-xNV0c.js").then((m) => m.screenshotTools) });
9505
+ return mount(options, { loadScreenshot: () => import("./screenshot-tools-BFj-93o4.js").then((m) => m.screenshotTools) });
9442
9506
  }
9443
9507
  //#endregion
9444
9508
  export { VERSION, init, EN as messages };
@@ -1,4 +1,4 @@
1
- (function(){let e={bodyMaxLength:2e3,nameMaxLength:50,unclaimedCommentCap:500,maxOriginsPerProject:20,sessionTtlMs:2592e6,cliSessionTtlMs:7776e6,transferTtlMs:6048e5,bulkMax:100,pinReportMax:200,lostCountMax:99,cliLoginTtlMs:6e5,anchorTextMaxLength:120,quoteMaxLength:500,quoteContextLength:32,pageOpenLimit:1e3,pageResolvedLimit:200,yoursLimit:100,contextEntryMax:20,contextMessageMaxLength:500,domSnippetMaxLength:4e3,screenshotMaxBytes:6e5,imagesPerComment:3,imageMaxBytes:6e5,imageMaxSide:1600,repliesPerComment:200,reactionsPerComment:2e3,screenshotUploadWindowMs:3e5},t=`data-nuni-mask`;function n(e,t={},...n){let r=document.createElement(e);for(let[e,n]of Object.entries(t))n!=null&&n!==!1&&(e.startsWith(`on`)&&typeof n==`function`?r.addEventListener(e.slice(2).toLowerCase(),n):e===`class`?r.className=String(n):n===!0?r.setAttribute(e,``):r.setAttribute(e,String(n)));for(let e of n.flat())e!=null&&e!==!1&&r.append(typeof e==`string`?document.createTextNode(e):e);return r}function r(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}async function i(e,t){for(let[n,i]of[[`image/webp`,.82],[`image/jpeg`,.8],[`image/jpeg`,.55]]){let a=await r(e,n,i);if(a&&a.type===n&&a.size<=t)return a}return null}function a(e){return Math.max(3,Math.round(Math.max(e.width,e.height)/250))}function o(e,t,n){let[r,i]=t.points;if(r){if(e.beginPath(),t.tool===`pen`){e.moveTo(r.x,r.y);for(let n of t.points.slice(1))e.lineTo(n.x,n.y)}else if(i&&t.tool===`box`)e.rect(r.x,r.y,i.x-r.x,i.y-r.y);else if(i){let t=Math.atan2(i.y-r.y,i.x-r.x),a=n*4;e.moveTo(r.x,r.y),e.lineTo(i.x,i.y);for(let n of[-1,1])e.moveTo(i.x,i.y),e.lineTo(i.x-a*Math.cos(t+n*Math.PI/7),i.y-a*Math.sin(t+n*Math.PI/7))}e.stroke()}}function s(e){let[t,n]=e.points;return!t||!n?!1:e.tool===`pen`?e.points.length>2:Math.hypot(n.x-t.x,n.y-t.y)>6}let c=async(t,{container:r,labels:c,color:l})=>{let u;try{u=await createImageBitmap(t)}catch{return null}let d=r.getRootNode();if(!d.querySelector(`style[data-nuni-markup]`)){let e=n(`style`,{"data-nuni-markup":``},`
1
+ (function(){let e={bodyMaxLength:2e3,nameMaxLength:50,unclaimedCommentCap:500,maxOriginsPerProject:20,sessionTtlMs:2592e6,cliSessionTtlMs:7776e6,transferTtlMs:6048e5,bulkMax:100,pinReportMax:200,lostCountMax:99,cliLoginTtlMs:6e5,anchorTextMaxLength:120,quoteMaxLength:500,quoteContextLength:32,pageOpenLimit:1e3,pageResolvedLimit:200,siteOpenLimit:300,siteResolvedLimit:100,yoursLimit:100,contextEntryMax:20,contextMessageMaxLength:500,domSnippetMaxLength:4e3,screenshotMaxBytes:6e5,imagesPerComment:3,imageMaxBytes:6e5,imageMaxSide:1600,repliesPerComment:200,reactionsPerComment:2e3,screenshotUploadWindowMs:3e5},t=`data-nuni-mask`;function n(e,t={},...n){let r=document.createElement(e);for(let[e,n]of Object.entries(t))n!=null&&n!==!1&&(e.startsWith(`on`)&&typeof n==`function`?r.addEventListener(e.slice(2).toLowerCase(),n):e===`class`?r.className=String(n):n===!0?r.setAttribute(e,``):r.setAttribute(e,String(n)));for(let e of n.flat())e!=null&&e!==!1&&r.append(typeof e==`string`?document.createTextNode(e):e);return r}function r(e,t,n){return new Promise(r=>e.toBlob(r,t,n))}async function i(e,t){for(let[n,i]of[[`image/webp`,.82],[`image/jpeg`,.8],[`image/jpeg`,.55]]){let a=await r(e,n,i);if(a&&a.type===n&&a.size<=t)return a}return null}function a(e){return Math.max(3,Math.round(Math.max(e.width,e.height)/250))}function o(e,t,n){let[r,i]=t.points;if(r){if(e.beginPath(),t.tool===`pen`){e.moveTo(r.x,r.y);for(let n of t.points.slice(1))e.lineTo(n.x,n.y)}else if(i&&t.tool===`box`)e.rect(r.x,r.y,i.x-r.x,i.y-r.y);else if(i){let t=Math.atan2(i.y-r.y,i.x-r.x),a=n*4;e.moveTo(r.x,r.y),e.lineTo(i.x,i.y);for(let n of[-1,1])e.moveTo(i.x,i.y),e.lineTo(i.x-a*Math.cos(t+n*Math.PI/7),i.y-a*Math.sin(t+n*Math.PI/7))}e.stroke()}}function s(e){let[t,n]=e.points;return!t||!n?!1:e.tool===`pen`?e.points.length>2:Math.hypot(n.x-t.x,n.y-t.y)>6}let c=async(t,{container:r,labels:c,color:l})=>{let u;try{u=await createImageBitmap(t)}catch{return null}let d=r.getRootNode();if(!d.querySelector(`style[data-nuni-markup]`)){let e=n(`style`,{"data-nuni-markup":``},`
2
2
  .mk {
3
3
  position: fixed;
4
4
  inset: 0;