relic-mcp 0.14.1 → 0.14.3
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/.claude-plugin/marketplace.json +4 -4
- package/.claude-plugin/plugin.json +2 -2
- package/dist/cover/cover.js +2 -2
- package/dist/cover/frame.html +524 -54
- package/dist/cover/styles.css +20 -0
- package/dist/relic-mcp.js +1 -1
- package/mcp-servers.json +2 -2
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/build-info.json +1 -1
- package/prebuilds/darwin-x64/build-info.json +1 -1
- package/prebuilds/linux-arm64-gnu/build-info.json +1 -1
- package/prebuilds/linux-x64-gnu/build-info.json +1 -1
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
"description": "Relic: zero-knowledge publishing for agent output. Turns a local file into a shareable link without handing the file to anybody.",
|
|
5
5
|
"owner": {
|
|
6
6
|
"name": "The Bushido Collective",
|
|
7
|
-
"url": "https://
|
|
7
|
+
"url": "https://withbushido.ai"
|
|
8
8
|
},
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
11
11
|
"name": "relic",
|
|
12
12
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
13
|
-
"version": "0.14.
|
|
13
|
+
"version": "0.14.3",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "The Bushido Collective",
|
|
17
|
-
"url": "https://
|
|
17
|
+
"url": "https://withbushido.ai"
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://github.com/TheBushidoCollective/relic",
|
|
20
20
|
"category": "productivity"
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"metadata": {
|
|
24
|
-
"version": "0.14.
|
|
24
|
+
"version": "0.14.3"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
5
5
|
"mcpServers": "./mcp-servers.json",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "The Bushido Collective",
|
|
8
|
-
"url": "https://
|
|
8
|
+
"url": "https://withbushido.ai"
|
|
9
9
|
},
|
|
10
10
|
"homepage": "https://github.com/TheBushidoCollective/relic",
|
|
11
11
|
"repository": "https://github.com/TheBushidoCollective/relic",
|
package/dist/cover/cover.js
CHANGED
|
@@ -20673,9 +20673,9 @@ async function renderFrameCover(kind, source, dialect, root) {
|
|
|
20673
20673
|
return;
|
|
20674
20674
|
if (data.type === "relic:sandbox-ready") {
|
|
20675
20675
|
if (kind === "html") {
|
|
20676
|
-
iframe.contentWindow?.postMessage({ type: "relic:render", html: codeToSend }, "*");
|
|
20676
|
+
iframe.contentWindow?.postMessage({ type: "relic:render", html: codeToSend, cover: true }, "*");
|
|
20677
20677
|
} else {
|
|
20678
|
-
iframe.contentWindow?.postMessage({ type: "relic:render-jsx", code: codeToSend }, "*");
|
|
20678
|
+
iframe.contentWindow?.postMessage({ type: "relic:render-jsx", code: codeToSend, cover: true }, "*");
|
|
20679
20679
|
}
|
|
20680
20680
|
} else if (data.type === "relic:tree") {
|
|
20681
20681
|
cleanup();
|
package/dist/cover/frame.html
CHANGED
|
@@ -11561,6 +11561,12 @@ function isTrackMarkMessage(data) {
|
|
|
11561
11561
|
return true;
|
|
11562
11562
|
return typeof msg.id === "string" && msg.id.length > 0 && msg.id.length <= 256;
|
|
11563
11563
|
}
|
|
11564
|
+
function isFrameVisibilityMessage(data) {
|
|
11565
|
+
if (typeof data !== "object" || data === null)
|
|
11566
|
+
return false;
|
|
11567
|
+
const msg = data;
|
|
11568
|
+
return msg.type === "relic:frame-visibility" && typeof msg.visible === "boolean";
|
|
11569
|
+
}
|
|
11564
11570
|
function isActiveMarkMessage(data) {
|
|
11565
11571
|
if (typeof data !== "object" || data === null)
|
|
11566
11572
|
return false;
|
|
@@ -11702,6 +11708,127 @@ function isSetScrollMessage(data) {
|
|
|
11702
11708
|
return isFractionMessage(data, "relic:set-scroll");
|
|
11703
11709
|
}
|
|
11704
11710
|
|
|
11711
|
+
// ../relic-viewer/src/mark-visibility.ts
|
|
11712
|
+
var CONCEALED_ATTRIBUTE = "data-concealed";
|
|
11713
|
+
var CONCEALMENT_CSS = `
|
|
11714
|
+
[${CONCEALED_ATTRIBUTE}='hidden']:not(mark) { display: none !important; }
|
|
11715
|
+
[${CONCEALED_ATTRIBUTE}='offscreen']:not(mark) { opacity: 0 !important; }
|
|
11716
|
+
mark[${CONCEALED_ATTRIBUTE}] {
|
|
11717
|
+
background: none !important;
|
|
11718
|
+
border-bottom-color: transparent !important;
|
|
11719
|
+
box-shadow: none !important;
|
|
11720
|
+
}
|
|
11721
|
+
`;
|
|
11722
|
+
function isRendered(el) {
|
|
11723
|
+
if (el.isConnected !== true)
|
|
11724
|
+
return true;
|
|
11725
|
+
const probe = el.checkVisibility;
|
|
11726
|
+
if (typeof probe === "function") {
|
|
11727
|
+
if (!probe.call(el, { checkVisibilityCSS: true, checkOpacity: false })) {
|
|
11728
|
+
return false;
|
|
11729
|
+
}
|
|
11730
|
+
} else {
|
|
11731
|
+
for (let node = el;node; node = node.parentElement) {
|
|
11732
|
+
if (typeof node.hasAttribute === "function" && node.hasAttribute("hidden")) {
|
|
11733
|
+
return false;
|
|
11734
|
+
}
|
|
11735
|
+
if (node.tagName === "DETAILS" && !node.open) {
|
|
11736
|
+
const summary = Array.from(node.children).find((child) => child.tagName === "SUMMARY");
|
|
11737
|
+
if (!summary || !summary.contains(el))
|
|
11738
|
+
return false;
|
|
11739
|
+
}
|
|
11740
|
+
const style = node.style;
|
|
11741
|
+
if (style && (style.display === "none" || style.visibility === "hidden")) {
|
|
11742
|
+
return false;
|
|
11743
|
+
}
|
|
11744
|
+
}
|
|
11745
|
+
}
|
|
11746
|
+
const rects = typeof el.getClientRects === "function" ? el.getClientRects() : undefined;
|
|
11747
|
+
if (rects !== undefined && rects.length === 0)
|
|
11748
|
+
return false;
|
|
11749
|
+
return true;
|
|
11750
|
+
}
|
|
11751
|
+
function setConcealed(node, concealment) {
|
|
11752
|
+
const current = node.getAttribute(CONCEALED_ATTRIBUTE);
|
|
11753
|
+
if (concealment === undefined) {
|
|
11754
|
+
if (current !== null)
|
|
11755
|
+
node.removeAttribute(CONCEALED_ATTRIBUTE);
|
|
11756
|
+
return;
|
|
11757
|
+
}
|
|
11758
|
+
if (current !== concealment)
|
|
11759
|
+
node.setAttribute(CONCEALED_ATTRIBUTE, concealment);
|
|
11760
|
+
}
|
|
11761
|
+
function createScrollportWatch(onChange) {
|
|
11762
|
+
const inside = new WeakMap;
|
|
11763
|
+
const observed = new Set;
|
|
11764
|
+
const observer = typeof IntersectionObserver === "function" ? new IntersectionObserver((entries) => {
|
|
11765
|
+
let changed = false;
|
|
11766
|
+
for (const entry of entries) {
|
|
11767
|
+
if (inside.get(entry.target) !== entry.isIntersecting) {
|
|
11768
|
+
inside.set(entry.target, entry.isIntersecting);
|
|
11769
|
+
changed = true;
|
|
11770
|
+
}
|
|
11771
|
+
}
|
|
11772
|
+
if (changed)
|
|
11773
|
+
onChange();
|
|
11774
|
+
}) : undefined;
|
|
11775
|
+
return {
|
|
11776
|
+
sync(elements) {
|
|
11777
|
+
if (observer === undefined)
|
|
11778
|
+
return;
|
|
11779
|
+
const wanted = new Set(elements);
|
|
11780
|
+
for (const el of observed) {
|
|
11781
|
+
if (wanted.has(el))
|
|
11782
|
+
continue;
|
|
11783
|
+
observer.unobserve(el);
|
|
11784
|
+
observed.delete(el);
|
|
11785
|
+
inside.delete(el);
|
|
11786
|
+
}
|
|
11787
|
+
for (const el of wanted) {
|
|
11788
|
+
if (observed.has(el))
|
|
11789
|
+
continue;
|
|
11790
|
+
observer.observe(el);
|
|
11791
|
+
observed.add(el);
|
|
11792
|
+
}
|
|
11793
|
+
},
|
|
11794
|
+
inView(el) {
|
|
11795
|
+
return inside.get(el) !== false;
|
|
11796
|
+
},
|
|
11797
|
+
disconnect() {
|
|
11798
|
+
observer?.disconnect();
|
|
11799
|
+
observed.clear();
|
|
11800
|
+
}
|
|
11801
|
+
};
|
|
11802
|
+
}
|
|
11803
|
+
function concealMarks(marks, watch, live = true) {
|
|
11804
|
+
watch.sync(marks.map((mark) => mark.node));
|
|
11805
|
+
const merged = new Map;
|
|
11806
|
+
for (const { node, subject } of marks) {
|
|
11807
|
+
const id = node.getAttribute("data-comment-id");
|
|
11808
|
+
if (id === null || id === "")
|
|
11809
|
+
continue;
|
|
11810
|
+
const concealment = !live || !isRendered(subject) ? "hidden" : !watch.inView(node) ? "offscreen" : "shown";
|
|
11811
|
+
setConcealed(node, concealment === "shown" ? undefined : concealment);
|
|
11812
|
+
const previous = merged.get(id);
|
|
11813
|
+
merged.set(id, mergeReason(previous, concealment));
|
|
11814
|
+
}
|
|
11815
|
+
const out = new Map;
|
|
11816
|
+
for (const [id, reason] of merged) {
|
|
11817
|
+
if (reason !== "shown")
|
|
11818
|
+
out.set(id, reason);
|
|
11819
|
+
}
|
|
11820
|
+
return out;
|
|
11821
|
+
}
|
|
11822
|
+
function mergeReason(previous, next) {
|
|
11823
|
+
if (previous === undefined)
|
|
11824
|
+
return next;
|
|
11825
|
+
if (previous === "shown" || next === "shown")
|
|
11826
|
+
return "shown";
|
|
11827
|
+
if (previous === "hidden" || next === "hidden")
|
|
11828
|
+
return "hidden";
|
|
11829
|
+
return "offscreen";
|
|
11830
|
+
}
|
|
11831
|
+
|
|
11705
11832
|
// ../relic-viewer/src/rendered-tree.ts
|
|
11706
11833
|
function isRevealChangeMessage(data) {
|
|
11707
11834
|
if (typeof data !== "object" || data === null)
|
|
@@ -12098,19 +12225,35 @@ var FRAME_MARK_CSS = `
|
|
|
12098
12225
|
cursor: default !important;
|
|
12099
12226
|
}
|
|
12100
12227
|
.relic-image-layer { position: fixed !important; inset: 0 !important; z-index: 2147483646 !important; pointer-events: none !important; }
|
|
12101
|
-
.relic-image-
|
|
12228
|
+
.relic-image-row {
|
|
12102
12229
|
position: absolute !important; box-sizing: border-box !important;
|
|
12103
|
-
|
|
12230
|
+
display: flex !important; align-items: flex-start !important; gap: 10px !important;
|
|
12231
|
+
padding: 6px !important; margin: 0 !important; max-height: 56px !important;
|
|
12232
|
+
overflow-x: auto !important; overflow-y: hidden !important; scrollbar-width: thin !important;
|
|
12233
|
+
pointer-events: none !important; overscroll-behavior: contain !important;
|
|
12234
|
+
}
|
|
12235
|
+
.relic-image-row.has-marks { pointer-events: auto !important; }
|
|
12236
|
+
.relic-image-mark, .relic-image-control {
|
|
12237
|
+
position: relative !important; box-sizing: border-box !important; flex: 0 0 28px !important;
|
|
12238
|
+
display: flex !important; align-items: center !important; justify-content: center !important;
|
|
12239
|
+
width: 28px !important; height: 28px !important; padding: 0 !important; margin: 0 !important;
|
|
12104
12240
|
border: 2px solid white !important; border-radius: 50% !important;
|
|
12105
|
-
background: #806026 !important; color: white !important;
|
|
12241
|
+
background: #806026 !important; color: white !important;
|
|
12242
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45) !important;
|
|
12106
12243
|
cursor: pointer !important; pointer-events: auto !important;
|
|
12107
12244
|
}
|
|
12245
|
+
.relic-image-control {
|
|
12246
|
+
opacity: 0 !important; pointer-events: none !important;
|
|
12247
|
+
transition: opacity 0.12s ease !important;
|
|
12248
|
+
}
|
|
12249
|
+
.relic-image-control svg, .relic-image-mark svg { display: block !important; width: 14px !important; height: 14px !important; fill: currentColor !important; pointer-events: none !important; }
|
|
12250
|
+
.relic-image-control.is-shown, .relic-image-control:focus-visible { opacity: 1 !important; pointer-events: auto !important; }
|
|
12251
|
+
.relic-image-mark.is-active, .relic-image-mark.is-paired { outline: 3px solid #806026 !important; outline-offset: 2px !important; }
|
|
12108
12252
|
.relic-image-control:focus-visible, .relic-image-mark:focus-visible { outline: 3px solid #222 !important; outline-offset: 2px !important; }
|
|
12109
|
-
.relic-image-mark.is-active, .relic-image-mark.is-paired { outline: 3px solid #806026 !important; }
|
|
12110
12253
|
.relic-image-mark.is-resolved { background: #777 !important; }
|
|
12111
|
-
.relic-image-mark.is-pending { pointer-events: none !important; opacity: .6 !important; }
|
|
12254
|
+
.relic-image-mark.is-pending { pointer-events: none !important; opacity: .6 !important; border-style: dashed !important; }
|
|
12112
12255
|
.relic-image-outline { position: absolute !important; box-sizing: border-box !important; border: 2px solid #b48c3c !important; pointer-events: none !important; }
|
|
12113
|
-
.relic-image-control[hidden] { display: none !important; }
|
|
12256
|
+
.relic-image-control[hidden], .relic-image-row[hidden] { display: none !important; }
|
|
12114
12257
|
.relic-pointing-active, .relic-pointing-active * {
|
|
12115
12258
|
cursor: crosshair !important;
|
|
12116
12259
|
}
|
|
@@ -12120,7 +12263,7 @@ function ensureFrameStyles(doc) {
|
|
|
12120
12263
|
return;
|
|
12121
12264
|
const style = doc.createElement("style");
|
|
12122
12265
|
style.id = "relic-frame-styles";
|
|
12123
|
-
style.textContent = FRAME_MARK_CSS;
|
|
12266
|
+
style.textContent = FRAME_MARK_CSS + CONCEALMENT_CSS;
|
|
12124
12267
|
doc.head?.appendChild(style);
|
|
12125
12268
|
}
|
|
12126
12269
|
function markFrameRect(el) {
|
|
@@ -12314,6 +12457,42 @@ function documentSize(doc) {
|
|
|
12314
12457
|
}
|
|
12315
12458
|
var frameOwnedNodes = new WeakSet;
|
|
12316
12459
|
var frameRegionOverlays = new WeakMap;
|
|
12460
|
+
var markSubjects = new WeakMap;
|
|
12461
|
+
function isFrameOwned(node) {
|
|
12462
|
+
for (let current = node;current; current = current.parentNode) {
|
|
12463
|
+
if (frameOwnedNodes.has(current))
|
|
12464
|
+
return true;
|
|
12465
|
+
}
|
|
12466
|
+
return false;
|
|
12467
|
+
}
|
|
12468
|
+
function subjectAt(doc, x, y) {
|
|
12469
|
+
const body = doc.body;
|
|
12470
|
+
if (!body || typeof body.getElementsByTagName !== "function")
|
|
12471
|
+
return;
|
|
12472
|
+
const scrollX = doc.defaultView?.scrollX ?? 0;
|
|
12473
|
+
const scrollY = doc.defaultView?.scrollY ?? 0;
|
|
12474
|
+
let best;
|
|
12475
|
+
let bestArea = Number.POSITIVE_INFINITY;
|
|
12476
|
+
for (const el of Array.from(body.getElementsByTagName("*"))) {
|
|
12477
|
+
if (isFrameOwned(el) || typeof el.getBoundingClientRect !== "function") {
|
|
12478
|
+
continue;
|
|
12479
|
+
}
|
|
12480
|
+
const box = el.getBoundingClientRect();
|
|
12481
|
+
if (box.width <= 0 || box.height <= 0)
|
|
12482
|
+
continue;
|
|
12483
|
+
const left = box.left + scrollX;
|
|
12484
|
+
const top = box.top + scrollY;
|
|
12485
|
+
if (x < left || x > left + box.width || y < top || y > top + box.height) {
|
|
12486
|
+
continue;
|
|
12487
|
+
}
|
|
12488
|
+
const area = box.width * box.height;
|
|
12489
|
+
if (area <= bestArea) {
|
|
12490
|
+
best = el;
|
|
12491
|
+
bestArea = area;
|
|
12492
|
+
}
|
|
12493
|
+
}
|
|
12494
|
+
return best;
|
|
12495
|
+
}
|
|
12317
12496
|
function frameOverlay(doc) {
|
|
12318
12497
|
if (!doc.body)
|
|
12319
12498
|
return;
|
|
@@ -12349,6 +12528,9 @@ function paintFrameRegion(root, rect, id) {
|
|
|
12349
12528
|
div.style.width = `${rect.w * docWidth}px`;
|
|
12350
12529
|
div.style.height = `${rect.h * docHeight}px`;
|
|
12351
12530
|
wireFrameMark(div, id, doc);
|
|
12531
|
+
const subject = subjectAt(doc, (rect.x + rect.w / 2) * docWidth, (rect.y + rect.h / 2) * docHeight);
|
|
12532
|
+
if (subject)
|
|
12533
|
+
markSubjects.set(div, subject);
|
|
12352
12534
|
overlay.appendChild(div);
|
|
12353
12535
|
return div;
|
|
12354
12536
|
}
|
|
@@ -12364,6 +12546,9 @@ function paintFramePin(root, point, id) {
|
|
|
12364
12546
|
pin.dataset.commentId = id;
|
|
12365
12547
|
pin.style.left = `${point.x * layer.docWidth}px`;
|
|
12366
12548
|
pin.style.top = `${point.y * layer.docHeight}px`;
|
|
12549
|
+
const subject = subjectAt(doc, point.x * layer.docWidth, point.y * layer.docHeight);
|
|
12550
|
+
if (subject)
|
|
12551
|
+
markSubjects.set(pin, subject);
|
|
12367
12552
|
if (!pending)
|
|
12368
12553
|
wireFrameMark(pin, id, doc);
|
|
12369
12554
|
layer.overlay.appendChild(pin);
|
|
@@ -12486,13 +12671,29 @@ function applyDocumentScrollFraction(doc, win, fraction) {
|
|
|
12486
12671
|
const bounded = Math.min(Math.max(fraction, 0), 1);
|
|
12487
12672
|
applyDocumentScrollTop(doc, win, bounded * travel);
|
|
12488
12673
|
}
|
|
12489
|
-
|
|
12674
|
+
var COMMENT_PLUS_PATH = "M256 480c141.4 0 256-107.5 256-240S397.4 0 256 0 0 107.5 0 240c0 54.3 19.2 104.3 51.6 144.5L2.8 476.8c-4.8 9-3.3 20 3.6 27.5s17.8 9.8 27.1 5.8l118.4-50.7C183.7 472.6 218.9 480 256 480zm0-336c13.3 0 24 10.7 24 24l0 48 48 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-48 0 0 48c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-48-48 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0 0-48c0-13.3 10.7-24 24-24z";
|
|
12675
|
+
var COMMENT_PATH = "M512 240c0 132.5-114.6 240-256 240-37.1 0-72.3-7.4-104.1-20.7L33.5 510.1c-9.4 4-20.2 1.7-27.1-5.8S-2 485.8 2.8 476.8l48.8-92.2C19.2 344.3 0 294.3 0 240 0 107.5 114.6 0 256 0S512 107.5 512 240z";
|
|
12676
|
+
function imageCommentIcon(doc, add = false) {
|
|
12677
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
12678
|
+
const svg = doc.createElementNS(ns, "svg");
|
|
12679
|
+
svg.setAttribute("viewBox", "0 0 512 512");
|
|
12680
|
+
svg.setAttribute("aria-hidden", "true");
|
|
12681
|
+
svg.setAttribute("focusable", "false");
|
|
12682
|
+
svg.appendChild(doc.createComment("! Font Awesome Pro 7.3.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2026 Fonticons, Inc."));
|
|
12683
|
+
const path = doc.createElementNS(ns, "path");
|
|
12684
|
+
path.setAttribute("d", add ? COMMENT_PLUS_PATH : COMMENT_PATH);
|
|
12685
|
+
svg.appendChild(path);
|
|
12686
|
+
return svg;
|
|
12687
|
+
}
|
|
12688
|
+
function setupImageMarks(doc, win, post, tracked, enabled, inParent) {
|
|
12490
12689
|
const entries = new Map;
|
|
12491
12690
|
const controls = new Map;
|
|
12691
|
+
const rows = new Map;
|
|
12492
12692
|
const hashes = new WeakMap;
|
|
12493
12693
|
let layer;
|
|
12494
12694
|
let scheduled = false;
|
|
12495
12695
|
let serial = 0;
|
|
12696
|
+
const pairedIds = new Set;
|
|
12496
12697
|
let resizeObserver;
|
|
12497
12698
|
let disposed = false;
|
|
12498
12699
|
let activeId = null;
|
|
@@ -12571,6 +12772,10 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12571
12772
|
return node.tagName.toLowerCase() === "img" ? node : undefined;
|
|
12572
12773
|
};
|
|
12573
12774
|
const visible = (img) => {
|
|
12775
|
+
if (!inParent())
|
|
12776
|
+
return;
|
|
12777
|
+
if (!isRendered(img))
|
|
12778
|
+
return;
|
|
12574
12779
|
if (!img.isConnected || !img.complete || img.naturalWidth === 0 || isOwned(img))
|
|
12575
12780
|
return;
|
|
12576
12781
|
const rect = markFrameRect(img);
|
|
@@ -12623,6 +12828,7 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12623
12828
|
for (const image of controls.keys())
|
|
12624
12829
|
resizeObserver?.unobserve(image);
|
|
12625
12830
|
controls.clear();
|
|
12831
|
+
rows.clear();
|
|
12626
12832
|
for (const entry of entries.values())
|
|
12627
12833
|
remove(entry);
|
|
12628
12834
|
}
|
|
@@ -12634,20 +12840,147 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12634
12840
|
return layer;
|
|
12635
12841
|
};
|
|
12636
12842
|
const remove = (entry) => {
|
|
12843
|
+
const image = entry.image;
|
|
12637
12844
|
entry.badge?.remove();
|
|
12638
12845
|
entry.outline?.remove();
|
|
12639
12846
|
delete entry.badge;
|
|
12640
12847
|
delete entry.outline;
|
|
12641
12848
|
delete entry.image;
|
|
12642
12849
|
delete entry.declaration;
|
|
12850
|
+
if (image)
|
|
12851
|
+
layoutRow(image);
|
|
12852
|
+
};
|
|
12853
|
+
const reportConcealed = (entry, mark, img) => {
|
|
12854
|
+
const concealment = isRendered(img) ? "offscreen" : "hidden";
|
|
12855
|
+
const key = `concealed:${concealment}`;
|
|
12856
|
+
if (entry.reported === key)
|
|
12857
|
+
return;
|
|
12858
|
+
entry.reported = key;
|
|
12859
|
+
post({
|
|
12860
|
+
type: "relic:frame-image-placement",
|
|
12861
|
+
id: mark.id,
|
|
12862
|
+
requestId: mark.requestId,
|
|
12863
|
+
status: "concealed",
|
|
12864
|
+
concealment
|
|
12865
|
+
});
|
|
12643
12866
|
};
|
|
12644
|
-
const
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12867
|
+
const CONTROL_SIZE = 28;
|
|
12868
|
+
const ROW_GAP = 10;
|
|
12869
|
+
const ROW_PADDING = 6;
|
|
12870
|
+
const layoutRow = (img) => {
|
|
12871
|
+
const row = rows.get(img);
|
|
12872
|
+
const rect = visible(img);
|
|
12873
|
+
if (!row)
|
|
12874
|
+
return;
|
|
12875
|
+
row.hidden = !rect;
|
|
12876
|
+
if (!rect)
|
|
12877
|
+
return;
|
|
12878
|
+
const count = row.children.length;
|
|
12879
|
+
const fullWidth = count * CONTROL_SIZE + (count - 1) * ROW_GAP + ROW_PADDING * 2;
|
|
12880
|
+
const width = Math.min(fullWidth, Math.max(rect.width, 78), win.innerWidth);
|
|
12881
|
+
const roomy = rect.width >= 56 && rect.height >= 56;
|
|
12882
|
+
const right = rect.left + rect.width + (roomy ? -8 : CONTROL_SIZE / 2) + ROW_PADDING;
|
|
12883
|
+
const top = rect.top + (roomy ? 8 : -CONTROL_SIZE / 2) - ROW_PADDING;
|
|
12884
|
+
row.style.width = `${width}px`;
|
|
12885
|
+
row.style.left = `${Math.max(0, Math.min(right - width, win.innerWidth - width))}px`;
|
|
12886
|
+
row.style.top = `${Math.max(0, Math.min(top, win.innerHeight - 56))}px`;
|
|
12887
|
+
row.classList.toggle("has-marks", count > 1);
|
|
12888
|
+
row.style.setProperty("scroll-padding-inline", `${ROW_PADDING}px`);
|
|
12889
|
+
};
|
|
12890
|
+
const revealButton = (row, button) => {
|
|
12891
|
+
const box = button.getBoundingClientRect();
|
|
12892
|
+
const viewport = row.getBoundingClientRect();
|
|
12893
|
+
if (box.left < viewport.left + ROW_PADDING)
|
|
12894
|
+
row.scrollLeft -= viewport.left + ROW_PADDING - box.left;
|
|
12895
|
+
else if (box.right > viewport.right - ROW_PADDING)
|
|
12896
|
+
row.scrollLeft += box.right - viewport.right + ROW_PADDING;
|
|
12897
|
+
};
|
|
12898
|
+
const revealBadge = (entry) => {
|
|
12899
|
+
const row = entry.image && rows.get(entry.image);
|
|
12900
|
+
if (row && entry.badge)
|
|
12901
|
+
revealButton(row, entry.badge);
|
|
12902
|
+
};
|
|
12903
|
+
const reportRows = () => {
|
|
12904
|
+
for (const entry of entries.values()) {
|
|
12905
|
+
if (!entry.badge || !entry.image || entry.declaration !== source(entry.image))
|
|
12906
|
+
continue;
|
|
12907
|
+
const row = rows.get(entry.image);
|
|
12908
|
+
const box = markFrameRect(entry.badge);
|
|
12909
|
+
const clip = row && markFrameRect(row);
|
|
12910
|
+
if (!box || !clip || row?.hidden)
|
|
12911
|
+
continue;
|
|
12912
|
+
const id = entry.mark.id;
|
|
12913
|
+
if (box.left < clip.left + ROW_PADDING - 0.5 || box.left + box.width > clip.left + clip.width - ROW_PADDING + 0.5) {
|
|
12914
|
+
if (entry.reported !== "concealed:offscreen") {
|
|
12915
|
+
entry.reported = "concealed:offscreen";
|
|
12916
|
+
post({
|
|
12917
|
+
type: "relic:frame-image-placement",
|
|
12918
|
+
id,
|
|
12919
|
+
requestId: entry.mark.requestId,
|
|
12920
|
+
status: "concealed",
|
|
12921
|
+
concealment: "offscreen"
|
|
12922
|
+
});
|
|
12923
|
+
}
|
|
12924
|
+
continue;
|
|
12925
|
+
}
|
|
12926
|
+
entry.reported = "placed";
|
|
12927
|
+
post({
|
|
12928
|
+
type: "relic:frame-image-placement",
|
|
12929
|
+
id,
|
|
12930
|
+
requestId: entry.mark.requestId,
|
|
12931
|
+
status: "placed",
|
|
12932
|
+
rect: box
|
|
12933
|
+
});
|
|
12934
|
+
if (tracked() === id)
|
|
12935
|
+
post({ type: "relic:frame-mark-rect", id, rect: box });
|
|
12936
|
+
}
|
|
12937
|
+
};
|
|
12938
|
+
let rowReportScheduled = false;
|
|
12939
|
+
const scheduleRowReport = () => {
|
|
12940
|
+
if (rowReportScheduled)
|
|
12941
|
+
return;
|
|
12942
|
+
rowReportScheduled = true;
|
|
12943
|
+
queueMicrotask(() => {
|
|
12944
|
+
rowReportScheduled = false;
|
|
12945
|
+
if (!disposed)
|
|
12946
|
+
reportRows();
|
|
12947
|
+
});
|
|
12948
|
+
};
|
|
12949
|
+
let pointer;
|
|
12950
|
+
const updateHover = () => {
|
|
12951
|
+
const always = win.matchMedia?.("(hover: none)").matches ?? false;
|
|
12952
|
+
let target;
|
|
12953
|
+
let targetArea = Number.POSITIVE_INFINITY;
|
|
12954
|
+
if (pointer && !always) {
|
|
12955
|
+
const { x, y } = pointer;
|
|
12956
|
+
const inside = (r) => x >= r.left && x <= r.left + r.width && y >= r.top && y <= r.top + r.height;
|
|
12957
|
+
for (const [img, button] of controls) {
|
|
12958
|
+
if (button.hidden)
|
|
12959
|
+
continue;
|
|
12960
|
+
const rect = visible(img);
|
|
12961
|
+
const box = (rows.get(img) ?? button).getBoundingClientRect();
|
|
12962
|
+
if (!(rect && inside(rect)) && !inside(box))
|
|
12963
|
+
continue;
|
|
12964
|
+
const area = rect ? rect.width * rect.height : 0;
|
|
12965
|
+
if (area <= targetArea) {
|
|
12966
|
+
target = img;
|
|
12967
|
+
targetArea = area;
|
|
12968
|
+
}
|
|
12969
|
+
}
|
|
12970
|
+
}
|
|
12971
|
+
for (const [img, button] of controls)
|
|
12972
|
+
button.classList.toggle("is-shown", always || img === target);
|
|
12973
|
+
};
|
|
12974
|
+
const onPointerMove = (event) => {
|
|
12975
|
+
const { clientX, clientY } = event;
|
|
12976
|
+
pointer = { x: clientX, y: clientY };
|
|
12977
|
+
updateHover();
|
|
12978
|
+
};
|
|
12979
|
+
const onPointerLeave = (event) => {
|
|
12980
|
+
if (event.relatedTarget !== null)
|
|
12981
|
+
return;
|
|
12982
|
+
pointer = undefined;
|
|
12983
|
+
updateHover();
|
|
12651
12984
|
};
|
|
12652
12985
|
const schedule = () => {
|
|
12653
12986
|
if (!enabled)
|
|
@@ -12673,9 +13006,10 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12673
13006
|
return;
|
|
12674
13007
|
const images = Array.from(doc.body.querySelectorAll("img")).filter((img) => !isOwned(img));
|
|
12675
13008
|
const live = new Set(images);
|
|
12676
|
-
for (const
|
|
13009
|
+
for (const img of controls.keys()) {
|
|
12677
13010
|
if (!live.has(img)) {
|
|
12678
|
-
|
|
13011
|
+
rows.get(img)?.remove();
|
|
13012
|
+
rows.delete(img);
|
|
12679
13013
|
controls.delete(img);
|
|
12680
13014
|
resizeObserver?.unobserve(img);
|
|
12681
13015
|
}
|
|
@@ -12687,8 +13021,17 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12687
13021
|
button.type = "button";
|
|
12688
13022
|
button.className = "relic-image-control";
|
|
12689
13023
|
button.setAttribute("aria-label", "Comment on image");
|
|
12690
|
-
button.
|
|
12691
|
-
|
|
13024
|
+
button.appendChild(imageCommentIcon(doc, true));
|
|
13025
|
+
const row = doc.createElement("div");
|
|
13026
|
+
row.className = "relic-image-row";
|
|
13027
|
+
row.appendChild(button);
|
|
13028
|
+
row.addEventListener("scroll", reportRows);
|
|
13029
|
+
button.addEventListener("focus", () => {
|
|
13030
|
+
revealButton(row, button);
|
|
13031
|
+
reportRows();
|
|
13032
|
+
});
|
|
13033
|
+
rows.set(img, row);
|
|
13034
|
+
host.appendChild(row);
|
|
12692
13035
|
controls.set(img, button);
|
|
12693
13036
|
resizeObserver?.observe(img);
|
|
12694
13037
|
const control = button;
|
|
@@ -12715,17 +13058,16 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12715
13058
|
}
|
|
12716
13059
|
const rect = visible(img);
|
|
12717
13060
|
button.hidden = !rect;
|
|
12718
|
-
|
|
12719
|
-
position(button, rect);
|
|
13061
|
+
layoutRow(img);
|
|
12720
13062
|
}
|
|
12721
|
-
|
|
13063
|
+
updateHover();
|
|
12722
13064
|
for (const entry of entries.values()) {
|
|
12723
13065
|
const mark = entry.mark;
|
|
12724
13066
|
const generation = ++entry.serial;
|
|
12725
13067
|
const img = resolve(mark.path);
|
|
12726
13068
|
const rect = img && visible(img);
|
|
12727
13069
|
const declaration = img && source(img);
|
|
12728
|
-
if (!img || !
|
|
13070
|
+
if (!img || !declaration || !/^[0-9a-f]{64}$/.test(mark.source_sha256) || new TextEncoder().encode(JSON.stringify({
|
|
12729
13071
|
kind: "image",
|
|
12730
13072
|
path: mark.path,
|
|
12731
13073
|
source_sha256: mark.source_sha256
|
|
@@ -12739,15 +13081,18 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12739
13081
|
});
|
|
12740
13082
|
continue;
|
|
12741
13083
|
}
|
|
13084
|
+
if (!rect) {
|
|
13085
|
+
remove(entry);
|
|
13086
|
+
reportConcealed(entry, mark, img);
|
|
13087
|
+
continue;
|
|
13088
|
+
}
|
|
12742
13089
|
if (entry.badge && (entry.image !== img || entry.declaration !== declaration))
|
|
12743
13090
|
remove(entry);
|
|
12744
|
-
const offset = (positions.get(img) ?? 0) + 1;
|
|
12745
|
-
positions.set(img, offset);
|
|
12746
13091
|
fingerprint(img).then((digest) => {
|
|
12747
13092
|
if (entries.get(mark.id) !== entry || entry.serial !== generation)
|
|
12748
13093
|
return;
|
|
12749
13094
|
const currentRect = visible(img);
|
|
12750
|
-
if (digest !== mark.source_sha256 || source(img) !== declaration || resolve(mark.path) !== img
|
|
13095
|
+
if (digest !== mark.source_sha256 || source(img) !== declaration || resolve(mark.path) !== img) {
|
|
12751
13096
|
remove(entry);
|
|
12752
13097
|
post({
|
|
12753
13098
|
type: "relic:frame-image-placement",
|
|
@@ -12757,6 +13102,12 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12757
13102
|
});
|
|
12758
13103
|
return;
|
|
12759
13104
|
}
|
|
13105
|
+
if (!currentRect) {
|
|
13106
|
+
remove(entry);
|
|
13107
|
+
reportConcealed(entry, mark, img);
|
|
13108
|
+
return;
|
|
13109
|
+
}
|
|
13110
|
+
const created = !entry.badge;
|
|
12760
13111
|
if (!entry.badge) {
|
|
12761
13112
|
entry.outline = doc.createElement("div");
|
|
12762
13113
|
entry.outline.className = "relic-image-outline";
|
|
@@ -12766,43 +13117,38 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12766
13117
|
entry.badge.className = "relic-image-mark";
|
|
12767
13118
|
entry.badge.dataset.commentId = mark.id;
|
|
12768
13119
|
entry.badge.setAttribute("aria-label", "Open image comment");
|
|
12769
|
-
entry.badge.
|
|
12770
|
-
if (mark.id === "pending:target")
|
|
13120
|
+
entry.badge.appendChild(imageCommentIcon(doc));
|
|
13121
|
+
if (mark.id === "pending:target") {
|
|
12771
13122
|
entry.badge.classList.add("is-pending");
|
|
13123
|
+
entry.badge.disabled = true;
|
|
13124
|
+
}
|
|
12772
13125
|
entry.badge.classList.toggle("is-active", mark.id === activeId);
|
|
13126
|
+
entry.badge.classList.toggle("is-paired", pairedIds.has(mark.id));
|
|
12773
13127
|
entry.badge.classList.toggle("is-resolved", quietIds.has(mark.id));
|
|
12774
|
-
|
|
13128
|
+
rows.get(img)?.insertBefore(entry.badge, controls.get(img) ?? null);
|
|
12775
13129
|
wireFrameMark(entry.badge, mark.id, doc);
|
|
13130
|
+
entry.badge.addEventListener("focus", () => {
|
|
13131
|
+
revealBadge(entry);
|
|
13132
|
+
reportRows();
|
|
13133
|
+
});
|
|
12776
13134
|
}
|
|
12777
13135
|
entry.image = img;
|
|
12778
13136
|
entry.declaration = declaration;
|
|
12779
|
-
position(entry.badge, currentRect, offset);
|
|
12780
13137
|
Object.assign(entry.outline.style, {
|
|
12781
13138
|
left: `${currentRect.left}px`,
|
|
12782
13139
|
top: `${currentRect.top}px`,
|
|
12783
13140
|
width: `${currentRect.width}px`,
|
|
12784
13141
|
height: `${currentRect.height}px`
|
|
12785
13142
|
});
|
|
12786
|
-
|
|
12787
|
-
if (
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
requestId: mark.requestId,
|
|
12792
|
-
status: "placed",
|
|
12793
|
-
rect: box
|
|
12794
|
-
});
|
|
12795
|
-
if (tracked() === mark.id)
|
|
12796
|
-
post({ type: "relic:frame-mark-rect", id: mark.id, rect: box });
|
|
12797
|
-
} else {
|
|
12798
|
-
remove(entry);
|
|
12799
|
-
post({
|
|
12800
|
-
type: "relic:frame-image-placement",
|
|
12801
|
-
id: mark.id,
|
|
12802
|
-
requestId: mark.requestId,
|
|
12803
|
-
status: "unavailable"
|
|
12804
|
-
});
|
|
13143
|
+
layoutRow(img);
|
|
13144
|
+
if (created) {
|
|
13145
|
+
const row = rows.get(img);
|
|
13146
|
+
if (row)
|
|
13147
|
+
row.scrollLeft = row.scrollWidth;
|
|
12805
13148
|
}
|
|
13149
|
+
if (created && (mark.id === activeId || mark.id === tracked()))
|
|
13150
|
+
revealBadge(entry);
|
|
13151
|
+
scheduleRowReport();
|
|
12806
13152
|
}).catch(() => {
|
|
12807
13153
|
if (entries.get(mark.id) !== entry || entry.serial !== generation)
|
|
12808
13154
|
return;
|
|
@@ -12833,6 +13179,8 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12833
13179
|
characterData: true
|
|
12834
13180
|
});
|
|
12835
13181
|
}
|
|
13182
|
+
doc.addEventListener("pointermove", onPointerMove, true);
|
|
13183
|
+
doc.addEventListener("mouseout", onPointerLeave);
|
|
12836
13184
|
doc.addEventListener("load", schedule, true);
|
|
12837
13185
|
doc.addEventListener("scroll", schedule, true);
|
|
12838
13186
|
doc.addEventListener("DOMContentLoaded", schedule);
|
|
@@ -12844,6 +13192,8 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12844
13192
|
disposed = true;
|
|
12845
13193
|
mutationObserver?.disconnect();
|
|
12846
13194
|
resizeObserver?.disconnect();
|
|
13195
|
+
doc.removeEventListener("pointermove", onPointerMove, true);
|
|
13196
|
+
doc.removeEventListener("mouseout", onPointerLeave);
|
|
12847
13197
|
doc.removeEventListener("load", schedule, true);
|
|
12848
13198
|
doc.removeEventListener("scroll", schedule, true);
|
|
12849
13199
|
doc.removeEventListener("DOMContentLoaded", schedule);
|
|
@@ -12851,13 +13201,53 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12851
13201
|
win.removeEventListener?.("scroll", schedule);
|
|
12852
13202
|
entries.clear();
|
|
12853
13203
|
controls.clear();
|
|
13204
|
+
rows.clear();
|
|
12854
13205
|
layer?.remove();
|
|
12855
13206
|
},
|
|
12856
13207
|
active(id) {
|
|
12857
13208
|
activeId = id;
|
|
13209
|
+
for (const entry of entries.values())
|
|
13210
|
+
entry.badge?.classList.toggle("is-active", entry.mark.id === id);
|
|
13211
|
+
const entry = id && entries.get(id);
|
|
13212
|
+
if (entry)
|
|
13213
|
+
revealBadge(entry);
|
|
13214
|
+
reportRows();
|
|
13215
|
+
},
|
|
13216
|
+
rect(id) {
|
|
13217
|
+
const entry = entries.get(id);
|
|
13218
|
+
if (!entry)
|
|
13219
|
+
return;
|
|
13220
|
+
const message = { type: "relic:frame-mark-rect", id };
|
|
13221
|
+
if (!entry.badge || !entry.image || entry.declaration !== source(entry.image))
|
|
13222
|
+
return message;
|
|
13223
|
+
const row = rows.get(entry.image);
|
|
13224
|
+
const box = markFrameRect(entry.badge);
|
|
13225
|
+
const clip = row && markFrameRect(row);
|
|
13226
|
+
if (!visible(entry.image) || !box || !clip || row?.hidden)
|
|
13227
|
+
return {
|
|
13228
|
+
...message,
|
|
13229
|
+
concealed: isRendered(entry.image) ? "offscreen" : "hidden"
|
|
13230
|
+
};
|
|
13231
|
+
if (box.left < clip.left + ROW_PADDING - 0.5 || box.left + box.width > clip.left + clip.width - ROW_PADDING + 0.5)
|
|
13232
|
+
return { ...message, concealed: "offscreen" };
|
|
13233
|
+
return { ...message, rect: box };
|
|
13234
|
+
},
|
|
13235
|
+
repaint() {
|
|
13236
|
+
if (!enabled || disposed)
|
|
13237
|
+
return;
|
|
13238
|
+
refresh();
|
|
12858
13239
|
},
|
|
12859
13240
|
quiet(ids) {
|
|
12860
13241
|
quietIds = new Set(ids);
|
|
13242
|
+
for (const entry of entries.values())
|
|
13243
|
+
entry.badge?.classList.toggle("is-resolved", quietIds.has(entry.mark.id));
|
|
13244
|
+
},
|
|
13245
|
+
pair(id, active) {
|
|
13246
|
+
if (active)
|
|
13247
|
+
pairedIds.add(id);
|
|
13248
|
+
else
|
|
13249
|
+
pairedIds.delete(id);
|
|
13250
|
+
entries.get(id)?.badge?.classList.toggle("is-paired", active);
|
|
12861
13251
|
},
|
|
12862
13252
|
paint(mark) {
|
|
12863
13253
|
if (!enabled) {
|
|
@@ -12900,6 +13290,7 @@ function setupImageMarks(doc, win, post, tracked, enabled) {
|
|
|
12900
13290
|
if (entries.get(id) !== entry || digest !== entry.mark.source_sha256 || source(img) !== declaration || resolve(entry.mark.path) !== img || !img.isConnected)
|
|
12901
13291
|
return;
|
|
12902
13292
|
img.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
13293
|
+
revealBadge(entry);
|
|
12903
13294
|
schedule();
|
|
12904
13295
|
}).catch(() => {});
|
|
12905
13296
|
}
|
|
@@ -12924,8 +13315,51 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
12924
13315
|
let selectionLive = false;
|
|
12925
13316
|
let trackedId = null;
|
|
12926
13317
|
let trackRaf;
|
|
12927
|
-
|
|
13318
|
+
let parentVisible = true;
|
|
13319
|
+
let concealScheduled = false;
|
|
13320
|
+
let concealedIds = new Map;
|
|
13321
|
+
let trackedConcealment;
|
|
13322
|
+
const conceal = () => {
|
|
13323
|
+
concealScheduled = false;
|
|
13324
|
+
if (!doc.body || typeof doc.body.querySelectorAll !== "function")
|
|
13325
|
+
return;
|
|
13326
|
+
const marks = [];
|
|
13327
|
+
const overlay = frameRegionOverlays.get(doc);
|
|
13328
|
+
if (overlay) {
|
|
13329
|
+
for (const node of Array.from(overlay.querySelectorAll("[data-comment-id]"))) {
|
|
13330
|
+
marks.push({ node, subject: markSubjects.get(node) ?? node });
|
|
13331
|
+
}
|
|
13332
|
+
}
|
|
13333
|
+
for (const node of Array.from(doc.body.querySelectorAll("mark.relic-text-mark[data-comment-id]"))) {
|
|
13334
|
+
marks.push({ node, subject: node });
|
|
13335
|
+
}
|
|
13336
|
+
concealedIds = concealMarks(marks, watch, parentVisible);
|
|
13337
|
+
const next = trackedId === null ? undefined : concealedIds.get(trackedId);
|
|
13338
|
+
if (next !== trackedConcealment) {
|
|
13339
|
+
trackedConcealment = next;
|
|
13340
|
+
reportTracked();
|
|
13341
|
+
}
|
|
13342
|
+
};
|
|
13343
|
+
const scheduleConceal = () => {
|
|
13344
|
+
if (concealScheduled)
|
|
13345
|
+
return;
|
|
13346
|
+
concealScheduled = true;
|
|
13347
|
+
if (typeof requestAnimationFrame === "function") {
|
|
13348
|
+
requestAnimationFrame(conceal);
|
|
13349
|
+
} else {
|
|
13350
|
+
setTimeout(conceal, 0);
|
|
13351
|
+
}
|
|
13352
|
+
};
|
|
13353
|
+
const watch = createScrollportWatch(scheduleConceal);
|
|
13354
|
+
const imageMarks = setupImageMarks(doc, win, postOutward, () => trackedId, imageControlsEnabled, () => parentVisible);
|
|
12928
13355
|
const reportMarkRect = (document2, id) => {
|
|
13356
|
+
const image = imageMarks.rect(id);
|
|
13357
|
+
if (image)
|
|
13358
|
+
return image;
|
|
13359
|
+
const concealed = concealedIds.get(id);
|
|
13360
|
+
if (concealed !== undefined) {
|
|
13361
|
+
return { type: "relic:frame-mark-rect", id, concealed };
|
|
13362
|
+
}
|
|
12929
13363
|
if (!document2.body) {
|
|
12930
13364
|
return { type: "relic:frame-mark-rect", id };
|
|
12931
13365
|
}
|
|
@@ -13007,6 +13441,7 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
13007
13441
|
postOutward(report);
|
|
13008
13442
|
};
|
|
13009
13443
|
const scheduleTrackReport = () => {
|
|
13444
|
+
scheduleConceal();
|
|
13010
13445
|
if (trackedId === null)
|
|
13011
13446
|
return;
|
|
13012
13447
|
if (typeof requestAnimationFrame === "function") {
|
|
@@ -13047,6 +13482,18 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
13047
13482
|
});
|
|
13048
13483
|
win.addEventListener("resize", () => scheduleTrackReport());
|
|
13049
13484
|
}
|
|
13485
|
+
if (typeof doc.addEventListener === "function") {
|
|
13486
|
+
doc.addEventListener("scroll", scheduleConceal, true);
|
|
13487
|
+
doc.addEventListener("toggle", scheduleConceal, true);
|
|
13488
|
+
}
|
|
13489
|
+
const markObserver = typeof MutationObserver === "function" && doc.nodeType === 9 ? new MutationObserver(scheduleConceal) : undefined;
|
|
13490
|
+
markObserver?.observe(doc, {
|
|
13491
|
+
subtree: true,
|
|
13492
|
+
childList: true,
|
|
13493
|
+
attributes: true,
|
|
13494
|
+
attributeFilter: ["class", "hidden", "open", "style"]
|
|
13495
|
+
});
|
|
13496
|
+
scheduleConceal();
|
|
13050
13497
|
doc.addEventListener("click", (event) => {
|
|
13051
13498
|
let node = event.target;
|
|
13052
13499
|
if (node?.nodeType !== 1)
|
|
@@ -13073,7 +13520,7 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
13073
13520
|
if (!armed)
|
|
13074
13521
|
return;
|
|
13075
13522
|
const target = event.target;
|
|
13076
|
-
if (typeof target?.closest === "function" && target.closest(".relic-region-mark, .relic-pin-mark, .relic-image-
|
|
13523
|
+
if (typeof target?.closest === "function" && target.closest(".relic-region-mark, .relic-pin-mark, .relic-image-row") !== null) {
|
|
13077
13524
|
return;
|
|
13078
13525
|
}
|
|
13079
13526
|
event.preventDefault?.();
|
|
@@ -13319,6 +13766,7 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
13319
13766
|
}
|
|
13320
13767
|
},
|
|
13321
13768
|
onPairMark(id, active) {
|
|
13769
|
+
imageMarks.pair(id, active);
|
|
13322
13770
|
if (!doc.body)
|
|
13323
13771
|
return;
|
|
13324
13772
|
const targets = doc.body.querySelectorAll(`[data-comment-id="${id}"]`);
|
|
@@ -13387,28 +13835,44 @@ function setupFrameInteraction(doc, win, postOutward, imageControlsEnabled = tru
|
|
|
13387
13835
|
suppressScroll = false;
|
|
13388
13836
|
}, 50);
|
|
13389
13837
|
},
|
|
13838
|
+
onFrameVisibility(visible) {
|
|
13839
|
+
parentVisible = visible;
|
|
13840
|
+
imageMarks.repaint();
|
|
13841
|
+
conceal();
|
|
13842
|
+
},
|
|
13390
13843
|
dispose() {
|
|
13391
13844
|
imageMarks.dispose();
|
|
13845
|
+
if (typeof doc.removeEventListener === "function") {
|
|
13846
|
+
doc.removeEventListener("scroll", scheduleConceal, true);
|
|
13847
|
+
doc.removeEventListener("toggle", scheduleConceal, true);
|
|
13848
|
+
}
|
|
13849
|
+
watch.disconnect();
|
|
13850
|
+
markObserver?.disconnect();
|
|
13392
13851
|
}
|
|
13393
13852
|
};
|
|
13394
13853
|
}
|
|
13395
13854
|
function createSandboxHandler(write, writeJsx, annotate = () => {}, interaction) {
|
|
13396
13855
|
let rendered = false;
|
|
13397
13856
|
let annotated = false;
|
|
13857
|
+
let cover = false;
|
|
13398
13858
|
return (data) => {
|
|
13399
13859
|
if (!rendered) {
|
|
13400
13860
|
if (isRenderMessage(data)) {
|
|
13401
13861
|
rendered = true;
|
|
13402
|
-
|
|
13862
|
+
cover = data.cover === true;
|
|
13863
|
+
write(data.html, !cover);
|
|
13403
13864
|
return true;
|
|
13404
13865
|
}
|
|
13405
13866
|
if (isRenderJsxMessage(data)) {
|
|
13406
13867
|
rendered = true;
|
|
13868
|
+
cover = data.cover === true;
|
|
13407
13869
|
writeJsx(data.code);
|
|
13408
13870
|
return true;
|
|
13409
13871
|
}
|
|
13410
13872
|
return false;
|
|
13411
13873
|
}
|
|
13874
|
+
if (cover)
|
|
13875
|
+
return false;
|
|
13412
13876
|
if (!annotated && isAnnotateMessage(data)) {
|
|
13413
13877
|
annotated = true;
|
|
13414
13878
|
annotate(data.marks, data.anchors);
|
|
@@ -13440,6 +13904,10 @@ function createSandboxHandler(write, writeJsx, annotate = () => {}, interaction)
|
|
|
13440
13904
|
interaction?.onTrackMark?.(data.id);
|
|
13441
13905
|
return true;
|
|
13442
13906
|
}
|
|
13907
|
+
if (isFrameVisibilityMessage(data)) {
|
|
13908
|
+
interaction?.onFrameVisibility?.(data.visible);
|
|
13909
|
+
return true;
|
|
13910
|
+
}
|
|
13443
13911
|
if (isActiveMarkMessage(data)) {
|
|
13444
13912
|
interaction?.onActiveMark?.(data.id);
|
|
13445
13913
|
return true;
|
|
@@ -13818,10 +14286,10 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
|
13818
14286
|
};
|
|
13819
14287
|
installSandboxContainment(window, document);
|
|
13820
14288
|
let interaction;
|
|
13821
|
-
const handle = createSandboxHandler((html) => {
|
|
14289
|
+
const handle = createSandboxHandler((html, imageControlsEnabled) => {
|
|
13822
14290
|
document.open();
|
|
13823
14291
|
installSandboxContainment(window, document);
|
|
13824
|
-
initInteraction(
|
|
14292
|
+
initInteraction(imageControlsEnabled);
|
|
13825
14293
|
document.write(sanitizeFrameHtml(html));
|
|
13826
14294
|
document.close();
|
|
13827
14295
|
listen();
|
|
@@ -13845,7 +14313,9 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
|
13845
14313
|
onRevealMark: (msg) => interaction?.onRevealMark(msg),
|
|
13846
14314
|
onPairMark: (id, active) => interaction?.onPairMark(id, active),
|
|
13847
14315
|
onTrackMark: (id) => interaction?.onTrackMark?.(id),
|
|
14316
|
+
onFrameVisibility: (visible) => interaction?.onFrameVisibility?.(visible),
|
|
13848
14317
|
onActiveMark: (id) => interaction?.onActiveMark?.(id),
|
|
14318
|
+
onQuietMarks: (ids) => interaction?.onQuietMarks?.(ids),
|
|
13849
14319
|
onRevealChange: (changeId, top) => interaction?.onRevealChange?.(changeId, top),
|
|
13850
14320
|
onSetScroll: (fraction) => interaction?.onSetScroll?.(fraction)
|
|
13851
14321
|
});
|
package/dist/cover/styles.css
CHANGED
|
@@ -6205,3 +6205,23 @@ canvas.relic-page {
|
|
|
6205
6205
|
background: var(--diff-added);
|
|
6206
6206
|
border-left: 2px solid var(--patina);
|
|
6207
6207
|
}
|
|
6208
|
+
|
|
6209
|
+
/* A mark the viewer does not draw. A hidden overlay leaves layout, because its
|
|
6210
|
+
thing has no box. An offscreen overlay is made transparent and keeps its box
|
|
6211
|
+
and hit testing: reveal measures both after a scroll, before the observer
|
|
6212
|
+
has caught up. A text mark keeps its words and loses only its decoration.
|
|
6213
|
+
The frame carries the same rules in CONCEALMENT_CSS (mark-visibility.ts),
|
|
6214
|
+
because it cannot read this sheet. */
|
|
6215
|
+
[data-concealed='hidden']:not(mark) {
|
|
6216
|
+
display: none !important;
|
|
6217
|
+
}
|
|
6218
|
+
|
|
6219
|
+
[data-concealed='offscreen']:not(mark) {
|
|
6220
|
+
opacity: 0 !important;
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6223
|
+
mark[data-concealed] {
|
|
6224
|
+
background: none !important;
|
|
6225
|
+
border-bottom-color: transparent !important;
|
|
6226
|
+
box-shadow: none !important;
|
|
6227
|
+
}
|
package/dist/relic-mcp.js
CHANGED
|
@@ -36993,7 +36993,7 @@ var DESCRIBE_TOOL_DEFINITION = {
|
|
|
36993
36993
|
additionalProperties: false
|
|
36994
36994
|
}
|
|
36995
36995
|
};
|
|
36996
|
-
var SERVER_VERSION = "0.14.
|
|
36996
|
+
var SERVER_VERSION = "0.14.3";
|
|
36997
36997
|
var SERVER_INFO = {
|
|
36998
36998
|
name: "relic",
|
|
36999
36999
|
title: "Relic",
|
package/mcp-servers.json
CHANGED
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"nodeApiVersion": 8,
|
|
21
21
|
"schemaVersion": 3,
|
|
22
|
-
"sourceSha256": "
|
|
22
|
+
"sourceSha256": "e00e11398621da867c16ea823bb6c2fcd83044340ab7f4549670bc16ea00c1ab",
|
|
23
23
|
"target": "darwin-arm64",
|
|
24
24
|
"toolchain": {
|
|
25
25
|
"binaryDeploymentTarget": "15.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"nodeApiVersion": 8,
|
|
21
21
|
"schemaVersion": 3,
|
|
22
|
-
"sourceSha256": "
|
|
22
|
+
"sourceSha256": "e00e11398621da867c16ea823bb6c2fcd83044340ab7f4549670bc16ea00c1ab",
|
|
23
23
|
"target": "darwin-x64",
|
|
24
24
|
"toolchain": {
|
|
25
25
|
"binaryDeploymentTarget": "15.0",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"nodeApiVersion": 8,
|
|
22
22
|
"schemaVersion": 3,
|
|
23
|
-
"sourceSha256": "
|
|
23
|
+
"sourceSha256": "e00e11398621da867c16ea823bb6c2fcd83044340ab7f4549670bc16ea00c1ab",
|
|
24
24
|
"target": "linux-arm64-gnu",
|
|
25
25
|
"toolchain": {
|
|
26
26
|
"compilerPath": "/opt/rh/gcc-toolset-14/root/usr/bin/gcc",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"nodeApiVersion": 8,
|
|
22
22
|
"schemaVersion": 3,
|
|
23
|
-
"sourceSha256": "
|
|
23
|
+
"sourceSha256": "e00e11398621da867c16ea823bb6c2fcd83044340ab7f4549670bc16ea00c1ab",
|
|
24
24
|
"target": "linux-x64-gnu",
|
|
25
25
|
"toolchain": {
|
|
26
26
|
"compilerPath": "/opt/rh/gcc-toolset-14/root/usr/bin/gcc",
|