@unhingged/vizu-core 0.1.9 → 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/auto.cjs CHANGED
@@ -38,7 +38,8 @@ function isInside(target, selector) {
38
38
  return !!target.closest(selector);
39
39
  }
40
40
  function escapeHtml(s) {
41
- return s.replace(/[&<>"']/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]);
41
+ if (s == null) return "";
42
+ return String(s).replace(/[&<>"']/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]);
42
43
  }
43
44
  function formatTime(ts) {
44
45
  const d = new Date(ts);
@@ -50,14 +51,16 @@ function formatTime(ts) {
50
51
  return d.toLocaleDateString() + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
51
52
  }
52
53
  function initials(name) {
53
- return name.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0].toUpperCase()).join("");
54
+ if (!name) return "";
55
+ return String(name).split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0].toUpperCase()).join("");
54
56
  }
55
57
  function avatarHtml(user, className = "vz-comment-author-avatar") {
56
58
  if (!user) return `<span class="${className}">\xB7</span>`;
59
+ const displayName = user.name || user.id || "User";
57
60
  if (user.avatarUrl) {
58
- return `<span class="${className}"><img src="${escapeHtml(user.avatarUrl)}" alt="${escapeHtml(user.name)}" /></span>`;
61
+ return `<span class="${className}"><img src="${escapeHtml(user.avatarUrl)}" alt="${escapeHtml(displayName)}" /></span>`;
59
62
  }
60
- return `<span class="${className}">${escapeHtml(initials(user.name) || "?")}</span>`;
63
+ return `<span class="${className}">${escapeHtml(initials(displayName) || "?")}</span>`;
61
64
  }
62
65
  function refId() {
63
66
  return Math.random().toString(36).slice(2, 10);
@@ -460,6 +463,26 @@ var CloudStorageAdapter = class {
460
463
  accessDenialReason() {
461
464
  return this.accessDeniedReason;
462
465
  }
466
+ /**
467
+ * Fetch the list of users who can be @-mentioned on this workspace.
468
+ * Used by the popover's mention dropdown — returns owner + joined
469
+ * editors/viewers, sorted owner-first. Silent on no auth (returns [])
470
+ * so popover doesn't open a popup just to render an empty dropdown.
471
+ */
472
+ async searchMentionable() {
473
+ if (!this.cachedToken || this.isExpired(this.cachedToken)) return [];
474
+ try {
475
+ const res = await this.fetchAuthed(
476
+ this.apiUrl + "/api/workspaces/" + encodeURIComponent(this.workspace) + "/mentionable",
477
+ { method: "GET" }
478
+ );
479
+ if (!res.ok) return [];
480
+ const json = await this.parseJson(res);
481
+ return Array.isArray(json?.users) ? json.users : [];
482
+ } catch {
483
+ return [];
484
+ }
485
+ }
463
486
  /* ─── StorageAdapter v2 ───────────────────────────────────────────── */
464
487
  async load(_namespace) {
465
488
  if (!this.cachedToken || this.isExpired(this.cachedToken)) {
@@ -1209,7 +1232,7 @@ function renderRepliesHtml(c) {
1209
1232
  const items = replies.map(
1210
1233
  (r) => `
1211
1234
  <div class="vz-reply" data-reply-id="${escapeHtml(r.id)}">
1212
- ${r.author ? `<div class="vz-comment-author">${avatarHtml(r.author)} <span class="vz-comment-author-name">${escapeHtml(r.author.name)}</span></div>` : ""}
1235
+ ${r.author ? `<div class="vz-comment-author">${avatarHtml(r.author)} <span class="vz-comment-author-name">${escapeHtml(r.author.name || r.author.id || "User")}</span></div>` : ""}
1213
1236
  <div class="vz-reply-text">${escapeHtml(r.text)}</div>
1214
1237
  <div class="vz-comment-meta">
1215
1238
  <span>${escapeHtml(formatTime(typeof r.createdAt === "number" ? r.createdAt : new Date(r.createdAt).getTime()))}</span>
@@ -1486,7 +1509,7 @@ var Popover = class {
1486
1509
  ${comments.length === 0 ? '<div class="vz-empty">No comments yet.</div>' : comments.map(
1487
1510
  (c) => `
1488
1511
  <div class="vz-comment" data-comment-id="${escapeHtml(c.id)}">
1489
- ${c.author ? `<div class="vz-comment-author">${avatarHtml(c.author)} <span class="vz-comment-author-name">${escapeHtml(c.author.name)}</span></div>` : ""}
1512
+ ${c.author ? `<div class="vz-comment-author">${avatarHtml(c.author)} <span class="vz-comment-author-name">${escapeHtml(c.author.name || c.author.id || "User")}</span></div>` : ""}
1490
1513
  <div>${renderCommentText(c.text, c.id)}</div>
1491
1514
  ${renderAttachmentsHtml(c.attachments)}
1492
1515
  <div class="vz-comment-meta">
@@ -3297,11 +3320,25 @@ var Vizu = class {
3297
3320
  this.user = user;
3298
3321
  this.popover?.setUser(user);
3299
3322
  this.pill?.setUser(user);
3323
+ if (this.pill) {
3324
+ if (this.shouldShowPill()) this.pill.show();
3325
+ else this.pill.hide();
3326
+ }
3300
3327
  this.bus.emit("user:changed", { user });
3301
3328
  }
3302
3329
  getUser() {
3303
3330
  return this.user;
3304
3331
  }
3332
+ /**
3333
+ * Whether the pill belongs on-screen right now. Cloud-mode workspaces
3334
+ * keep it hidden until the user is signed in; non-cloud usage (local /
3335
+ * memory storage) has no auth and always shows it. Recomputed on every
3336
+ * setUser and on mount.
3337
+ */
3338
+ shouldShowPill() {
3339
+ if (!(this.storage instanceof CloudStorageAdapter)) return true;
3340
+ return this.user !== null;
3341
+ }
3305
3342
  /**
3306
3343
  * Cloud-mode write gate. Every entry point that creates or modifies a
3307
3344
  * comment first checks that we have a known user. Without one, the
@@ -3665,7 +3702,7 @@ var Vizu = class {
3665
3702
  this.pill.setUser(this.user);
3666
3703
  this.pill.setComments(this.comments);
3667
3704
  this.pill.setMultiSelectState(this.multiSelectMode, this.pendingFingerprints.length);
3668
- this.pill.show();
3705
+ if (this.shouldShowPill()) this.pill.show();
3669
3706
  this.highlighter = new Highlighter(
3670
3707
  this.root,
3671
3708
  this.opts.ignoreSelectors ?? [],