overplay 4.6.0 → 4.7.1
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 +17 -0
- package/dist/index.d.ts +2 -1
- package/dist/overplay.esm.js +255 -71
- package/dist/overplay.esm.js.map +1 -1
- package/dist/overplay.iife.js +1 -1
- package/dist/overplay.iife.js.map +1 -1
- package/dist/overplay.umd.js +1 -1
- package/dist/overplay.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/overplay.esm.js
CHANGED
|
@@ -107,6 +107,7 @@ function serviceOrigin(origin) {
|
|
|
107
107
|
}
|
|
108
108
|
class HostedAdapter {
|
|
109
109
|
constructor(serverUrl, projectId, page, sessionChanged) {
|
|
110
|
+
/** The Overplay service address, such as https://overplay.app. */
|
|
110
111
|
__publicField(this, "base");
|
|
111
112
|
__publicField(this, "key");
|
|
112
113
|
__publicField(this, "accessToken", "");
|
|
@@ -474,7 +475,7 @@ function newActivity(comments, seen, selfId) {
|
|
|
474
475
|
function sameComments(a, b) {
|
|
475
476
|
return a.length === b.length && JSON.stringify(a) === JSON.stringify(b);
|
|
476
477
|
}
|
|
477
|
-
const version = "4.
|
|
478
|
+
const version = "4.7.1";
|
|
478
479
|
const VERSION = version;
|
|
479
480
|
const PACKAGE = "overplay";
|
|
480
481
|
function updatePrompt(current, latest) {
|
|
@@ -546,7 +547,7 @@ const CHOICES = {
|
|
|
546
547
|
buttonColor: ["default", "site", "indigo", "violet", "blue", "teal", "amber", "orange", "red", "pink"],
|
|
547
548
|
resolvedPins: ["green", "gray"],
|
|
548
549
|
pinSize: ["regular", "small"],
|
|
549
|
-
pinLabel: ["number", "initials"],
|
|
550
|
+
pinLabel: ["number", "initials", "photo"],
|
|
550
551
|
pushContent: [true, false],
|
|
551
552
|
notifications: [true, false],
|
|
552
553
|
shortcut: [true, false]
|
|
@@ -597,6 +598,7 @@ const paths = {
|
|
|
597
598
|
trash: "M3 6h18M9 6V4h6v2M5 6l1 14h12l1-14M10 10v6m4-6v6",
|
|
598
599
|
pin: "M12 21s7-6 7-13A7 7 0 0 0 5 8c0 7 7 13 7 13ZM12 6v4M10 8h4",
|
|
599
600
|
search: "m21 21-5-5M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Z",
|
|
601
|
+
filter: "M4 6h16M7 12h10M10 18h4",
|
|
600
602
|
eye: "M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12ZM12 14.8a2.8 2.8 0 1 0 0-5.6 2.8 2.8 0 0 0 0 5.6Z",
|
|
601
603
|
eyeOff: "M3 3l18 18M10.6 5.6A9 9 0 0 1 12 5.5c6 0 9.5 6.5 9.5 6.5a16 16 0 0 1-2.9 3.6M6.5 6.9A16 16 0 0 0 2.5 12S6 18.5 12 18.5a9 9 0 0 0 4.6-1.3M10 10a2.8 2.8 0 0 0 4 4",
|
|
602
604
|
unlink: "M4.5 4.5h15v15h-15zM9.5 9.5l5 5M14.5 9.5l-5 5",
|
|
@@ -622,6 +624,18 @@ function icon(name) {
|
|
|
622
624
|
svg.append(path);
|
|
623
625
|
return svg;
|
|
624
626
|
}
|
|
627
|
+
function logo() {
|
|
628
|
+
const ns = "http://www.w3.org/2000/svg", svg = document.createElementNS(ns, "svg");
|
|
629
|
+
svg.setAttribute("viewBox", "0 0 48 48");
|
|
630
|
+
svg.setAttribute("aria-hidden", "true");
|
|
631
|
+
const shape = document.createElementNS(ns, "path");
|
|
632
|
+
shape.setAttribute("d", "M24 4a20 20 0 1 1 0 40H9a5 5 0 0 1-5-5V24A20 20 0 0 1 24 4Z");
|
|
633
|
+
shape.setAttribute("fill", "#0068f9");
|
|
634
|
+
const ring = document.createElementNS(ns, "circle");
|
|
635
|
+
for (const [k, v] of [["cx", "24"], ["cy", "24"], ["r", "8"], ["fill", "none"], ["stroke", "#fff"], ["stroke-width", "5"]]) ring.setAttribute(k, v);
|
|
636
|
+
svg.append(shape, ring);
|
|
637
|
+
return svg;
|
|
638
|
+
}
|
|
625
639
|
function button(label, className, onClick, name) {
|
|
626
640
|
const el = element("button", className);
|
|
627
641
|
el.type = "button";
|
|
@@ -751,16 +765,16 @@ const styles = `
|
|
|
751
765
|
@media(prefers-color-scheme:dark){:host([data-theme=system]){${tokenDeclarations(darkTokens)}color-scheme:dark}}
|
|
752
766
|
:host>div{font:16px/1.5 var(--cw-font);color:var(--cw-ink);letter-spacing:normal;text-align:left}*,*::before,*::after{box-sizing:border-box}button,input,textarea,select{font:inherit}button{cursor:pointer}button:disabled{cursor:default;opacity:.45}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:3px solid var(--cw-accent);outline-offset:3px}button{color:inherit}svg{width:18px;height:18px;flex-shrink:0}button{touch-action:manipulation}img{max-width:100%}[hidden]{display:none!important}
|
|
753
767
|
.launcher{position:fixed;bottom:24px;right:24px;display:flex;align-items:center;justify-content:center;width:52px;height:52px;border:1px solid #ffffff24;background:var(--cw-inverse);color:var(--cw-on-inverse);padding:0;border-radius:50%;box-shadow:0 6px 24px #12172233;font-weight:600;font-size:16px;pointer-events:auto;touch-action:none;user-select:none;-webkit-user-select:none;cursor:grab;transition:transform .16s,box-shadow .16s}.launcher:hover{transform:translateY(-2px);box-shadow:0 8px 28px #20243733}.launcher.dragging{cursor:grabbing;transition:none;transform:scale(1.06);box-shadow:0 12px 32px #2024374d}.launcher[aria-expanded=true]{background:var(--cw-accent)}.launcher>svg{width:23px;height:23px}.badge{position:absolute;top:-5px;right:-5px;font-weight:650;font-size:12px;line-height:18px;min-width:22px;height:22px;border:2px solid var(--cw-surface);border-radius:999px;background:var(--cw-accent);color:#fff;text-align:center;padding:0 4px}.launcher[aria-expanded=true] .badge{background:var(--cw-inverse)}
|
|
754
|
-
.panel{position:fixed;top:0;right:0;bottom:0;width:400px;max-width:100vw;display:flex;flex-direction:column;background:var(--cw-surface);border-left:1px solid var(--cw-line);box-shadow:var(--cw-shadow);pointer-events:auto;overflow:hidden}.panel.entering{animation:slide-in .2s ease-out}.panel.left{right:auto;left:0;border-left:0;border-right:1px solid var(--cw-line);--cw-slide:-24px}.detail{display:flex;flex-direction:column;flex:1;min-height:0}.detail.entering{animation:enter .16s ease-out}.detail:focus{outline:0}.head{display:flex;align-items:center;gap:10px;padding:22px 20px 16px}.brand-icon{width:34px;height:34px;display:grid;place-items:center;background:var(--cw-accent-soft);color:var(--cw-accent);border-radius:50%}.heading{flex:1;min-width:0}.heading h2{font-size:19px;line-height:1.3;letter-spacing:-.4px;margin:0;font-weight:650}.eyebrow{font-size:12px;font-weight:700;letter-spacing:1.4px;color:var(--cw-muted);text-transform:uppercase;margin:0 0 4px}.subtext{color:var(--cw-muted);font-size:14px;margin:4px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button{width:32px;height:32px;display:inline-grid;place-items:center;background:transparent;border:0;border-radius:50%;flex-shrink:0}.icon-button:hover{background:var(--cw-surface-soft)}.icon-button.danger{color:var(--cw-danger)}.icon-button svg{width:16px;height:16px}.icon-button.outlined{width:42px;height:42px;border:1px solid var(--cw-line-strong);border-radius:50%;color:var(--cw-ink-soft)}.icon-button.outlined svg{width:18px;height:18px}.icon-button.busy:disabled{opacity:1;color:var(--cw-accent)}.icon-button.busy svg{animation:spin .9s linear infinite}.toolbar{padding:0 20px 16px;display:flex;gap:10px;align-items:center}.primary{border:0;border-radius:var(--cw-radius-pill);background:var(--cw-accent);color:#fff;display:inline-flex;gap:7px;align-items:center;justify-content:center;padding:10px 16px;font-size:14px;font-weight:550;white-space:nowrap;box-shadow:inset 0 -1px 0 #12172238;transition:filter .16s}.primary:hover{filter:brightness(.94)}.toolbar .primary{flex:1}.quiet{border:1px solid var(--cw-line-strong);background:var(--cw-surface);color:var(--cw-ink);border-radius:var(--cw-radius-pill);padding:9px 16px;font-size:14px}.quiet:hover{background:var(--cw-canvas)}.quiet.update{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;flex-shrink:0;color:var(--cw-accent);border-color:var(--cw-accent-line);background:var(--cw-accent-soft);font-weight:600}.quiet.update:hover{border-color:var(--cw-accent)}.quiet.update svg{width:14px;height:14px}.scope{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin:0 20px 8px;padding:4px;background:var(--cw-surface-soft);border-radius:var(--cw-radius-pill)}.scope-option{display:flex;gap:7px;align-items:center;justify-content:center;border:0;background:transparent;border-radius:var(--cw-radius-pill);padding:8px 6px;font-size:14px;font-weight:550;color:var(--cw-muted);white-space:nowrap}.scope-option:hover{color:var(--cw-ink)}.scope-option[aria-pressed=true]{background:var(--cw-surface);color:var(--cw-ink);box-shadow:0 1px 3px #2024371f}.scope-option[aria-pressed=true] .count{background:var(--cw-accent-soft);color:var(--cw-accent)}.tabs{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;margin:0 20px 8px;padding:4px;border:1px solid var(--cw-line);border-radius:var(--cw-radius-pill)}.tab{display:flex;gap:7px;align-items:center;justify-content:center;border:0;border-radius:var(--cw-radius-pill);background:transparent;padding:7px 6px;color:var(--cw-muted);font-size:14px;font-weight:550;white-space:nowrap}.tab[aria-pressed=true]{color:var(--cw-on-inverse);background:var(--cw-inverse)}.tab[aria-pressed=true] .count{background:#ffffff2e;color:var(--cw-on-inverse)}.count{background:var(--cw-surface-soft);padding:0 6px;border-radius:5px;font-size:12px;line-height:18px}.search{position:relative;margin:4px 20px 8px}.search>svg{position:absolute;left:11px;top:11px;width:15px;height:15px;color:var(--cw-subtle);pointer-events:none}.search input{border:1px solid var(--cw-line);border-radius:var(--cw-radius);background:var(--cw-canvas);width:100%;padding:9px 38px 9px 34px;color:var(--cw-ink);font-size:14px}.search input::placeholder{color:var(--cw-subtle)}.search input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}.search-clear{position:absolute;right:5px;top:50%;transform:translateY(-50%);width:28px;height:28px;color:var(--cw-muted)}.search-clear svg{width:14px;height:14px}.people{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 20px 6px}.filter-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cw-line);background:var(--cw-surface);color:var(--cw-ink-soft);border-radius:999px;padding:4px 6px 4px 11px;font-size:13px;font-weight:600;white-space:nowrap}.filter-chip:hover{background:var(--cw-canvas)}.filter-chip[aria-pressed=true]{background:var(--cw-accent-soft);border-color:var(--cw-accent-line);color:var(--cw-accent)}.filter-chip[aria-pressed=true] .count{background:var(--cw-surface);color:var(--cw-accent)}.select{position:relative;display:inline-flex;min-width:0;max-width:220px;color:var(--cw-muted)}.select::after{content:'';position:absolute;right:12px;top:50%;width:6px;height:6px;border:solid currentColor;border-width:0 1.5px 1.5px 0;transform:translateY(-70%) rotate(45deg);pointer-events:none}.select select{appearance:none;-webkit-appearance:none;border:1px solid var(--cw-line);background:var(--cw-surface);color:var(--cw-ink-soft);border-radius:999px;padding:5px 30px 5px 11px;font-size:13px;font-weight:600;line-height:18px;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.select select:hover{background:var(--cw-canvas)}.select.chosen{color:var(--cw-accent)}.select.chosen select{background:var(--cw-accent-soft);border-color:var(--cw-accent-line);color:var(--cw-accent)}.select option{color:var(--cw-ink);background:var(--cw-surface)}.link-button{margin-left:auto;border:0;background:transparent;color:var(--cw-accent);font-size:13px;font-weight:600;padding:4px 2px;white-space:nowrap}.link-button:hover{text-decoration:underline}mark.hit{background:color-mix(in srgb,var(--cw-accent) 22%,transparent);color:inherit;border-radius:3px;padding:0 1px;-webkit-box-decoration-break:clone;box-decoration-break:clone}.note-match{display:flex;align-items:center;gap:5px;margin:4px 0 0 31px;font-size:12.5px;line-height:1.5;color:var(--cw-muted);min-width:0}.note-match svg{width:12px;height:12px}.note-match-label{font-weight:600;color:var(--cw-ink-soft);white-space:nowrap;flex-shrink:0;max-width:45%;overflow:hidden;text-overflow:ellipsis}.note-match-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.list{flex:1;overflow:auto;overscroll-behavior:contain;padding:8px 12px 16px;min-height:0}.list-title{display:flex;align-items:center;gap:8px;padding:12px 8px 6px;color:var(--cw-muted);font-size:12px;letter-spacing:1.15px;text-transform:uppercase;font-weight:600}.page-path{text-transform:none;letter-spacing:0;font-size:13px;color:var(--cw-ink-soft);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.page-name{text-transform:none;letter-spacing:0;font-size:13px;font-weight:600;color:var(--cw-ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.here{flex-shrink:0;padding:0 6px;border-radius:5px;background:var(--cw-accent-soft);color:var(--cw-accent);font-size:11px;letter-spacing:.4px;line-height:18px}.go{margin-left:auto;display:inline-flex;align-items:center;gap:4px;color:var(--cw-accent);font-weight:600}.note.current{background:var(--cw-accent-soft);border-color:var(--cw-accent-line)}.note{display:block;text-align:left;width:100%;border:1px solid transparent;background:var(--cw-surface);color:var(--cw-ink);border-radius:10px;padding:9px 10px;margin-bottom:2px}.note .note-header .via{color:var(--cw-muted);font-size:10px;max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.note-header{margin-bottom:4px;gap:7px}.note .avatar{width:24px;height:24px;font-size:11px}.note .name{font-size:13px}.note .time{margin-left:0}.note-meta{margin-left:auto;display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--cw-muted);white-space:nowrap;flex-shrink:0}.note-meta svg{width:12px;height:12px}.note:hover{background:var(--cw-canvas);border-color:var(--cw-line)}.note-header{display:flex;align-items:center;gap:8px;margin-bottom:9px}.avatar{display:inline-grid;place-items:center;width:29px;height:29px;border-radius:50%;background:var(--cw-avatar-bg);color:var(--cw-avatar-ink);font-size:12px;font-weight:700;flex-shrink:0;overflow:hidden}.avatar img{height:100%;width:100%;object-fit:cover}.name{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.time{font-size:12px;color:var(--cw-muted);margin-left:auto;white-space:nowrap}.note-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin:0 0 0 31px;line-height:1.5;font-size:14px;color:var(--cw-ink-soft);overflow-wrap:anywhere}.note-footer{display:flex;gap:6px;align-items:center;margin:6px 0 0 31px;color:var(--cw-muted);font-size:12px}.note-footer svg{width:12px;height:12px}.note-number{color:var(--cw-accent);font-weight:700}.status{display:inline-flex;align-items:center;gap:4px}.status.resolved{color:var(--cw-resolved-text)}.status-dot{width:5px;height:5px;background:var(--cw-accent);border-radius:50%;display:inline-block}.empty{height:100%;min-height:230px;display:flex;align-items:center;justify-content:center;text-align:center;padding:32px 16px}.empty-icon{position:relative;display:grid;place-items:center;margin:0 auto 18px;width:64px;height:64px;border:1px solid var(--cw-line);border-radius:21px;transform:rotate(-7deg);background:var(--cw-canvas);color:var(--cw-accent)}.empty-icon svg{width:27px;height:27px}.empty h3{font-size:17px;letter-spacing:-.25px;margin:0 0 8px}.empty p{font-size:14px;color:var(--cw-muted);line-height:1.7;margin:0 auto 18px;max-width:230px}.footer{display:flex;align-items:center;gap:9px;border-top:1px solid var(--cw-line);padding:14px 20px;background:var(--cw-canvas)}.footer .avatar{width:25px;height:25px;font-size:11px}.identity{border:0;background:transparent;text-align:left;padding:0;min-width:0;flex:1}.identity strong{display:block;font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.identity small{font-size:12px;color:var(--cw-muted)}.storage-label{font-size:11px;color:var(--cw-muted);text-align:right;line-height:1.5;max-width:105px}.storage-dot{display:inline-block;width:5px;height:5px;background:var(--cw-resolved);border-radius:50%;margin-right:4px}.error{padding:12px 16px;margin:12px 20px;border:1px solid var(--cw-danger-line);background:var(--cw-danger-soft);color:var(--cw-danger);border-radius:10px;font-size:14px;overflow-wrap:anywhere}.error button{display:block;margin-top:8px}.notice{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:1;background:var(--cw-inverse);color:var(--cw-on-inverse);border:1px solid #ffffff20;border-radius:12px;padding:12px 15px;display:flex;align-items:center;gap:12px;font-size:14px;box-shadow:0 6px 30px #0002;pointer-events:auto;max-width:calc(100vw - 32px)}.notice kbd{font-size:12px;color:var(--cw-inverse-muted);border:1px solid #ffffff30;border-radius:4px;padding:2px 5px}.notice button{color:var(--cw-on-inverse)}.notice.beside-panel{left:calc(50% - 200px)}.notice.beside-panel.left{left:calc(50% + 200px)}.pins{position:fixed;inset:0;pointer-events:none}.pin{position:absolute;width:32px;height:36px;transform:translate(-50%,-100%);border:2px solid #fff;border-radius:15px 15px 15px 4px;background:var(--cw-accent);color:#fff;box-shadow:var(--cw-shadow-sm);display:grid;place-items:center;font-size:13px;font-weight:650;pointer-events:auto;transition:scale .15s}.pin:hover,.pin[aria-pressed=true]{scale:1.12}.pin.resolved{background:var(--cw-resolved)}.pin.highlight{animation:pulse 1s ease-out 2}.pin.hovered{animation:pulse 1s ease-out infinite;scale:1.12;opacity:1!important}.focus-ring.hover-ring{animation:none;border-style:dashed}.focus-ring{position:absolute;border:2px solid var(--cw-accent);border-radius:var(--cw-radius);background:color-mix(in srgb,var(--cw-accent) 6%,transparent);pointer-events:none;animation:enter .16s ease-out}.pick-ring{border-style:dashed;animation:none;transition:left .08s,top .08s,width .08s,height .08s}.pin:focus-visible{outline-offset:3px}.pin.preview{pointer-events:none;opacity:.7}.pin.movable{cursor:grab;touch-action:none}.pins.focused .pin:not([aria-pressed=true]):not(.dragging){opacity:.3;transition:opacity .15s,scale .15s}.pins.focused .pin:hover,.pins.focused .pin:focus-visible{opacity:1}.pin.dragging{cursor:grabbing;scale:1.15;pointer-events:none;box-shadow:var(--cw-shadow);transition:none;z-index:1}.detail-hint{margin:16px 20px 12px;color:var(--cw-muted);font-size:14px;line-height:1.6}.compose{padding:0 20px 20px}.compose textarea,.reply-bar textarea{scrollbar-width:none}
|
|
768
|
+
.panel{position:fixed;top:0;right:0;bottom:0;width:400px;max-width:100vw;display:flex;flex-direction:column;background:var(--cw-surface);border-left:1px solid var(--cw-line);box-shadow:var(--cw-shadow);pointer-events:auto;overflow:hidden}.panel.entering{animation:slide-in .2s ease-out}.panel.left{right:auto;left:0;border-left:0;border-right:1px solid var(--cw-line);--cw-slide:-24px}.detail{display:flex;flex-direction:column;flex:1;min-height:0}.detail.entering{animation:enter .16s ease-out}.detail:focus{outline:0}.head{display:flex;align-items:center;gap:10px;padding:22px 20px 16px}.brand-icon{width:34px;height:34px;display:grid;place-items:center;flex-shrink:0}.brand-icon svg{width:34px;height:34px}.heading{flex:1;min-width:0}.heading h2{font-size:19px;line-height:1.3;letter-spacing:-.4px;margin:0;font-weight:650}.eyebrow{font-size:12px;font-weight:700;letter-spacing:1.4px;color:var(--cw-muted);text-transform:uppercase;margin:0 0 4px}.subtext{color:var(--cw-muted);font-size:14px;margin:4px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button{width:32px;height:32px;display:inline-grid;place-items:center;background:transparent;border:0;border-radius:50%;flex-shrink:0}.icon-button:hover{background:var(--cw-surface-soft)}.icon-button.danger{color:var(--cw-danger)}.icon-button svg{width:16px;height:16px}.icon-button.outlined{width:42px;height:42px;border:1px solid var(--cw-line-strong);border-radius:50%;color:var(--cw-ink-soft)}.icon-button.outlined svg{width:18px;height:18px}.icon-button.busy:disabled{opacity:1;color:var(--cw-accent)}.icon-button.busy svg{animation:spin .9s linear infinite}.toolbar{padding:0 20px 16px;display:flex;gap:10px;align-items:center}.primary{border:0;border-radius:var(--cw-radius-pill);background:var(--cw-accent);color:#fff;display:inline-flex;gap:7px;align-items:center;justify-content:center;padding:10px 16px;font-size:14px;font-weight:550;white-space:nowrap;box-shadow:inset 0 -1px 0 #12172238;transition:filter .16s}.primary:hover{filter:brightness(.94)}.toolbar .primary{flex:1}.quiet{border:1px solid var(--cw-line-strong);background:var(--cw-surface);color:var(--cw-ink);border-radius:var(--cw-radius-pill);padding:9px 16px;font-size:14px}.quiet:hover{background:var(--cw-canvas)}.quiet.update{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;flex-shrink:0;color:var(--cw-accent);border-color:var(--cw-accent-line);background:var(--cw-accent-soft);font-weight:600}.quiet.update:hover{border-color:var(--cw-accent)}.quiet.update svg{width:14px;height:14px}.scope{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin:0 20px 8px;padding:4px;background:var(--cw-surface-soft);border-radius:var(--cw-radius-pill)}.scope-option{display:flex;gap:7px;align-items:center;justify-content:center;border:0;background:transparent;border-radius:var(--cw-radius-pill);padding:8px 6px;font-size:14px;font-weight:550;color:var(--cw-muted);white-space:nowrap}.scope-option:hover{color:var(--cw-ink)}.scope-option[aria-pressed=true]{background:var(--cw-surface);color:var(--cw-ink);box-shadow:0 1px 3px #2024371f}.scope-option[aria-pressed=true] .count{background:var(--cw-accent-soft);color:var(--cw-accent)}.tabs{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;margin:0 20px 8px;padding:4px;border:1px solid var(--cw-line);border-radius:var(--cw-radius-pill)}.tab{display:flex;gap:7px;align-items:center;justify-content:center;border:0;border-radius:var(--cw-radius-pill);background:transparent;padding:7px 6px;color:var(--cw-muted);font-size:14px;font-weight:550;white-space:nowrap}.tab[aria-pressed=true]{color:var(--cw-on-inverse);background:var(--cw-inverse)}.tab[aria-pressed=true] .count{background:#ffffff2e;color:var(--cw-on-inverse)}.count{display:inline-block;background:var(--cw-surface-soft);min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:12px;line-height:20px;text-align:center}.search{position:relative;flex:1;min-width:0}.search>svg{position:absolute;left:15px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--cw-subtle);pointer-events:none}.search input{border:1px solid var(--cw-line);border-radius:var(--cw-radius-pill);background:var(--cw-canvas);width:100%;height:42px;padding:0 40px 0 40px;color:var(--cw-ink);font-size:14px}.search input::placeholder{color:var(--cw-subtle)}.search input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}.search-clear{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:28px;height:28px;color:var(--cw-muted)}.search-clear svg{width:14px;height:14px}.people{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 20px 10px}.scope-row,.search-row{display:flex;align-items:center;gap:10px;margin:0 20px 10px}.scope-row .scope{flex:1;margin:0}.filter-toggle{position:relative}.filter-toggle.on{border-color:var(--cw-accent-line);color:var(--cw-accent);background:var(--cw-accent-soft)}.filter-toggle .count{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;line-height:18px;font-size:11px;background:var(--cw-accent);color:#fff;box-shadow:0 0 0 2px var(--cw-surface)}.filter-menu{margin:0 20px 10px;padding:14px;border:1px solid var(--cw-line);border-radius:20px;background:var(--cw-surface);box-shadow:var(--cw-shadow-sm)}.filter-group+.filter-group{margin-top:14px}.filter-group h3{margin:0 0 8px;font-size:11px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;color:var(--cw-muted)}.filter-options{display:flex;flex-wrap:wrap;gap:6px}.option-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;border:1px solid var(--cw-line);background:var(--cw-surface);color:var(--cw-ink-soft);border-radius:999px;padding:3px 3px 3px 11px;font-size:13px;font-weight:600;white-space:nowrap}.option-chip>span:first-child{overflow:hidden;text-overflow:ellipsis}.option-chip:hover{background:var(--cw-canvas)}.option-chip[aria-pressed=true]{background:var(--cw-accent-soft);border-color:var(--cw-accent-line);color:var(--cw-accent)}.option-chip[aria-pressed=true] .count{background:var(--cw-surface);color:var(--cw-accent)}.chip-label{overflow:hidden;text-overflow:ellipsis;max-width:160px}.chip-remove{width:22px;height:22px;border-radius:50%;color:var(--cw-accent)}.chip-remove svg{width:12px;height:12px}.chip-remove:hover{background:var(--cw-surface)}.copy-field{display:flex;align-items:center;gap:6px;border:1px solid var(--cw-line);border-radius:var(--cw-radius-pill);padding:4px 4px 4px 14px;background:var(--cw-canvas)}.copy-field code{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--cw-ink)}.copy-button{width:32px;height:32px;border-radius:50%;color:var(--cw-muted)}.copy-button svg{width:15px;height:15px}.copy-button.done{color:var(--cw-resolved)}.mcp{display:flex;flex-direction:column;gap:18px}.mcp-intro{display:flex;gap:12px;align-items:flex-start;padding:14px;border:1px solid var(--cw-accent-line);border-radius:20px;background:var(--cw-accent-soft)}.mcp-badge{display:grid;place-items:center;flex-shrink:0;width:36px;height:36px;border-radius:50%;background:var(--cw-accent);color:#fff}.mcp-badge svg{width:18px;height:18px}.mcp-intro h3{margin:0 0 3px;font-size:15px;font-weight:650;color:var(--cw-ink)}.mcp-intro p{margin:0;font-size:13px;line-height:1.5;color:var(--cw-ink-soft)}.mcp-block h4{margin:0 0 8px;font-size:11px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;color:var(--cw-muted)}.mcp-steps{list-style:none;counter-reset:step;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}.mcp-steps li{counter-increment:step;display:flex;align-items:center;gap:10px;font-size:13.5px;line-height:1.45;color:var(--cw-ink-soft)}.mcp-steps li::before{content:counter(step);display:grid;place-items:center;flex-shrink:0;width:24px;height:24px;border-radius:50%;background:var(--cw-surface-soft);color:var(--cw-ink);font-size:12px;font-weight:650}.mcp-prompts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.mcp-prompts li{padding:9px 14px;border:1px solid var(--cw-line);border-radius:14px;background:var(--cw-canvas);font-size:13px;line-height:1.45;color:var(--cw-ink-soft)}.mcp-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding-top:2px}.mcp-foot p{margin:0;font-size:12px;color:var(--cw-muted)}.mcp-link{display:inline-flex;align-items:center;gap:6px;text-decoration:none;font-weight:600;padding:8px 14px;font-size:13px}.mcp-link svg{width:14px;height:14px}.filter-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cw-accent-line);background:var(--cw-accent-soft);color:var(--cw-accent);border-radius:999px;padding:3px 3px 3px 11px;font-size:13px;font-weight:600;white-space:nowrap;max-width:100%}.filter-chip .count{background:var(--cw-surface);color:var(--cw-accent)}.filter-chip:hover{background:var(--cw-canvas)}.filter-chip[aria-pressed=true]{background:var(--cw-accent-soft);border-color:var(--cw-accent-line);color:var(--cw-accent)}.filter-chip[aria-pressed=true] .count{background:var(--cw-surface);color:var(--cw-accent)}.select{position:relative;display:inline-flex;min-width:0;max-width:220px;color:var(--cw-muted)}.select::after{content:'';position:absolute;right:12px;top:50%;width:6px;height:6px;border:solid currentColor;border-width:0 1.5px 1.5px 0;transform:translateY(-70%) rotate(45deg);pointer-events:none}.select select{appearance:none;-webkit-appearance:none;border:1px solid var(--cw-line);background:var(--cw-surface);color:var(--cw-ink-soft);border-radius:999px;padding:5px 30px 5px 11px;font-size:13px;font-weight:600;line-height:18px;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.select select:hover{background:var(--cw-canvas)}.select.chosen{color:var(--cw-accent)}.select.chosen select{background:var(--cw-accent-soft);border-color:var(--cw-accent-line);color:var(--cw-accent)}.select option{color:var(--cw-ink);background:var(--cw-surface)}.link-button{margin-left:auto;border:0;background:transparent;color:var(--cw-accent);font-size:13px;font-weight:600;padding:4px 2px;white-space:nowrap}.link-button:hover{text-decoration:underline}mark.hit{background:color-mix(in srgb,var(--cw-accent) 22%,transparent);color:inherit;border-radius:3px;padding:0 1px;-webkit-box-decoration-break:clone;box-decoration-break:clone}.note-match{display:flex;align-items:center;gap:5px;margin:4px 0 0 31px;font-size:12.5px;line-height:1.5;color:var(--cw-muted);min-width:0}.note-match svg{width:12px;height:12px}.note-match-label{font-weight:600;color:var(--cw-ink-soft);white-space:nowrap;flex-shrink:0;max-width:45%;overflow:hidden;text-overflow:ellipsis}.note-match-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.list{flex:1;overflow:auto;overscroll-behavior:contain;padding:8px 12px 16px;min-height:0}.list-title{display:flex;align-items:center;gap:8px;padding:12px 8px 6px;color:var(--cw-muted);font-size:12px;letter-spacing:1.15px;text-transform:uppercase;font-weight:600}.page-path{text-transform:none;letter-spacing:0;font-size:13px;color:var(--cw-ink-soft);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.page-name{text-transform:none;letter-spacing:0;font-size:13px;font-weight:600;color:var(--cw-ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.here{flex-shrink:0;padding:0 6px;border-radius:5px;background:var(--cw-accent-soft);color:var(--cw-accent);font-size:11px;letter-spacing:.4px;line-height:18px}.go{margin-left:auto;display:inline-flex;align-items:center;gap:4px;color:var(--cw-accent);font-weight:600}.note.current{background:var(--cw-accent-soft);border-color:var(--cw-accent-line)}.note{display:block;text-align:left;width:100%;border:1px solid transparent;background:var(--cw-surface);color:var(--cw-ink);border-radius:10px;padding:9px 10px;margin-bottom:2px}.note .note-header .via{color:var(--cw-muted);font-size:10px;max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.note-header{margin-bottom:4px;gap:7px}.note .avatar{width:24px;height:24px;font-size:11px}.note .name{font-size:13px}.note .time{margin-left:0}.note-meta{margin-left:auto;display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--cw-muted);white-space:nowrap;flex-shrink:0}.note-meta svg{width:12px;height:12px}.note:hover{background:var(--cw-canvas);border-color:var(--cw-line)}.note-header{display:flex;align-items:center;gap:8px;margin-bottom:9px}.avatar{display:inline-grid;place-items:center;width:29px;height:29px;border-radius:50%;background:var(--cw-avatar-bg);color:var(--cw-avatar-ink);font-size:12px;font-weight:700;flex-shrink:0;overflow:hidden}.avatar img{height:100%;width:100%;object-fit:cover}.name{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.time{font-size:12px;color:var(--cw-muted);margin-left:auto;white-space:nowrap}.note-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin:0 0 0 31px;line-height:1.5;font-size:14px;color:var(--cw-ink-soft);overflow-wrap:anywhere}.note-footer{display:flex;gap:6px;align-items:center;margin:6px 0 0 31px;color:var(--cw-muted);font-size:12px}.note-footer svg{width:12px;height:12px}.note-number{color:var(--cw-accent);font-weight:700}.status{display:inline-flex;align-items:center;gap:4px}.status.resolved{color:var(--cw-resolved-text)}.status-dot{width:5px;height:5px;background:var(--cw-accent);border-radius:50%;display:inline-block}.empty{height:100%;min-height:230px;display:flex;align-items:center;justify-content:center;text-align:center;padding:32px 16px}.empty-icon{position:relative;display:grid;place-items:center;margin:0 auto 18px;width:64px;height:64px;border:1px solid var(--cw-line);border-radius:21px;transform:rotate(-7deg);background:var(--cw-canvas);color:var(--cw-accent)}.empty-icon svg{width:27px;height:27px}.empty h3{font-size:17px;letter-spacing:-.25px;margin:0 0 8px}.empty p{font-size:14px;color:var(--cw-muted);line-height:1.7;margin:0 auto 18px;max-width:230px}.footer{display:flex;align-items:center;gap:9px;border-top:1px solid var(--cw-line);padding:14px 20px;background:var(--cw-canvas)}.footer .avatar{width:25px;height:25px;font-size:11px}.identity{border:0;background:transparent;text-align:left;padding:0;min-width:0;flex:1}.identity strong{display:block;font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.identity small{font-size:12px;color:var(--cw-muted)}.storage-label{font-size:11px;color:var(--cw-muted);text-align:right;line-height:1.5;max-width:105px}.storage-dot{display:inline-block;width:5px;height:5px;background:var(--cw-resolved);border-radius:50%;margin-right:4px}.error{padding:12px 16px;margin:12px 20px;border:1px solid var(--cw-danger-line);background:var(--cw-danger-soft);color:var(--cw-danger);border-radius:10px;font-size:14px;overflow-wrap:anywhere}.error button{display:block;margin-top:8px}.notice{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:1;background:var(--cw-inverse);color:var(--cw-on-inverse);border:1px solid #ffffff20;border-radius:12px;padding:12px 15px;display:flex;align-items:center;gap:12px;font-size:14px;box-shadow:0 6px 30px #0002;pointer-events:auto;max-width:calc(100vw - 32px)}.notice kbd{font-size:12px;color:var(--cw-inverse-muted);border:1px solid #ffffff30;border-radius:4px;padding:2px 5px}.notice button{color:var(--cw-on-inverse)}.notice.beside-panel{left:calc(50% - 200px)}.notice.beside-panel.left{left:calc(50% + 200px)}.pins{position:fixed;inset:0;pointer-events:none}.pin{position:absolute;width:32px;height:36px;transform:translate(-50%,-100%);border:2px solid #fff;border-radius:15px 15px 15px 4px;background:var(--cw-accent);color:#fff;box-shadow:var(--cw-shadow-sm);display:grid;place-items:center;font-size:13px;font-weight:650;pointer-events:auto;transition:scale .15s}.pin:hover,.pin[aria-pressed=true]{scale:1.12}.pin.resolved{background:var(--cw-resolved)}.pin .pin-photo{width:100%;height:100%;border-radius:inherit;background:transparent;color:inherit;font-size:inherit;box-shadow:none}.pin.resolved .pin-photo img{filter:grayscale(1);opacity:.6}.pin.highlight{animation:pulse 1s ease-out 2}.pin.hovered{animation:pulse 1s ease-out infinite;scale:1.12;opacity:1!important}.focus-ring.hover-ring{animation:none;border-style:dashed}.focus-ring{position:absolute;border:2px solid var(--cw-accent);border-radius:var(--cw-radius);background:color-mix(in srgb,var(--cw-accent) 6%,transparent);pointer-events:none;animation:enter .16s ease-out}.pick-ring{border-style:dashed;animation:none;transition:left .08s,top .08s,width .08s,height .08s}.pin:focus-visible{outline-offset:3px}.pin.preview{pointer-events:none;opacity:.7}.pin.movable{cursor:grab;touch-action:none}.pins.focused .pin:not([aria-pressed=true]):not(.dragging){opacity:.3;transition:opacity .15s,scale .15s}.pins.focused .pin:hover,.pins.focused .pin:focus-visible{opacity:1}.pin.dragging{cursor:grabbing;scale:1.15;pointer-events:none;box-shadow:var(--cw-shadow);transition:none;z-index:1}.detail-hint{margin:16px 20px 12px;color:var(--cw-muted);font-size:14px;line-height:1.6}.compose{padding:0 20px 20px}.compose textarea,.reply-bar textarea{scrollbar-width:none}
|
|
755
769
|
.compose textarea::-webkit-scrollbar,.reply-bar textarea::-webkit-scrollbar{display:none}
|
|
756
770
|
.compose textarea{width:100%;display:block;resize:vertical;min-height:120px;max-height:40vh;border:1px solid var(--cw-line);border-radius:var(--cw-radius);padding:12px;color:var(--cw-ink);background:var(--cw-canvas);font-size:16px;line-height:1.6}.compose textarea::placeholder{color:var(--cw-subtle)}.compose-bar{display:flex;align-items:center;gap:10px;justify-content:space-between;margin-top:12px}.compose-hint{font-size:11px;color:var(--cw-muted)}.thread-body{overflow:auto;overscroll-behavior:contain;flex:1;min-height:0;padding:18px 20px 4px}.message{padding:16px 0;border-bottom:1px solid var(--cw-line)}.message:first-child{padding-top:0}.message:last-child{border:0}.message-text{font-size:16px;line-height:1.7;white-space:pre-wrap;overflow-wrap:anywhere;margin:10px 0 0 37px}.thread-actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 20px;border-top:1px solid var(--cw-line)}.resolve{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;font-weight:600}.resolve svg{width:16px;height:16px;color:var(--cw-resolved)}.resolve:hover{border-color:var(--cw-resolved);background:var(--cw-resolved-soft);color:var(--cw-resolved-text)}.resolve.done svg{color:var(--cw-muted)}.resolve.done:hover{border-color:var(--cw-line-strong);background:var(--cw-canvas);color:var(--cw-ink)}.confirm-delete{display:flex;align-items:center;gap:8px}.danger-button{display:inline-flex;align-items:center;gap:6px;border:0;border-radius:var(--cw-radius-pill);background:var(--cw-danger);color:#fff;padding:10px 12px;font-size:14px;font-weight:600;white-space:nowrap}.danger-button svg{width:15px;height:15px}.detail-head{padding:12px 20px 16px;border-bottom:1px solid var(--cw-line)}.detail-bar{display:flex;align-items:center;justify-content:space-between;margin:0 -8px 12px -10px}.back-link{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;border-radius:var(--cw-radius-sm);padding:6px 10px;font-size:14px;font-weight:600;color:var(--cw-muted)}.back-link:hover{background:var(--cw-surface-soft);color:var(--cw-ink)}.back-link svg{width:16px;height:16px}.detail-title{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.detail-title h2{font-size:20px;line-height:1.3;letter-spacing:-.4px;margin:0;font-weight:650}.detail-head .subtext{white-space:normal}.chip{display:inline-flex;align-items:center;gap:6px;padding:1px 9px;border-radius:999px;background:var(--cw-accent-soft);color:var(--cw-accent);font-size:12px;font-weight:600;line-height:20px}.chip svg{width:12px;height:12px}.chip.resolved{background:var(--cw-resolved-soft);color:var(--cw-resolved-text)}.thread .compose{padding-top:12px;border-top:1px solid var(--cw-line)}.thread textarea{min-height:88px}.anchor-status{margin:0 20px 12px;padding:12px;background:var(--cw-canvas);border:1px solid var(--cw-line);border-radius:10px;color:var(--cw-muted);font-size:13px;line-height:1.55}.anchor-head{display:flex;align-items:flex-start;gap:10px;margin-bottom:8px}.anchor-icon{display:grid;place-items:center;flex-shrink:0;width:28px;height:28px;border-radius:8px;background:var(--cw-warning-soft);color:var(--cw-warning)}.anchor-status.removed .anchor-icon{background:var(--cw-danger-soft);color:var(--cw-danger)}.anchor-icon svg{width:16px;height:16px}.anchor-titles{display:flex;flex-direction:column;min-width:0}.anchor-titles strong{color:var(--cw-ink);font-size:13px;font-weight:650;line-height:1.35}.anchor-target{color:var(--cw-ink-soft);overflow-wrap:anywhere}.identity-form{padding:0 20px 20px}.identity-form label{font-size:14px;font-weight:600;display:block;margin-bottom:7px}.identity-form input{width:100%;border:1px solid var(--cw-line);background:var(--cw-canvas);color:var(--cw-ink);padding:10px;border-radius:var(--cw-radius);margin-bottom:14px}.identity-form p{font-size:13px;color:var(--cw-muted);line-height:1.7;margin:0 0 14px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
757
771
|
@keyframes enter{from{opacity:0;translate:0 5px}to{opacity:1;translate:0 0}}@keyframes slide-in{from{opacity:0;translate:var(--cw-slide,24px) 0}to{opacity:1;translate:0 0}}@keyframes pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--cw-accent) 55%,transparent)}100%{box-shadow:0 0 0 16px transparent}}@keyframes spin{to{rotate:360deg}}@keyframes toast-out{to{opacity:0;translate:0 6px}}
|
|
758
772
|
.toasts{position:fixed;right:24px;bottom:88px;width:320px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:8px;pointer-events:none}.toasts.left{right:auto;left:24px}.toasts.top{bottom:auto;top:88px;flex-direction:column-reverse}.toasts.beside-panel{right:424px;bottom:24px;max-width:calc(100vw - 448px)}.toasts.beside-panel.left{right:auto;left:424px}.toasts.beside-panel.top{top:24px;bottom:auto}.toast{display:flex;gap:10px;align-items:flex-start;background:var(--cw-inverse);color:var(--cw-on-inverse);border:1px solid #ffffff20;border-radius:var(--cw-radius-md,12px);padding:12px 14px;box-shadow:var(--cw-shadow,var(--cw-shadow));animation:enter .18s ease-out}.toast.leaving{animation:toast-out .2s ease-in forwards}.toast-icon{background:var(--cw-accent-soft,var(--cw-accent-soft));color:var(--cw-accent)}.toast-icon svg{width:15px;height:15px}.toast-body{min-width:0;flex:1}.toast-title{margin:0;font-size:14px;line-height:1.4;color:var(--cw-inverse-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-title strong{color:var(--cw-on-inverse);font-weight:600}.toast-number{margin-left:6px;padding:0 6px;border-radius:5px;background:var(--cw-accent-soft,var(--cw-accent-soft));color:var(--cw-accent);font-size:12px;font-weight:650;line-height:18px;display:inline-block}.toast-text{margin:3px 0 0;font-size:14px;line-height:1.5;color:#fff;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
|
|
759
773
|
.popover{position:fixed;left:0;top:0;z-index:1;width:320px;max-height:min(560px,calc(100vh - 24px));display:flex;flex-direction:column;background:var(--cw-surface);border:1px solid var(--cw-line);border-radius:var(--cw-radius-lg);box-shadow:var(--cw-shadow);pointer-events:auto;color:var(--cw-ink)}.popover>.detail{min-height:0}.popover .detail.entering{animation:pop .18s cubic-bezier(.2,.9,.3,1.15)}@keyframes pop{from{opacity:0;transform:translateY(6px) scale(.97)}}.popover.sheet{left:8px;right:8px;top:auto;bottom:8px;width:auto;max-height:75vh}
|
|
760
|
-
.card-head .detail-bar{margin:0;padding:12px 12px 0 10px}.pop-head{position:relative;display:flex;align-items:center;gap:10px;padding:14px 10px 12px 14px}.panel .pop-head{padding:12px 14px 12px 20px}.pop-
|
|
774
|
+
.card-head .detail-bar{margin:0;padding:12px 12px 0 10px}.pop-head{position:relative;display:flex;align-items:center;gap:10px;padding:14px 10px 12px 14px}.panel .pop-head{padding:12px 14px 12px 20px}.pop-title{flex:1;min-width:0}.pop-title h2{margin:0;font-size:14px;font-weight:650;letter-spacing:-.2px;line-height:1.35}.pop-sub{margin:0;font-size:12px;line-height:1.5;color:var(--cw-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pop-head .icon-button{width:28px;height:28px;color:var(--cw-muted)}.pop-head .icon-button:hover{background:var(--cw-surface-soft);color:var(--cw-ink)}.pop-head .icon-button svg{width:16px;height:16px}.pop-head .more svg{stroke-width:3.2}
|
|
761
775
|
.pop-menu{position:absolute;top:46px;right:10px;z-index:2;min-width:190px;padding:4px;background:var(--cw-surface);border:1px solid var(--cw-line);border-radius:var(--cw-radius-md);box-shadow:var(--cw-shadow)}.menu-item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;border:0;border-radius:8px;background:transparent;font-size:13px;text-align:left;color:var(--cw-ink)}.menu-item:hover{background:var(--cw-surface-soft)}.menu-item svg{width:15px;height:15px;color:var(--cw-muted)}.menu-item.danger,.menu-item.danger svg{color:var(--cw-danger)}
|
|
762
|
-
.thread .thread-body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:0 14px}.panel .thread .thread-body{padding:0 20px}.thread .message{padding:12px 0;border-top:1px solid var(--cw-line);border-bottom:0}.thread .message:first-child{padding-top:12px}.thread .note-header{gap:8px;margin:0}.thread .avatar{width:
|
|
763
|
-
.thread-actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;border-top:1px solid var(--cw-line)}.panel .thread-actions{padding:12px 20px}.reply-bar{flex:1;min-width:0;display:flex;align-items:flex-end;gap:8px}.reply-bar textarea{flex:1;min-width:0;min-height:32px;max-height:120px;resize:none;padding:6px 10px;border:1px solid var(--cw-line-strong);border-radius:var(--cw-radius);background:var(--cw-surface);color:var(--cw-ink);font-size:13px;line-height:1.45;overflow:auto}.reply-bar textarea:focus-visible{outline:0;border-color:var(--cw-accent);box-shadow:0 0 0 3px var(--cw-accent-line)}.reply-bar textarea::placeholder{color:var(--cw-subtle)}.thread-actions .primary{padding:7px 12px;font-size:13px;border-radius:var(--cw-radius-pill);white-space:nowrap}.thread-actions .primary svg{width:14px;height:14px}.thread-actions .primary.
|
|
776
|
+
.thread .thread-body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:0 14px}.panel .thread .thread-body{padding:0 20px}.thread .message{padding:12px 0;border-top:1px solid var(--cw-line);border-bottom:0}.thread .message:first-child{padding-top:12px}.thread .note-header{gap:8px;margin:0}.thread .avatar{width:30px;height:30px;font-size:11px}.thread .name{font-size:14px}.thread .time{font-size:12px}.thread .message-text{margin:4px 0 0 38px;font-size:14px;line-height:1.55;color:var(--cw-ink-soft)}.panel .thread .message-text{font-size:15px}
|
|
777
|
+
.thread-actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;border-top:1px solid var(--cw-line)}.panel .thread-actions{padding:12px 20px}.reply-bar{flex:1;min-width:0;display:flex;align-items:flex-end;gap:8px}.reply-bar textarea{flex:1;min-width:0;min-height:32px;max-height:120px;resize:none;padding:6px 10px;border:1px solid var(--cw-line-strong);border-radius:var(--cw-radius);background:var(--cw-surface);color:var(--cw-ink);font-size:13px;line-height:1.45;overflow:auto}.reply-bar textarea:focus-visible{outline:0;border-color:var(--cw-accent);box-shadow:0 0 0 3px var(--cw-accent-line)}.reply-bar textarea::placeholder{color:var(--cw-subtle)}.thread-actions .primary{padding:7px 12px;font-size:13px;border-radius:var(--cw-radius-pill);white-space:nowrap}.thread-actions .primary svg{width:14px;height:14px}.thread-actions .primary.send{width:34px;height:34px;padding:0;border-radius:50%;flex-shrink:0}.thread-actions .primary.send svg{width:16px;height:16px}.thread-actions .primary.resolve{border:0;background:var(--cw-accent);color:#fff;transition:background .16s}.thread-actions .primary.resolve:hover{border:0;background:var(--cw-resolved);color:#fff;filter:none}.thread-actions .primary.resolve svg{color:currentColor}.thread-actions .status{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:600;color:var(--cw-resolved-text)}.thread-actions .status svg{width:14px;height:14px}.thread-actions .reopen{padding:6px 12px;font-size:13px;font-weight:500}
|
|
764
778
|
.popover .anchor-status,.popover .error{margin:0 14px 10px}.new-comment .compose{margin:0 14px;padding:14px 0;border-top:1px solid var(--cw-line)}.panel .new-comment .compose{margin:0 20px;padding:16px 0 20px}.popover .compose textarea:focus-visible{outline:0;border-color:var(--cw-accent);box-shadow:0 0 0 3px var(--cw-accent-line)}.popover .new-comment .compose textarea{min-height:76px;font-size:14px}.popover .compose-hint{font-size:11px}
|
|
765
779
|
.pin.below{transform:translate(-50%,0);border-radius:4px 15px 15px 15px}
|
|
766
780
|
.pick-ring{border-style:solid;background:transparent}.head-actions{display:flex;align-items:center;gap:2px;margin-right:-6px}
|
|
@@ -781,7 +795,7 @@ const styles = `
|
|
|
781
795
|
.menu-note{display:flex;align-items:center;gap:2px;margin:4px 0 0;padding:8px 10px 4px;border-top:1px solid var(--cw-line);font-size:12px;color:var(--cw-muted)}
|
|
782
796
|
.footer{justify-content:space-between}
|
|
783
797
|
.footer-note{font-size:13px;color:var(--cw-muted)}
|
|
784
|
-
.panel:focus{outline:0}.settings-tabs{grid-template-columns:1fr 1fr;margin:0 0
|
|
798
|
+
.panel:focus{outline:0}.settings-tabs{grid-template-columns:1fr 1fr;margin:14px 0 0}.settings-body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:16px 20px 12px}
|
|
785
799
|
.setting-group{padding:14px 0 6px}
|
|
786
800
|
.setting-group+.setting-group{border-top:1px solid var(--cw-line)}
|
|
787
801
|
.setting-group h3{margin:0 0 6px;font-size:12px;font-weight:650;letter-spacing:1.1px;text-transform:uppercase;color:var(--cw-muted)}
|
|
@@ -1431,7 +1445,9 @@ function describeElement(el) {
|
|
|
1431
1445
|
}
|
|
1432
1446
|
return info;
|
|
1433
1447
|
}
|
|
1434
|
-
const
|
|
1448
|
+
const DATE_FILTERS = [["any", "Any Time"], ["today", "Today"], ["yesterday", "Yesterday"], ["week", "Last 7 Days"], ["month", "Last 30 Days"]];
|
|
1449
|
+
const REPLY_FILTERS = [["any", "Any"], ["without", "No Replies"], ["with", "With Replies"]];
|
|
1450
|
+
const DEFAULT_FILTERS = Object.freeze({ status: "open", query: "", mine: false, author: "", date: "any", replies: "any" });
|
|
1435
1451
|
const MAX_QUERY = 200, MAX_WORDS = 8;
|
|
1436
1452
|
function foldChar(char) {
|
|
1437
1453
|
return char.normalize("NFKD").replace(new RegExp("\\p{M}", "gu"), "").toLowerCase();
|
|
@@ -1520,13 +1536,26 @@ function isMine(note, userId) {
|
|
|
1520
1536
|
function statusMatches(comment, status) {
|
|
1521
1537
|
return status === "all" || (status === "resolved" ? comment.resolved : !comment.resolved);
|
|
1522
1538
|
}
|
|
1539
|
+
function lastActivity(comment) {
|
|
1540
|
+
return Math.max(new Date(comment.timestamp).getTime(), ...comment.replies.map((r) => new Date(r.timestamp).getTime()));
|
|
1541
|
+
}
|
|
1542
|
+
function dateMatches(comment, date2, now = /* @__PURE__ */ new Date()) {
|
|
1543
|
+
if (date2 === "any") return true;
|
|
1544
|
+
const at = lastActivity(comment), midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime(), day = 864e5;
|
|
1545
|
+
if (date2 === "today") return at >= midnight;
|
|
1546
|
+
if (date2 === "yesterday") return at >= new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1).getTime() && at < midnight;
|
|
1547
|
+
return at >= now.getTime() - (date2 === "week" ? 7 : 30) * day;
|
|
1548
|
+
}
|
|
1549
|
+
function repliesMatch(comment, replies) {
|
|
1550
|
+
return replies === "any" || replies === "with" === comment.replies.length > 0;
|
|
1551
|
+
}
|
|
1523
1552
|
function filterNotes(comments, filters, context) {
|
|
1524
1553
|
const words2 = parseQuery(filters.query);
|
|
1525
|
-
return comments.filter((c) => statusMatches(c, filters.status) && (!filters.mine || isMine(c, context.userId)) && (!filters.author || authorKey(c) === filters.author) && !!matchNote(c, words2, context));
|
|
1554
|
+
return comments.filter((c) => statusMatches(c, filters.status) && (!filters.mine || isMine(c, context.userId)) && (!filters.author || authorKey(c) === filters.author) && dateMatches(c, filters.date, context.now) && repliesMatch(c, filters.replies) && !!matchNote(c, words2, context));
|
|
1526
1555
|
}
|
|
1527
1556
|
function filterCounts(comments, filters, context) {
|
|
1528
1557
|
const words2 = parseQuery(filters.query);
|
|
1529
|
-
const searched = comments.filter((c) => !!matchNote(c, words2, context));
|
|
1558
|
+
const searched = comments.filter((c) => dateMatches(c, filters.date, context.now) && repliesMatch(c, filters.replies) && !!matchNote(c, words2, context));
|
|
1530
1559
|
const byPerson = (c) => (!filters.mine || isMine(c, context.userId)) && (!filters.author || authorKey(c) === filters.author);
|
|
1531
1560
|
const people = searched.filter(byPerson);
|
|
1532
1561
|
const inStatus = searched.filter((c) => statusMatches(c, filters.status));
|
|
@@ -1548,8 +1577,11 @@ function filterCounts(comments, filters, context) {
|
|
|
1548
1577
|
authors: [...authors.values()].sort((a, b) => a.name.localeCompare(b.name) || a.key.localeCompare(b.key))
|
|
1549
1578
|
};
|
|
1550
1579
|
}
|
|
1580
|
+
function filterCount(filters) {
|
|
1581
|
+
return Number(filters.mine || !!filters.author) + Number(filters.date !== "any") + Number(filters.replies !== "any");
|
|
1582
|
+
}
|
|
1551
1583
|
function filtersActive(filters) {
|
|
1552
|
-
return !!filters.query.trim() || filters
|
|
1584
|
+
return !!filters.query.trim() || filterCount(filters) > 0;
|
|
1553
1585
|
}
|
|
1554
1586
|
function clearFilters(comments) {
|
|
1555
1587
|
return { ...DEFAULT_FILTERS, status: comments.some((c) => !c.resolved) || !comments.length ? "open" : "all" };
|
|
@@ -1560,7 +1592,9 @@ function readFilters(value) {
|
|
|
1560
1592
|
status: v.status === "resolved" || v.status === "all" ? v.status : "open",
|
|
1561
1593
|
query: typeof v.query === "string" ? v.query.slice(0, MAX_QUERY) : "",
|
|
1562
1594
|
mine: v.mine === true,
|
|
1563
|
-
author: typeof v.author === "string" ? v.author.slice(0, 300) : ""
|
|
1595
|
+
author: typeof v.author === "string" ? v.author.slice(0, 300) : "",
|
|
1596
|
+
date: DATE_FILTERS.some(([d]) => d === v.date) ? v.date : "any",
|
|
1597
|
+
replies: REPLY_FILTERS.some(([r]) => r === v.replies) ? v.replies : "any"
|
|
1564
1598
|
};
|
|
1565
1599
|
}
|
|
1566
1600
|
const THEMES = ["light", "dark", "system"];
|
|
@@ -1677,6 +1711,9 @@ class Overlay {
|
|
|
1677
1711
|
/** Search, status, Mine and author for the list and the pins. Kept for this browser tab (session storage). */
|
|
1678
1712
|
__publicField(this, "filters", { ...DEFAULT_FILTERS });
|
|
1679
1713
|
__publicField(this, "scope", "page");
|
|
1714
|
+
/** Search and filters stay out of the way until the person opens them. */
|
|
1715
|
+
__publicField(this, "searchOpen", false);
|
|
1716
|
+
__publicField(this, "filterMenuOpen", false);
|
|
1680
1717
|
__publicField(this, "filterKey");
|
|
1681
1718
|
__publicField(this, "searchTimer");
|
|
1682
1719
|
__publicField(this, "launcherKey");
|
|
@@ -3023,7 +3060,7 @@ ${this.currentPage()}`;
|
|
|
3023
3060
|
heading(title, subtitle) {
|
|
3024
3061
|
const head = element("div", "head");
|
|
3025
3062
|
const brand = element("span", "brand-icon");
|
|
3026
|
-
brand.append(
|
|
3063
|
+
brand.append(logo());
|
|
3027
3064
|
const heading = element("div", "heading");
|
|
3028
3065
|
heading.append(element("h2", "", title));
|
|
3029
3066
|
if (subtitle) heading.append(element("p", "subtext", subtitle));
|
|
@@ -3123,6 +3160,15 @@ ${this.currentPage()}`;
|
|
|
3123
3160
|
this.render();
|
|
3124
3161
|
(_a = this.shadow.querySelector(".detail")) == null ? void 0 : _a.focus({ preventScroll: true });
|
|
3125
3162
|
}
|
|
3163
|
+
/** What a pin shows. "photo" falls back to initials for an author without a picture. */
|
|
3164
|
+
pinFace(label, name, url, n) {
|
|
3165
|
+
if (label === "photo" && url) {
|
|
3166
|
+
const face = avatar(name, url);
|
|
3167
|
+
face.classList.add("pin-photo");
|
|
3168
|
+
return [face];
|
|
3169
|
+
}
|
|
3170
|
+
return [document.createTextNode(label === "number" ? String(n) : initials(name))];
|
|
3171
|
+
}
|
|
3126
3172
|
/** Header of a detail view: a back link and close on one bar, then the title. It reads as a step inside the list. */
|
|
3127
3173
|
detailHeading(title, subtitle, extra) {
|
|
3128
3174
|
const wrap = element("div", "detail-head");
|
|
@@ -3138,7 +3184,7 @@ ${this.currentPage()}`;
|
|
|
3138
3184
|
return wrap;
|
|
3139
3185
|
}
|
|
3140
3186
|
/** Header of a comment card: icon, title, status line, a menu for less frequent actions, and close. */
|
|
3141
|
-
cardHeading(title, subtitle,
|
|
3187
|
+
cardHeading(title, subtitle, hint = "", remove) {
|
|
3142
3188
|
const wrap = element("div", "card-head");
|
|
3143
3189
|
if (this.docked) {
|
|
3144
3190
|
const bar = element("div", "detail-bar");
|
|
@@ -3149,8 +3195,6 @@ ${this.currentPage()}`;
|
|
|
3149
3195
|
}
|
|
3150
3196
|
const head = element("div", "pop-head");
|
|
3151
3197
|
if (hint) head.title = hint;
|
|
3152
|
-
const tile = element("span", "pop-icon");
|
|
3153
|
-
tile.append(icon(name));
|
|
3154
3198
|
const text = element("div", "pop-title");
|
|
3155
3199
|
text.append(element("h2", "", title), element("p", "pop-sub", subtitle));
|
|
3156
3200
|
const menu = element("div", "pop-menu");
|
|
@@ -3167,7 +3211,7 @@ ${this.currentPage()}`;
|
|
|
3167
3211
|
del.disabled = this.saving;
|
|
3168
3212
|
menu.append(del);
|
|
3169
3213
|
}
|
|
3170
|
-
head.append(
|
|
3214
|
+
head.append(text);
|
|
3171
3215
|
if (menu.childElementCount) {
|
|
3172
3216
|
const more = button("More actions", "icon-button more", () => {
|
|
3173
3217
|
}, "more");
|
|
@@ -3212,9 +3256,37 @@ ${this.currentPage()}`;
|
|
|
3212
3256
|
refresh.disabled = this.loading || this.saving;
|
|
3213
3257
|
toolbar.append(add, refresh);
|
|
3214
3258
|
panel.append(toolbar);
|
|
3215
|
-
panel.append(this.scopeBar());
|
|
3216
3259
|
const comments = this.scopeComments();
|
|
3217
|
-
|
|
3260
|
+
const scopeRow = element("div", "scope-row");
|
|
3261
|
+
scopeRow.append(this.scopeBar());
|
|
3262
|
+
if (comments.length || this.searchOpen) {
|
|
3263
|
+
const toggle = button(
|
|
3264
|
+
this.searchOpen ? "Close search and filters" : "Search and filter",
|
|
3265
|
+
"icon-button outlined search-toggle",
|
|
3266
|
+
() => this.toggleSearch(),
|
|
3267
|
+
this.searchOpen ? "close" : "search"
|
|
3268
|
+
);
|
|
3269
|
+
toggle.setAttribute("aria-expanded", String(this.searchOpen));
|
|
3270
|
+
toggle.setAttribute("aria-keyshortcuts", "/");
|
|
3271
|
+
toggle.dataset.focus = "search-toggle";
|
|
3272
|
+
scopeRow.append(toggle);
|
|
3273
|
+
}
|
|
3274
|
+
panel.append(scopeRow);
|
|
3275
|
+
if (this.searchOpen) {
|
|
3276
|
+
const row = element("div", "search-row");
|
|
3277
|
+
const active = filterCount(this.filters);
|
|
3278
|
+
const filter = button(active ? `Filters, ${active} on` : "Filters", `icon-button outlined filter-toggle${active ? " on" : ""}`, () => {
|
|
3279
|
+
this.filterMenuOpen = !this.filterMenuOpen;
|
|
3280
|
+
this.render();
|
|
3281
|
+
}, "filter");
|
|
3282
|
+
filter.setAttribute("aria-expanded", String(this.filterMenuOpen));
|
|
3283
|
+
filter.dataset.focus = "filter-toggle";
|
|
3284
|
+
if (active) filter.append(element("span", "count", String(active)));
|
|
3285
|
+
row.append(this.searchField(), filter);
|
|
3286
|
+
panel.append(row);
|
|
3287
|
+
if (this.filterMenuOpen) panel.append(this.filterMenu());
|
|
3288
|
+
panel.append(this.peopleBar());
|
|
3289
|
+
}
|
|
3218
3290
|
panel.append(this.statusTabs());
|
|
3219
3291
|
if (this.error) panel.append(this.errorBox());
|
|
3220
3292
|
panel.append(this.commentList(comments));
|
|
@@ -3224,12 +3296,15 @@ ${this.currentPage()}`;
|
|
|
3224
3296
|
const target = event.composedPath()[0];
|
|
3225
3297
|
if (event.key !== "/" || event.ctrlKey || event.metaKey || event.altKey || event.isComposing) return;
|
|
3226
3298
|
if (target instanceof Element && target.closest("input,textarea,select")) return;
|
|
3227
|
-
|
|
3228
|
-
if (
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3299
|
+
event.preventDefault();
|
|
3300
|
+
if (!this.searchOpen) {
|
|
3301
|
+
this.searchOpen = true;
|
|
3302
|
+
this.saveFilters();
|
|
3303
|
+
this.render();
|
|
3232
3304
|
}
|
|
3305
|
+
const field = this.shadow.querySelector(".search input");
|
|
3306
|
+
field == null ? void 0 : field.focus();
|
|
3307
|
+
field == null ? void 0 : field.select();
|
|
3233
3308
|
});
|
|
3234
3309
|
return panel;
|
|
3235
3310
|
}
|
|
@@ -3243,12 +3318,13 @@ ${this.currentPage()}`;
|
|
|
3243
3318
|
if (!saved) return;
|
|
3244
3319
|
this.filters = readFilters(saved);
|
|
3245
3320
|
this.scope = saved.scope === "all" ? "all" : "page";
|
|
3321
|
+
this.searchOpen = saved.searchOpen === true || filtersActive(this.filters);
|
|
3246
3322
|
} catch {
|
|
3247
3323
|
}
|
|
3248
3324
|
}
|
|
3249
3325
|
saveFilters() {
|
|
3250
3326
|
try {
|
|
3251
|
-
sessionStorage.setItem(this.filterKey, JSON.stringify({ ...this.filters, scope: this.scope }));
|
|
3327
|
+
sessionStorage.setItem(this.filterKey, JSON.stringify({ ...this.filters, scope: this.scope, searchOpen: this.searchOpen }));
|
|
3252
3328
|
} catch {
|
|
3253
3329
|
}
|
|
3254
3330
|
}
|
|
@@ -3275,6 +3351,7 @@ ${this.currentPage()}`;
|
|
|
3275
3351
|
};
|
|
3276
3352
|
swap(".scope", this.scopeBar());
|
|
3277
3353
|
swap(".people", this.peopleBar());
|
|
3354
|
+
swap(".filter-menu", this.filterMenu());
|
|
3278
3355
|
swap(".tabs", this.statusTabs());
|
|
3279
3356
|
swap(".list", this.commentList(comments));
|
|
3280
3357
|
this.pins.replaceChildren();
|
|
@@ -3345,38 +3422,83 @@ ${this.currentPage()}`;
|
|
|
3345
3422
|
search.append(icon("search"), input, clear);
|
|
3346
3423
|
return search;
|
|
3347
3424
|
}
|
|
3348
|
-
/**
|
|
3425
|
+
/** Opens search and filters, or closes them and clears what they filtered. The page tabs and status stay. */
|
|
3426
|
+
toggleSearch() {
|
|
3427
|
+
var _a;
|
|
3428
|
+
this.searchOpen = !this.searchOpen;
|
|
3429
|
+
this.filterMenuOpen = false;
|
|
3430
|
+
if (!this.searchOpen) this.filters = { ...DEFAULT_FILTERS, status: this.filters.status };
|
|
3431
|
+
this.saveFilters();
|
|
3432
|
+
this.render();
|
|
3433
|
+
if (!this.searchOpen) this.announceResults();
|
|
3434
|
+
if (this.searchOpen) (_a = this.shadow.querySelector(".search input")) == null ? void 0 : _a.focus();
|
|
3435
|
+
}
|
|
3436
|
+
/** How many notes a filter choice shows with the other filters and the search. */
|
|
3437
|
+
countWith(patch) {
|
|
3438
|
+
return filterNotes(this.scopeComments(), { ...this.filters, ...patch }, this.filterContext()).length;
|
|
3439
|
+
}
|
|
3440
|
+
/** The people a note can be filtered by: Mine for a signed-in person, then each author. */
|
|
3441
|
+
personChoices() {
|
|
3442
|
+
const hosted = this.storage instanceof HostedAdapter ? this.storage : null;
|
|
3443
|
+
const counts = filterCounts(this.scopeComments(), this.filters, this.filterContext());
|
|
3444
|
+
const choices = [{ label: "Anyone", patch: { mine: false, author: "" }, on: !this.filters.mine && !this.filters.author }];
|
|
3445
|
+
const mine = !hosted || hosted.signedIn;
|
|
3446
|
+
if (mine) choices.push({ label: "Mine", patch: { mine: true, author: "" }, on: this.filters.mine });
|
|
3447
|
+
const authors = counts.authors.filter((a) => !mine || a.key !== `id:${this.user.id}` || this.filters.author === a.key);
|
|
3448
|
+
if (this.filters.author && !authors.some((a) => a.key === this.filters.author)) authors.push({ key: this.filters.author, name: "Earlier author", count: 0 });
|
|
3449
|
+
for (const a of authors) choices.push({ label: a.name, patch: { mine: false, author: a.key }, on: this.filters.author === a.key });
|
|
3450
|
+
return choices;
|
|
3451
|
+
}
|
|
3452
|
+
/** The filter menu: person, last activity and replies. Each choice shows how many notes it leaves. */
|
|
3453
|
+
filterMenu() {
|
|
3454
|
+
const menu = element("div", "filter-menu");
|
|
3455
|
+
menu.setAttribute("role", "group");
|
|
3456
|
+
menu.setAttribute("aria-label", "Filters");
|
|
3457
|
+
const group = (title, choices, key2) => {
|
|
3458
|
+
const section = element("div", "filter-group");
|
|
3459
|
+
section.setAttribute("role", "group");
|
|
3460
|
+
section.setAttribute("aria-label", title);
|
|
3461
|
+
section.append(element("h3", "", title));
|
|
3462
|
+
const options = element("div", "filter-options");
|
|
3463
|
+
choices.forEach((choice, i) => {
|
|
3464
|
+
const count = this.countWith(choice.patch);
|
|
3465
|
+
const option = button(choice.label, "option-chip", () => this.setFilters(choice.patch));
|
|
3466
|
+
option.setAttribute("aria-pressed", String(choice.on));
|
|
3467
|
+
option.setAttribute("aria-label", `${title}: ${choice.label}, ${count} ${count === 1 ? "note" : "notes"}`);
|
|
3468
|
+
option.dataset.focus = `filter-${key2}-${i}`;
|
|
3469
|
+
option.append(element("span", "count", String(count)));
|
|
3470
|
+
options.append(option);
|
|
3471
|
+
});
|
|
3472
|
+
section.append(options);
|
|
3473
|
+
menu.append(section);
|
|
3474
|
+
};
|
|
3475
|
+
group("Person", this.personChoices(), "person");
|
|
3476
|
+
group("Last Activity", DATE_FILTERS.map(([date2, label]) => ({ label, patch: { date: date2 }, on: this.filters.date === date2 })), "date");
|
|
3477
|
+
group("Replies", REPLY_FILTERS.map(([replies, label]) => ({ label, patch: { replies }, on: this.filters.replies === replies })), "replies");
|
|
3478
|
+
return menu;
|
|
3479
|
+
}
|
|
3480
|
+
/** The filters in use, each as a chip with its count and a remove button. Hidden when none is set. */
|
|
3349
3481
|
peopleBar() {
|
|
3350
3482
|
const bar = element("div", "people");
|
|
3351
3483
|
bar.setAttribute("role", "group");
|
|
3352
|
-
bar.setAttribute("aria-label", "
|
|
3353
|
-
const
|
|
3354
|
-
const
|
|
3355
|
-
|
|
3356
|
-
const
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
bar.append(
|
|
3363
|
-
}
|
|
3364
|
-
const
|
|
3365
|
-
if (
|
|
3366
|
-
if (
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
select.dataset.focus = "filter-author";
|
|
3371
|
-
select.append(new Option("All Authors", ""));
|
|
3372
|
-
for (const author of authors) select.append(new Option(`${author.name} (${author.count})`, author.key));
|
|
3373
|
-
select.value = this.filters.author;
|
|
3374
|
-
select.addEventListener("change", () => this.setFilters({ author: select.value, mine: false }));
|
|
3375
|
-
wrap.append(select);
|
|
3376
|
-
bar.append(wrap);
|
|
3377
|
-
}
|
|
3378
|
-
if (filtersActive(this.filters)) {
|
|
3379
|
-
const clear = button("Clear Filters", "link-button", () => this.clearAllFilters());
|
|
3484
|
+
bar.setAttribute("aria-label", "Filters in use");
|
|
3485
|
+
const none = { mine: false, author: "", date: "any", replies: "any" };
|
|
3486
|
+
const chip = (label, reset, key2) => {
|
|
3487
|
+
const own = Object.fromEntries(Object.entries(none).filter(([k]) => !(k in reset)));
|
|
3488
|
+
const count = this.countWith(own);
|
|
3489
|
+
const item = element("span", "filter-chip");
|
|
3490
|
+
item.append(element("span", "chip-label", label), element("span", "count", String(count)));
|
|
3491
|
+
const remove = button(`Remove filter: ${label}`, "icon-button chip-remove", () => this.setFilters(reset), "close");
|
|
3492
|
+
remove.dataset.focus = `chip-${key2}`;
|
|
3493
|
+
item.append(remove);
|
|
3494
|
+
bar.append(item);
|
|
3495
|
+
};
|
|
3496
|
+
const person = this.personChoices().find((c) => c.on && c.label !== "Anyone");
|
|
3497
|
+
if (person) chip(person.label, { mine: false, author: "" }, "person");
|
|
3498
|
+
if (this.filters.date !== "any") chip(DATE_FILTERS.find(([d]) => d === this.filters.date)[1], { date: "any" }, "date");
|
|
3499
|
+
if (this.filters.replies !== "any") chip(REPLY_FILTERS.find(([r]) => r === this.filters.replies)[1], { replies: "any" }, "replies");
|
|
3500
|
+
if (filterCount(this.filters) > 1) {
|
|
3501
|
+
const clear = button("Clear All", "link-button", () => this.setFilters({ mine: false, author: "", date: "any", replies: "any" }));
|
|
3380
3502
|
clear.dataset.focus = "filter-clear";
|
|
3381
3503
|
bar.append(clear);
|
|
3382
3504
|
}
|
|
@@ -3602,7 +3724,7 @@ ${this.currentPage()}`;
|
|
|
3602
3724
|
}
|
|
3603
3725
|
newCommentForm() {
|
|
3604
3726
|
const view = this.view("New comment", "new-comment");
|
|
3605
|
-
view.append(this.cardHeading("New comment", `Commenting as ${this.user.name}
|
|
3727
|
+
view.append(this.cardHeading("New comment", `Commenting as ${this.user.name}`));
|
|
3606
3728
|
if (this.error) view.append(this.errorBox());
|
|
3607
3729
|
view.append(this.composer(false));
|
|
3608
3730
|
return view;
|
|
@@ -3614,7 +3736,6 @@ ${this.currentPage()}`;
|
|
|
3614
3736
|
view.append(this.cardHeading(
|
|
3615
3737
|
`Note #${this.pageComments().indexOf(comment) + 1}`,
|
|
3616
3738
|
comment.resolved ? "Resolved conversation" : "Open conversation",
|
|
3617
|
-
"comment",
|
|
3618
3739
|
this.canMove(comment) ? "Drag its pin to move it." : "",
|
|
3619
3740
|
canDelete ? () => this.deleteNote(comment) : void 0
|
|
3620
3741
|
));
|
|
@@ -3649,7 +3770,7 @@ ${this.currentPage()}`;
|
|
|
3649
3770
|
field.value = this.replyText;
|
|
3650
3771
|
field.dataset.focus = "reply";
|
|
3651
3772
|
field.disabled = this.saving;
|
|
3652
|
-
const send = button(this.saving ? "Saving…" : "Send Reply", "primary send", () => {
|
|
3773
|
+
const send = button(this.saving ? "Saving…" : "Send Reply", "primary send icon-button", () => {
|
|
3653
3774
|
}, "arrow");
|
|
3654
3775
|
send.type = "submit";
|
|
3655
3776
|
const resolve = button("Mark as Resolved", "primary resolve", () => this.toggleResolved(comment), "check");
|
|
@@ -3692,13 +3813,19 @@ ${this.currentPage()}`;
|
|
|
3692
3813
|
/** Display choices for this project. Each person has their own, saved in this browser. */
|
|
3693
3814
|
settingsView() {
|
|
3694
3815
|
const view = this.view("Settings", "settings");
|
|
3695
|
-
|
|
3816
|
+
const header = this.detailHeading("Settings", "Only for you. Others keep their own.");
|
|
3817
|
+
view.append(header);
|
|
3696
3818
|
const current = this.settings, body = element("div", "settings-body");
|
|
3697
3819
|
const tabs = element("div", "tabs settings-tabs");
|
|
3698
3820
|
tabs.setAttribute("role", "group");
|
|
3699
3821
|
tabs.setAttribute("aria-label", "Settings section");
|
|
3700
|
-
|
|
3701
|
-
|
|
3822
|
+
const hosted = this.storage instanceof HostedAdapter ? this.storage : null;
|
|
3823
|
+
if (!hosted && this.settingsTab === "mcp") this.settingsTab = "general";
|
|
3824
|
+
const sections = [["general", "General"], ["appearance", "Appearance"]];
|
|
3825
|
+
if (hosted) sections.push(["mcp", "MCP"]);
|
|
3826
|
+
tabs.style.gridTemplateColumns = `repeat(${sections.length},1fr)`;
|
|
3827
|
+
for (const [id, label] of sections) {
|
|
3828
|
+
const tab = button(label, "tab", () => {
|
|
3702
3829
|
this.settingsTab = id;
|
|
3703
3830
|
this.render();
|
|
3704
3831
|
});
|
|
@@ -3706,26 +3833,26 @@ ${this.currentPage()}`;
|
|
|
3706
3833
|
tab.dataset.focus = `settings-tab-${id}`;
|
|
3707
3834
|
tabs.append(tab);
|
|
3708
3835
|
}
|
|
3709
|
-
|
|
3836
|
+
header.append(tabs);
|
|
3710
3837
|
const section = (title, tab) => {
|
|
3711
3838
|
const group = element("section", "setting-group");
|
|
3712
3839
|
group.append(element("h3", "", title));
|
|
3713
3840
|
if (tab === this.settingsTab) body.append(group);
|
|
3714
3841
|
return group;
|
|
3715
3842
|
};
|
|
3716
|
-
const row = (group,
|
|
3843
|
+
const row = (group, label, hint, control) => {
|
|
3717
3844
|
const line = element("div", "setting");
|
|
3718
3845
|
const text = element("div", "setting-text");
|
|
3719
|
-
text.append(element("strong", "",
|
|
3846
|
+
text.append(element("strong", "", label));
|
|
3720
3847
|
if (hint) text.append(element("small", "", hint));
|
|
3721
3848
|
line.append(text, control);
|
|
3722
3849
|
group.append(line);
|
|
3723
3850
|
return line;
|
|
3724
3851
|
};
|
|
3725
|
-
const choice = (name,
|
|
3852
|
+
const choice = (name, label, options) => {
|
|
3726
3853
|
const group = element("div", "segmented");
|
|
3727
3854
|
group.setAttribute("role", "group");
|
|
3728
|
-
group.setAttribute("aria-label",
|
|
3855
|
+
group.setAttribute("aria-label", label);
|
|
3729
3856
|
for (const [value, text] of options) {
|
|
3730
3857
|
const option = button(text, "segment", () => this.setSetting(name, value));
|
|
3731
3858
|
option.setAttribute("aria-pressed", String(current[name] === value));
|
|
@@ -3734,13 +3861,13 @@ ${this.currentPage()}`;
|
|
|
3734
3861
|
}
|
|
3735
3862
|
return group;
|
|
3736
3863
|
};
|
|
3737
|
-
const toggle = (name,
|
|
3864
|
+
const toggle = (name, label, disabled = false) => {
|
|
3738
3865
|
const on = current[name] === true;
|
|
3739
3866
|
const control = element("button", "switch");
|
|
3740
3867
|
control.type = "button";
|
|
3741
3868
|
control.setAttribute("role", "switch");
|
|
3742
3869
|
control.setAttribute("aria-checked", String(on));
|
|
3743
|
-
control.setAttribute("aria-label",
|
|
3870
|
+
control.setAttribute("aria-label", label);
|
|
3744
3871
|
control.dataset.focus = `set-${name}`;
|
|
3745
3872
|
control.disabled = disabled;
|
|
3746
3873
|
control.addEventListener("click", () => this.setSetting(name, !on));
|
|
@@ -3752,8 +3879,12 @@ ${this.currentPage()}`;
|
|
|
3752
3879
|
const sample = element("div", `pin-sample${current.pinSize === "small" ? " small" : ""}${current.resolvedPins === "gray" ? " resolved-gray" : ""}`);
|
|
3753
3880
|
sample.setAttribute("aria-hidden", "true");
|
|
3754
3881
|
sample.style.setProperty("--cw-accent", this.pinColor());
|
|
3755
|
-
const
|
|
3756
|
-
|
|
3882
|
+
const samplePin = (n, cls) => {
|
|
3883
|
+
const pin = element("span", cls);
|
|
3884
|
+
pin.append(...this.pinFace(current.pinLabel, this.user.name, this.user.avatarUrl, n));
|
|
3885
|
+
return pin;
|
|
3886
|
+
};
|
|
3887
|
+
sample.append(samplePin(1, "pin"), samplePin(2, "pin resolved"), element("span", "pin-sample-text", "Open and resolved"));
|
|
3757
3888
|
pins.append(sample);
|
|
3758
3889
|
const swatches = element("div", "swatches");
|
|
3759
3890
|
swatches.setAttribute("role", "group");
|
|
@@ -3771,7 +3902,7 @@ ${this.currentPage()}`;
|
|
|
3771
3902
|
row(pins, "Color", current.pinColor === "site" ? "The color of this site" : PIN_COLORS[current.pinColor].label, swatches).classList.add("wide");
|
|
3772
3903
|
row(pins, "Resolved pins", "", choice("resolvedPins", "Resolved pins", [["green", "Green"], ["gray", "Gray"]]));
|
|
3773
3904
|
row(pins, "Size", "", choice("pinSize", "Pin size", [["regular", "Regular"], ["small", "Small"]]));
|
|
3774
|
-
row(pins, "Label", "", choice("pinLabel", "Pin label", [["number", "Number"], ["initials", "Initials"]]));
|
|
3905
|
+
row(pins, "Label", "", choice("pinLabel", "Pin label", [["number", "Number"], ["initials", "Initials"], ["photo", "Photo"]]));
|
|
3775
3906
|
const floating = section("Comments button", "appearance");
|
|
3776
3907
|
const buttonSample = element("div", "launcher-sample");
|
|
3777
3908
|
buttonSample.setAttribute("aria-hidden", "true");
|
|
@@ -3812,11 +3943,64 @@ ${this.currentPage()}`;
|
|
|
3812
3943
|
row(alerts, "New activity", "Show a short notice when others comment or reply.", toggle("notifications", "Show new activity"));
|
|
3813
3944
|
const keys = shortcutLabel(this.config.keyboardShortcut ?? "", /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent));
|
|
3814
3945
|
row(alerts, "Keyboard shortcut", this.shortcut ? `${keys} opens and closes comments.` : "This site has no shortcut.", toggle("shortcut", "Keyboard shortcut", !this.shortcut));
|
|
3946
|
+
if (hosted && this.settingsTab === "mcp") body.append(this.mcpSettings(hosted.base));
|
|
3815
3947
|
const foot = element("div", "settings-foot");
|
|
3948
|
+
foot.hidden = this.settingsTab === "mcp";
|
|
3816
3949
|
foot.append(element("span", "", "Saved in this browser"), button("Reset to Defaults", "quiet", () => this.resetSettings()));
|
|
3817
3950
|
view.append(body, foot);
|
|
3818
3951
|
return view;
|
|
3819
3952
|
}
|
|
3953
|
+
/** The MCP tab: what it does, the server address to copy, how to connect, and prompts to try. */
|
|
3954
|
+
mcpSettings(base) {
|
|
3955
|
+
const endpoint = `${base}/mcp`, wrap = element("div", "mcp");
|
|
3956
|
+
const intro = element("div", "mcp-intro"), badge = element("span", "mcp-badge");
|
|
3957
|
+
badge.append(icon("sparkle"));
|
|
3958
|
+
const copyText = element("div");
|
|
3959
|
+
copyText.append(
|
|
3960
|
+
element("h3", "", "Connect an AI agent"),
|
|
3961
|
+
element("p", "", "Claude, Cursor and other agents can read, answer and resolve the comments of this project.")
|
|
3962
|
+
);
|
|
3963
|
+
intro.append(badge, copyText);
|
|
3964
|
+
const block = (title) => {
|
|
3965
|
+
const group = element("section", "mcp-block");
|
|
3966
|
+
group.append(element("h4", "", title));
|
|
3967
|
+
wrap.append(group);
|
|
3968
|
+
return group;
|
|
3969
|
+
};
|
|
3970
|
+
wrap.append(intro);
|
|
3971
|
+
const address = element("div", "copy-field"), code = element("code", "", endpoint);
|
|
3972
|
+
const copy = button("Copy server address", "icon-button copy-button", async () => {
|
|
3973
|
+
var _a;
|
|
3974
|
+
try {
|
|
3975
|
+
await navigator.clipboard.writeText(endpoint);
|
|
3976
|
+
this.announce("Server address copied.");
|
|
3977
|
+
copy.classList.add("done");
|
|
3978
|
+
setTimeout(() => copy.classList.remove("done"), 1500);
|
|
3979
|
+
} catch {
|
|
3980
|
+
(_a = getSelection()) == null ? void 0 : _a.selectAllChildren(code);
|
|
3981
|
+
}
|
|
3982
|
+
}, "copy");
|
|
3983
|
+
copy.dataset.focus = "mcp-copy";
|
|
3984
|
+
address.append(code, copy);
|
|
3985
|
+
block("Server address").append(address);
|
|
3986
|
+
const steps = element("ol", "mcp-steps");
|
|
3987
|
+
for (const step of ["Add the address as a remote MCP server in your agent.", "Sign in and allow access to this project.", "Ask the agent to work on the comments."])
|
|
3988
|
+
steps.append(element("li", "", step));
|
|
3989
|
+
block("How to connect").append(steps);
|
|
3990
|
+
const prompts = element("ul", "mcp-prompts");
|
|
3991
|
+
for (const prompt of ["Fix the open comments on this page.", "Summarize the feedback from this week.", "Reply to each comment when its fix is done."])
|
|
3992
|
+
prompts.append(element("li", "", `“${prompt}”`));
|
|
3993
|
+
block("Try asking").append(prompts);
|
|
3994
|
+
const foot = element("div", "mcp-foot");
|
|
3995
|
+
const more = element("a", "quiet mcp-link");
|
|
3996
|
+
more.append(element("span", "", "Open MCP Settings"), icon("open"));
|
|
3997
|
+
more.href = `${base}/app/mcp`;
|
|
3998
|
+
more.target = "_blank";
|
|
3999
|
+
more.rel = "noopener";
|
|
4000
|
+
foot.append(more, element("p", "", "MCP needs a plan that includes it."));
|
|
4001
|
+
wrap.append(foot);
|
|
4002
|
+
return wrap;
|
|
4003
|
+
}
|
|
3820
4004
|
/** Colors the floating button with the person's choice. The default button keeps its own dark and open styles. */
|
|
3821
4005
|
paintLauncher(launcher) {
|
|
3822
4006
|
const choice = this.settings.buttonColor;
|
|
@@ -3891,7 +4075,7 @@ ${this.currentPage()}`;
|
|
|
3891
4075
|
pin.title += ". Drag to move.";
|
|
3892
4076
|
pin.addEventListener("pointerdown", (event) => this.startPinDrag(event, comment));
|
|
3893
4077
|
}
|
|
3894
|
-
pin.replaceChildren(
|
|
4078
|
+
pin.replaceChildren(...this.pinFace(settings.pinLabel, comment.author, comment.avatarUrl, index + 1));
|
|
3895
4079
|
pin.dataset.id = comment.id;
|
|
3896
4080
|
pin.setAttribute("aria-pressed", String(this.activeId === comment.id));
|
|
3897
4081
|
this.pins.append(pin);
|