@loupekit/sdk 0.13.1 → 0.14.0-next.80
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.global.js +149 -31
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +149 -31
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.global.js
CHANGED
|
@@ -427,11 +427,35 @@ var Loupe = (() => {
|
|
|
427
427
|
.hfeed-p { font-weight: 700; }
|
|
428
428
|
.hfeed-p-critical { color: var(--pin); }
|
|
429
429
|
.hfeed-p-high { color: #e0a92c; }
|
|
430
|
+
.hfeed-a { display: flex; gap: 4px; margin-top: 3px; }
|
|
431
|
+
.hfeed-act {
|
|
432
|
+
font-size: 11px; padding: 3px 8px; border: 1px solid var(--line); border-radius: 6px;
|
|
433
|
+
background: var(--bg-3); color: var(--ink); cursor: pointer;
|
|
434
|
+
}
|
|
435
|
+
.hfeed-act:hover { border-color: var(--accent); }
|
|
436
|
+
.hfeed-act.danger:hover, .hfeed-act.danger[data-armed] { border-color: var(--pin); color: var(--pin); }
|
|
430
437
|
.hempty { padding: 14px; text-align: center; color: var(--muted); font-size: 12px; }
|
|
431
438
|
.hfoot { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--line); font-size: 11px; color: var(--muted); text-align: center; }
|
|
432
439
|
|
|
433
440
|
/* Timeline grouping (project scope) + the repo filter. */
|
|
434
|
-
.daylabel {
|
|
441
|
+
.daylabel {
|
|
442
|
+
display: flex; align-items: center; gap: 6px; width: 100%; margin: 8px 0 0; padding: 4px 2px;
|
|
443
|
+
border: 0; background: none; cursor: pointer; text-align: left;
|
|
444
|
+
font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700;
|
|
445
|
+
}
|
|
446
|
+
.daylabel::before { content: "\u25BE"; font-size: 10px; width: 10px; }
|
|
447
|
+
.daylabel.shut::before { content: "\u25B8"; }
|
|
448
|
+
.daylabel::after {
|
|
449
|
+
content: attr(data-n); padding: 0 6px; border-radius: 999px; background: var(--bg-3);
|
|
450
|
+
font-size: 10px; letter-spacing: 0;
|
|
451
|
+
}
|
|
452
|
+
.daylabel:hover { color: var(--ink); }
|
|
453
|
+
.listfilters { display: flex; gap: 6px; margin: 6px 0 2px; }
|
|
454
|
+
.listfilters select {
|
|
455
|
+
flex: 1; min-width: 0; font-size: 12px; padding: 4px 6px; border: 1px solid var(--line); border-radius: 7px;
|
|
456
|
+
background: var(--bg-2); color: var(--ink); outline: none; cursor: pointer;
|
|
457
|
+
}
|
|
458
|
+
.listfilters select:focus { border-color: var(--accent); }
|
|
435
459
|
/* The page a comment belongs to, shown in the project scope (Settings \u2192 Page paths). */
|
|
436
460
|
.pathtag {
|
|
437
461
|
padding: 1px 6px; border-radius: 999px; background: var(--bg-3); color: var(--muted);
|
|
@@ -3983,7 +4007,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
3983
4007
|
};
|
|
3984
4008
|
|
|
3985
4009
|
// src/app.ts
|
|
3986
|
-
var SDK_VERSION = true ? "0.
|
|
4010
|
+
var SDK_VERSION = true ? "0.14.0" : "dev";
|
|
3987
4011
|
var FAB_SIZE = 46;
|
|
3988
4012
|
var FAB_DRAG_THRESHOLD = 6;
|
|
3989
4013
|
var LAUNCHER_SHORTCUT = "Alt+Shift+L";
|
|
@@ -4138,6 +4162,11 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
4138
4162
|
this.allLoaded = false;
|
|
4139
4163
|
/** A clicked stat tile, which narrows the list. */
|
|
4140
4164
|
this.statFilter = "";
|
|
4165
|
+
/** One board stage to show, or "" for every stage. */
|
|
4166
|
+
this.statusFilter = "";
|
|
4167
|
+
this.sortOrder = "";
|
|
4168
|
+
/** Day groups the reader folded shut. Session only: "Today" means another day tomorrow. */
|
|
4169
|
+
this.collapsedDays = /* @__PURE__ */ new Set();
|
|
4141
4170
|
/** Accent preset id (see ACCENTS), applied as inline --accent / --accent-soft. */
|
|
4142
4171
|
this.accent = "indigo";
|
|
4143
4172
|
/** Collapsed to the one-line minimize bar. */
|
|
@@ -4831,6 +4860,27 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
4831
4860
|
this.renderList();
|
|
4832
4861
|
};
|
|
4833
4862
|
listHead.appendChild(search);
|
|
4863
|
+
const filters = el("div", "listfilters");
|
|
4864
|
+
this.statusSel = el("select", "lf-status");
|
|
4865
|
+
this.statusSel.setAttribute("aria-label", "Filter by status");
|
|
4866
|
+
this.statusSel.append(
|
|
4867
|
+
optionEl("All statuses", "", !this.statusFilter),
|
|
4868
|
+
...Object.keys(STAGE_LABELS).map((k) => optionEl(STAGE_LABELS[k], k, this.statusFilter === k))
|
|
4869
|
+
);
|
|
4870
|
+
this.statusSel.onchange = () => {
|
|
4871
|
+
this.statusFilter = this.statusSel.value;
|
|
4872
|
+
this.saveState();
|
|
4873
|
+
this.renderList();
|
|
4874
|
+
};
|
|
4875
|
+
this.sortSel = el("select", "lf-sort");
|
|
4876
|
+
this.sortSel.setAttribute("aria-label", "Order");
|
|
4877
|
+
this.sortSel.append(optionEl("Newest first", "newest", false), optionEl("Oldest first", "oldest", false), optionEl("Page order", "page", false));
|
|
4878
|
+
this.sortSel.onchange = () => {
|
|
4879
|
+
this.sortOrder = this.sortSel.value;
|
|
4880
|
+
this.saveState();
|
|
4881
|
+
this.renderList();
|
|
4882
|
+
};
|
|
4883
|
+
filters.append(this.statusSel, this.sortSel);
|
|
4834
4884
|
this.listEl = el("div", "list");
|
|
4835
4885
|
const homeView = el("div", "view home-view");
|
|
4836
4886
|
this.homeEl = homeView;
|
|
@@ -4838,7 +4888,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
4838
4888
|
this.commentsHint = el("div", "hint-slot");
|
|
4839
4889
|
this.reviewBar = el("div", "reviewbar");
|
|
4840
4890
|
this.reviewBar.style.display = "none";
|
|
4841
|
-
commentsView.append(this.commentsHint, tools, listHead, this.reviewBar, this.listEl, this.buildIntegrations());
|
|
4891
|
+
commentsView.append(this.commentsHint, tools, listHead, filters, this.reviewBar, this.listEl, this.buildIntegrations());
|
|
4842
4892
|
const activityView = el("div", "view activity-view");
|
|
4843
4893
|
this.activityEl = activityView;
|
|
4844
4894
|
const chatView = el("div", "view chat-view");
|
|
@@ -5500,17 +5550,49 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
5500
5550
|
feed.innerHTML = recent.length ? recent.map((c) => {
|
|
5501
5551
|
const stage = STAGE_LABELS[normalizeStatus(c.status)];
|
|
5502
5552
|
const prio = normalizePriority(c.priority);
|
|
5503
|
-
return `<
|
|
5553
|
+
return `<div class="hfeed-i" role="button" tabindex="0" data-id="${escapeAttr(c.id)}"><span class="hfeed-t">${escapeHtml(c.title || (c.body.split("\n")[0] ?? "").slice(0, 60))}</span><span class="hfeed-m">${escapeHtml(c.author?.name ?? "")} \xB7 <span title="${escapeAttr(this.fmtWhen(c.createdAt))}">${fmtAgo(c.createdAt)}</span> \xB7 <span class="hfeed-s hfeed-s-${normalizeStatus(c.status)}">${stage}</span><span class="hfeed-p hfeed-p-${prio}">${PRIORITY_LABELS[prio]}</span></span><span class="hfeed-a"><button class="hfeed-act" data-act="resolve">${isResolved(c) ? "Reopen" : "Resolve"}</button><button class="hfeed-act" data-act="open">Open</button><button class="hfeed-act danger" data-act="delete">Delete</button></span></div>`;
|
|
5504
5554
|
}).join("") : `<div class="hempty">Nothing here yet.</div>`;
|
|
5505
5555
|
feed.querySelectorAll(".hfeed-i").forEach((b) => {
|
|
5506
|
-
|
|
5507
|
-
|
|
5556
|
+
const id = b.dataset.id;
|
|
5557
|
+
const open = () => {
|
|
5508
5558
|
this.statFilter = "";
|
|
5559
|
+
this.statusFilter = "";
|
|
5509
5560
|
this.setTab("comments");
|
|
5510
5561
|
this.expanded.add(id);
|
|
5562
|
+
const c = recent.find((x) => x.id === id);
|
|
5563
|
+
if (c) void this.loadMessages(c);
|
|
5511
5564
|
this.renderList();
|
|
5512
5565
|
this.flash(id);
|
|
5513
5566
|
};
|
|
5567
|
+
b.onclick = open;
|
|
5568
|
+
b.onkeydown = (e) => {
|
|
5569
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
5570
|
+
e.preventDefault();
|
|
5571
|
+
open();
|
|
5572
|
+
}
|
|
5573
|
+
};
|
|
5574
|
+
b.querySelectorAll(".hfeed-act").forEach((btn) => {
|
|
5575
|
+
btn.onclick = (e) => {
|
|
5576
|
+
e.stopPropagation();
|
|
5577
|
+
const c = recent.find((x) => x.id === id);
|
|
5578
|
+
if (!c) return;
|
|
5579
|
+
if (btn.dataset.act === "open") open();
|
|
5580
|
+
else if (btn.dataset.act === "resolve") void this.toggleResolved(c);
|
|
5581
|
+
else if (btn.dataset.act === "delete") {
|
|
5582
|
+
if (btn.dataset.armed) void this.deleteComment(c);
|
|
5583
|
+
else {
|
|
5584
|
+
btn.dataset.armed = "1";
|
|
5585
|
+
btn.textContent = "Confirm delete";
|
|
5586
|
+
setTimeout(() => {
|
|
5587
|
+
if (btn.isConnected) {
|
|
5588
|
+
delete btn.dataset.armed;
|
|
5589
|
+
btn.textContent = "Delete";
|
|
5590
|
+
}
|
|
5591
|
+
}, 3e3);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
};
|
|
5595
|
+
});
|
|
5514
5596
|
});
|
|
5515
5597
|
}
|
|
5516
5598
|
async loadNotifications() {
|
|
@@ -6619,6 +6701,8 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
6619
6701
|
if (typeof p?.tab === "string" && known.includes(p.tab)) this.tab = p.tab;
|
|
6620
6702
|
if (p?.scope === "page" || p?.scope === "all") this.scope = p.scope;
|
|
6621
6703
|
if (typeof p?.statFilter === "string") this.statFilter = p.statFilter;
|
|
6704
|
+
if (typeof p?.statusFilter === "string" && p.statusFilter in STAGE_LABELS) this.statusFilter = p.statusFilter;
|
|
6705
|
+
if (["newest", "oldest", "page"].includes(p?.sortOrder)) this.sortOrder = p.sortOrder;
|
|
6622
6706
|
if (p?.float && typeof p.float.w === "number") this.floatRect = { ...this.floatRect, ...p.float };
|
|
6623
6707
|
if (typeof p?.markersHidden === "boolean") this.markersHidden = p.markersHidden;
|
|
6624
6708
|
if (typeof p?.launcherHidden === "boolean") this.launcherHidden = p.launcherHidden;
|
|
@@ -6645,6 +6729,8 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
6645
6729
|
fab: this.fabPos,
|
|
6646
6730
|
scope: this.scope,
|
|
6647
6731
|
statFilter: this.statFilter,
|
|
6732
|
+
statusFilter: this.statusFilter,
|
|
6733
|
+
sortOrder: this.sortOrder,
|
|
6648
6734
|
accent: this.accent,
|
|
6649
6735
|
minimized: this.minimized,
|
|
6650
6736
|
hoverHints: this.hoverHints,
|
|
@@ -6727,25 +6813,43 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
6727
6813
|
if (this.statFilter === "needs_you" && !needsYou({ status: c.status }).needs) return false;
|
|
6728
6814
|
if (this.statFilter === "resolved" && stage !== "resolved") return false;
|
|
6729
6815
|
if (this.statFilter === "stale" && (stage === "resolved" || !(Date.parse(c.createdAt) < weekAgo))) return false;
|
|
6816
|
+
if (this.statusFilter && stage !== this.statusFilter) return false;
|
|
6730
6817
|
return !q || `${c.title ?? ""} ${c.body} ${c.author?.name ?? ""}`.toLowerCase().includes(q);
|
|
6731
6818
|
});
|
|
6732
|
-
|
|
6819
|
+
const order = this.sortOrder || (this.scope === "all" ? "newest" : "page");
|
|
6820
|
+
if (this.sortSel) this.sortSel.value = order;
|
|
6821
|
+
if (this.statusSel) this.statusSel.value = this.statusFilter;
|
|
6822
|
+
if (order === "newest") items = [...items].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
6823
|
+
if (order === "oldest") items = [...items].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
6824
|
+
const grouped = order !== "page";
|
|
6825
|
+
const perDay = /* @__PURE__ */ new Map();
|
|
6826
|
+
if (grouped) for (const c of items) perDay.set(dayLabel(c.createdAt), (perDay.get(dayLabel(c.createdAt)) ?? 0) + 1);
|
|
6733
6827
|
this.renderReviewBar();
|
|
6734
6828
|
if (!items.length) {
|
|
6735
6829
|
this.listEl.appendChild(el(
|
|
6736
6830
|
"div",
|
|
6737
6831
|
"empty",
|
|
6738
|
-
this.statFilter ? "Nothing in this bucket." : q ? "No comments match your search." : this.scope === "all" ? "No feedback in this project yet." : "No comments yet. Use Inspect to pick an element, or Note to drop a comment anywhere on the page."
|
|
6832
|
+
this.statFilter ? "Nothing in this bucket." : this.statusFilter ? `Nothing in ${STAGE_LABELS[this.statusFilter]}.` : q ? "No comments match your search." : this.scope === "all" ? "No feedback in this project yet." : "No comments yet. Use Inspect to pick an element, or Note to drop a comment anywhere on the page."
|
|
6739
6833
|
));
|
|
6740
6834
|
}
|
|
6741
6835
|
let lastDay = "";
|
|
6742
6836
|
items.forEach((c, i) => {
|
|
6743
|
-
if (
|
|
6837
|
+
if (grouped) {
|
|
6744
6838
|
const day = dayLabel(c.createdAt);
|
|
6839
|
+
const shut = this.collapsedDays.has(day);
|
|
6745
6840
|
if (day !== lastDay) {
|
|
6746
|
-
|
|
6841
|
+
const head = el("button", "daylabel" + (shut ? " shut" : ""), day);
|
|
6842
|
+
head.dataset.n = String(perDay.get(day) ?? 0);
|
|
6843
|
+
head.setAttribute("aria-expanded", String(!shut));
|
|
6844
|
+
head.onclick = () => {
|
|
6845
|
+
if (shut) this.collapsedDays.delete(day);
|
|
6846
|
+
else this.collapsedDays.add(day);
|
|
6847
|
+
this.renderList();
|
|
6848
|
+
};
|
|
6849
|
+
this.listEl.appendChild(head);
|
|
6747
6850
|
lastDay = day;
|
|
6748
6851
|
}
|
|
6852
|
+
if (shut) return;
|
|
6749
6853
|
}
|
|
6750
6854
|
this.listEl.appendChild(this.itemView(c, i));
|
|
6751
6855
|
});
|
|
@@ -6892,31 +6996,14 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
6892
6996
|
}
|
|
6893
6997
|
const actions = el("div", "actions");
|
|
6894
6998
|
const doneBtn = el("button", "", isResolved(c) ? "Reopen" : "Resolve");
|
|
6895
|
-
doneBtn.onclick =
|
|
6999
|
+
doneBtn.onclick = (e) => {
|
|
6896
7000
|
e.stopPropagation();
|
|
6897
|
-
|
|
6898
|
-
c.status = status;
|
|
6899
|
-
await this.store.update(c.id, { status });
|
|
6900
|
-
this.renderPins();
|
|
6901
|
-
this.renderList();
|
|
6902
|
-
this.addActivity({
|
|
6903
|
-
kind: status === "resolved" ? "comment.resolve" : "comment.reopen",
|
|
6904
|
-
label: `${status === "resolved" ? "Resolved" : "Reopened"} \u201C${c.title || c.body.split("\n")[0] || "comment"}\u201D`
|
|
6905
|
-
});
|
|
7001
|
+
void this.toggleResolved(c);
|
|
6906
7002
|
};
|
|
6907
7003
|
const del = el("button", "", "Delete");
|
|
6908
|
-
del.onclick =
|
|
7004
|
+
del.onclick = (e) => {
|
|
6909
7005
|
e.stopPropagation();
|
|
6910
|
-
|
|
6911
|
-
this.comments = this.comments.filter((x) => x.id !== c.id);
|
|
6912
|
-
this.resolved.delete(c.id);
|
|
6913
|
-
this.renderPins();
|
|
6914
|
-
this.renderList();
|
|
6915
|
-
this.addActivity({
|
|
6916
|
-
kind: "comment.delete",
|
|
6917
|
-
label: `Deleted \u201C${c.title || c.body.split("\n")[0] || "comment"}\u201D`,
|
|
6918
|
-
level: "warn"
|
|
6919
|
-
});
|
|
7006
|
+
void this.deleteComment(c);
|
|
6920
7007
|
};
|
|
6921
7008
|
actions.append(doneBtn, del);
|
|
6922
7009
|
detail.appendChild(actions);
|
|
@@ -6933,6 +7020,37 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
|
|
|
6933
7020
|
};
|
|
6934
7021
|
return item;
|
|
6935
7022
|
}
|
|
7023
|
+
/**
|
|
7024
|
+
* Resolve or reopen a comment. Shared by the card and the Home feed. The page list
|
|
7025
|
+
* and the project list are separate reads, so the change is applied to both.
|
|
7026
|
+
*/
|
|
7027
|
+
async toggleResolved(c) {
|
|
7028
|
+
const status = isResolved(c) ? "queue" : "resolved";
|
|
7029
|
+
await this.store.update(c.id, { status });
|
|
7030
|
+
for (const x of [c, ...this.comments, ...this.allComments]) if (x.id === c.id) x.status = status;
|
|
7031
|
+
this.renderPins();
|
|
7032
|
+
this.renderList();
|
|
7033
|
+
this.renderHome();
|
|
7034
|
+
this.addActivity({
|
|
7035
|
+
kind: status === "resolved" ? "comment.resolve" : "comment.reopen",
|
|
7036
|
+
label: `${status === "resolved" ? "Resolved" : "Reopened"} \u201C${c.title || c.body.split("\n")[0] || "comment"}\u201D`
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
7039
|
+
/** Delete a comment, from the card or the Home feed. */
|
|
7040
|
+
async deleteComment(c) {
|
|
7041
|
+
await this.store.remove(c.id);
|
|
7042
|
+
this.comments = this.comments.filter((x) => x.id !== c.id);
|
|
7043
|
+
this.allComments = this.allComments.filter((x) => x.id !== c.id);
|
|
7044
|
+
this.resolved.delete(c.id);
|
|
7045
|
+
this.renderPins();
|
|
7046
|
+
this.renderList();
|
|
7047
|
+
this.renderHome();
|
|
7048
|
+
this.addActivity({
|
|
7049
|
+
kind: "comment.delete",
|
|
7050
|
+
label: `Deleted \u201C${c.title || c.body.split("\n")[0] || "comment"}\u201D`,
|
|
7051
|
+
level: "warn"
|
|
7052
|
+
});
|
|
7053
|
+
}
|
|
6936
7054
|
/**
|
|
6937
7055
|
* The review banner. This is the one place the panel asks a human to decide
|
|
6938
7056
|
* something, so it sits above everything else in the detail.
|