dsh-web-annotator 0.0.0-stage → 0.2.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +112 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +110 -2
- package/README_en.md +136 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/cordis.patch.yml +15 -0
- package/docs/AGENTS.md +16 -0
- package/docs/README.md +25 -0
- package/docs/architecture.md +170 -0
- package/docs/browser-integration.md +94 -0
- package/docs/distribution.md +66 -0
- package/docs/harness-browser-annotation.patch +1909 -0
- package/docs/images/annotation-expanded-dark.png +0 -0
- package/docs/images/annotation-expanded.png +0 -0
- package/docs/images/annotation-image.png +0 -0
- package/docs/images/annotation-message.png +0 -0
- package/docs/images/annotation-region.png +0 -0
- package/docs/marketplace/cslkkl__dsh-web-annotator.yml +7 -0
- package/docs/publishing.md +129 -0
- package/docs/roadmap.md +31 -0
- package/docs/verification.md +179 -0
- package/dsh-plugin.naming.json +27 -0
- package/examples/responsive-demo/AGENTS.md +12 -0
- package/examples/responsive-demo/README.md +33 -0
- package/examples/responsive-demo/index.html +13 -0
- package/examples/responsive-demo/src/main.tsx +110 -0
- package/examples/responsive-demo/src/style.css +344 -0
- package/examples/responsive-demo/vite.config.ts +9 -0
- package/lib/THIRD_PARTY_LICENSES.md +345 -0
- package/lib/bridge.js +890 -0
- package/lib/bridge.js.map +7 -0
- package/lib/client.js +1083 -0
- package/lib/client.js.map +7 -0
- package/lib/host/THIRD_PARTY_NOTICES.md +15 -0
- package/lib/host/browser-provider/LICENSE +21 -0
- package/lib/host/browser-provider/README.md +130 -0
- package/lib/host/browser-provider/lib/client.css +217 -0
- package/lib/host/browser-provider/lib/client.css.map +7 -0
- package/lib/host/browser-provider/lib/client.js +2360 -0
- package/lib/host/browser-provider/lib/client.js.map +7 -0
- package/lib/host/browser-provider/lib/index.js +7 -0
- package/lib/host/browser-provider/lib/index.js.map +7 -0
- package/lib/host/browser-provider/package.json +23 -0
- package/lib/host/bundle.json +27 -0
- package/lib/host/chat-provider/LICENSE +21 -0
- package/lib/host/chat-provider/README.md +186 -0
- package/lib/host/chat-provider/lib/client.css +1604 -0
- package/lib/host/chat-provider/lib/client.css.map +7 -0
- package/lib/host/chat-provider/lib/client.js +11074 -0
- package/lib/host/chat-provider/lib/client.js.map +7 -0
- package/lib/host/chat-provider/lib/index.js +911 -0
- package/lib/host/chat-provider/lib/index.js.map +7 -0
- package/lib/host/chat-provider/package.json +31 -0
- package/lib/host/layout-provider/LICENSE +21 -0
- package/lib/host/layout-provider/README.md +109 -0
- package/lib/host/layout-provider/lib/client.css +177 -0
- package/lib/host/layout-provider/lib/client.css.map +7 -0
- package/lib/host/layout-provider/lib/client.js +565 -0
- package/lib/host/layout-provider/lib/client.js.map +7 -0
- package/lib/host/layout-provider/lib/index.js +7 -0
- package/lib/host/layout-provider/lib/index.js.map +7 -0
- package/lib/host/layout-provider/package.json +25 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +7 -0
- package/lib/vite.d.ts +8 -0
- package/lib/vite.js +1254 -0
- package/lib/vite.js.map +7 -0
- package/licenses/deepseek-harness-MIT.txt +21 -0
- package/locale/AGENTS.md +11 -0
- package/locale/README.md +11 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +154 -4
- package/screenshots.json +7 -0
package/lib/bridge.js
ADDED
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// src/browser/page-inspector.ts
|
|
4
|
+
function annotatePage(options) {
|
|
5
|
+
window.__DSH_PAGE_ANNOTATION_CANCEL__?.();
|
|
6
|
+
const doc = document;
|
|
7
|
+
const originalUrl = location.href;
|
|
8
|
+
const originalFocus = doc.activeElement;
|
|
9
|
+
const root = doc.createElement("div");
|
|
10
|
+
root.setAttribute("data-dsh-page-annotation", "");
|
|
11
|
+
root.style.cssText = "position:fixed;inset:0;z-index:2147483647;pointer-events:none;";
|
|
12
|
+
const shadow = root.attachShadow({ mode: "open" });
|
|
13
|
+
const style = doc.createElement("style");
|
|
14
|
+
style.textContent = `
|
|
15
|
+
:host{all:initial;color-scheme:${options.dark ? "dark" : "light"}}
|
|
16
|
+
*{box-sizing:border-box}
|
|
17
|
+
.outline{position:fixed;pointer-events:none;border:2px solid #2979ff;border-radius:5px;background:#2979ff0c}
|
|
18
|
+
.hint{position:fixed;top:12px;left:50%;transform:translateX(-50%);padding:8px 12px;border-radius:8px;background:#181a20;color:#fff;font:13px/20px system-ui;max-width:calc(100vw - 24px);pointer-events:none}
|
|
19
|
+
.editor,.options{position:fixed;pointer-events:auto;border:0;background:${options.dark ? "#292a2d" : "#fff"};color:${options.dark ? "#ededee" : "#303338"};box-shadow:0 0 0 .5px ${options.dark ? "#55565b" : "#dcdfe3"},0 2px 8px #00000014;font:14px/22px system-ui}
|
|
20
|
+
.editor{border-radius:26px;corner-shape:round;padding:7px 8px;width:min(384px,calc(100vw - 24px));overflow:hidden;max-height:calc(100vh - 24px);display:flex;flex-direction:column}
|
|
21
|
+
.editor.expanded{width:min(420px,calc(100vw - 24px));border-radius:24px;padding:0}
|
|
22
|
+
.expanded>.row{padding:12px 14px;flex:none}
|
|
23
|
+
.expanded>.row>.save{display:none}
|
|
24
|
+
.expanded textarea{max-height:min(66px,20vh)}
|
|
25
|
+
.editor:focus-within{box-shadow:0 0 0 .5px ${options.dark ? "#85868b" : "#bfc4cc"},0 2px 8px #00000014}
|
|
26
|
+
.row{display:flex;align-items:center;gap:10px}
|
|
27
|
+
button,select,textarea{font:inherit;color:inherit}
|
|
28
|
+
button{border:0;border-radius:8px;padding:6px 8px;background:transparent;cursor:pointer;flex:none}
|
|
29
|
+
button:disabled{opacity:.4;cursor:default}
|
|
30
|
+
button:hover:not(:disabled){background:${options.dark ? "#3b3c40" : "#f0f1f3"}}
|
|
31
|
+
button:focus-visible,select:focus-visible{outline:2px solid #2979ff;outline-offset:2px}
|
|
32
|
+
.options-toggle,.save{display:flex;align-items:center;justify-content:center;padding:0;width:30px;height:30px;border-radius:50%;corner-shape:round}
|
|
33
|
+
.options-toggle{color:${options.dark ? "#bdc0c5" : "#737880"}}
|
|
34
|
+
.save,.save:disabled{background:${options.dark ? "#797c82" : "#a2a5a9"};color:#fff;opacity:1}
|
|
35
|
+
.save:not(:disabled){background:${options.dark ? "#d2d3d6" : "#777c83"};color:${options.dark ? "#242529" : "#fff"}}
|
|
36
|
+
.save:hover:not(:disabled){background:${options.dark ? "#eeeff1" : "#60656d"}}
|
|
37
|
+
textarea{display:block;flex:1;width:100%;min-width:0;height:22px;max-height:110px;resize:none;border:0;background:transparent;padding:0;margin:0;outline:0;line-height:22px;overflow-y:auto}
|
|
38
|
+
textarea::placeholder{color:${options.dark ? "#aaaeb5" : "#888e96"};opacity:1}
|
|
39
|
+
.options{position:static;box-shadow:none;flex:1;min-height:0;overflow:auto;font-size:14px;line-height:22px;scrollbar-width:thin}
|
|
40
|
+
.target-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 20px;background:${options.dark ? "#35363a" : "#f4f4f4"}}
|
|
41
|
+
.target{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:${options.dark ? "#c9ccd1" : "#656b73"}}
|
|
42
|
+
.target-controls{display:flex;gap:2px;flex:none}
|
|
43
|
+
.target-controls button{font-size:13px;padding:0;width:24px;height:24px}
|
|
44
|
+
.properties{padding:12px 20px;display:grid;gap:10px}
|
|
45
|
+
.property{display:grid;grid-template-columns:minmax(90px,1fr) minmax(0,180px);align-items:center;gap:12px;color:${options.dark ? "#c9ccd1" : "#6e747b"}}
|
|
46
|
+
.field{display:flex;align-items:center;gap:9px;border:.5px solid ${options.dark ? "#57595f" : "#dedfe2"};border-radius:13px;padding:7px 10px;min-width:0}
|
|
47
|
+
.field input{font:13px/20px ui-monospace,monospace;background:transparent;color:inherit;border:0;outline:0;width:100%;min-width:0}
|
|
48
|
+
.field:focus-within{border-color:${options.dark ? "#a2a5ac" : "#a5a9b1"}}
|
|
49
|
+
.swatch{width:22px;height:22px;border-radius:7px;flex:none;box-shadow:inset 0 0 0 .5px #8886}
|
|
50
|
+
.intent{display:flex;align-items:center;justify-content:space-between;padding:0 20px 12px;gap:8px;font-size:13px;color:${options.dark ? "#c9ccd1" : "#6e747b"}}
|
|
51
|
+
select{font:inherit;border:.5px solid ${options.dark ? "#57595f" : "#dedfe2"};background:transparent;border-radius:10px;padding:5px 8px;max-width:160px}
|
|
52
|
+
.footer{border-top:.5px solid ${options.dark ? "#44464b" : "#eee"};display:flex;align-items:center;justify-content:space-between;padding:10px 12px;flex:none}
|
|
53
|
+
.footer>.cancel{border:.5px solid ${options.dark ? "#55565b" : "#e5e6e9"};border-radius:20px;padding:6px 12px;font-size:13px}
|
|
54
|
+
.style-hint{font-size:12px;line-height:18px;margin:3px 0;color:${options.dark ? "#adb1b8" : "#8a9097"}}
|
|
55
|
+
.error{color:${options.dark ? "#ffb0a9" : "#b82920"};font-size:12px;padding:3px 40px 1px}
|
|
56
|
+
.hidden{display:none!important}
|
|
57
|
+
`;
|
|
58
|
+
const outline = doc.createElement("div");
|
|
59
|
+
outline.className = "outline hidden";
|
|
60
|
+
const hint = doc.createElement("div");
|
|
61
|
+
hint.className = "hint";
|
|
62
|
+
hint.textContent = options.copy.select;
|
|
63
|
+
const editor = doc.createElement("form");
|
|
64
|
+
editor.className = "editor hidden";
|
|
65
|
+
editor.setAttribute("aria-label", options.copy.placeholder);
|
|
66
|
+
const row = doc.createElement("div");
|
|
67
|
+
row.className = "row";
|
|
68
|
+
function icon(parts) {
|
|
69
|
+
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
70
|
+
for (const [name, value] of Object.entries({
|
|
71
|
+
width: "18",
|
|
72
|
+
height: "18",
|
|
73
|
+
viewBox: "0 0 16 16",
|
|
74
|
+
fill: "none",
|
|
75
|
+
stroke: "currentColor",
|
|
76
|
+
"stroke-width": "1.3",
|
|
77
|
+
"stroke-linecap": "round",
|
|
78
|
+
"stroke-linejoin": "round",
|
|
79
|
+
"aria-hidden": "true"
|
|
80
|
+
}))
|
|
81
|
+
svg.setAttribute(name, value);
|
|
82
|
+
for (const [tag, attributes] of parts) {
|
|
83
|
+
const node = doc.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
84
|
+
for (const [name, value] of Object.entries(attributes)) node.setAttribute(name, value);
|
|
85
|
+
svg.append(node);
|
|
86
|
+
}
|
|
87
|
+
return svg;
|
|
88
|
+
}
|
|
89
|
+
const settings = doc.createElement("button");
|
|
90
|
+
settings.type = "button";
|
|
91
|
+
settings.className = "options-toggle";
|
|
92
|
+
settings.title = options.copy.settings;
|
|
93
|
+
settings.setAttribute("aria-label", options.copy.settings);
|
|
94
|
+
settings.setAttribute("aria-expanded", "false");
|
|
95
|
+
settings.setAttribute("aria-haspopup", "dialog");
|
|
96
|
+
settings.append(
|
|
97
|
+
icon([
|
|
98
|
+
["path", { d: "M2.3 5h5.85M12.05 5h1.65" }],
|
|
99
|
+
["circle", { cx: "9.95", cy: "5", r: "1.45" }],
|
|
100
|
+
["path", { d: "M2.3 11h1.65M7.85 11h5.85" }],
|
|
101
|
+
["circle", { cx: "5.75", cy: "11", r: "1.45" }]
|
|
102
|
+
])
|
|
103
|
+
);
|
|
104
|
+
const optionsPanel = doc.createElement("section");
|
|
105
|
+
optionsPanel.className = "options hidden";
|
|
106
|
+
optionsPanel.setAttribute("role", "dialog");
|
|
107
|
+
optionsPanel.setAttribute("aria-label", options.copy.settings);
|
|
108
|
+
const intentRow = doc.createElement("div");
|
|
109
|
+
intentRow.className = "intent";
|
|
110
|
+
const intentLabel = doc.createElement("span");
|
|
111
|
+
intentLabel.textContent = options.copy.comment + " / " + options.copy.question;
|
|
112
|
+
const intent = doc.createElement("select");
|
|
113
|
+
intent.setAttribute("aria-label", options.copy.comment + " / " + options.copy.question);
|
|
114
|
+
for (const [value, label] of [
|
|
115
|
+
["comment", options.copy.comment],
|
|
116
|
+
["question", options.copy.question]
|
|
117
|
+
]) {
|
|
118
|
+
const option = doc.createElement("option");
|
|
119
|
+
option.value = value;
|
|
120
|
+
option.textContent = label;
|
|
121
|
+
intent.append(option);
|
|
122
|
+
}
|
|
123
|
+
intent.value = options.intent;
|
|
124
|
+
const input = doc.createElement("textarea");
|
|
125
|
+
input.placeholder = options.copy.placeholder;
|
|
126
|
+
input.setAttribute("aria-label", options.copy.placeholder);
|
|
127
|
+
input.maxLength = 1e3;
|
|
128
|
+
const save = doc.createElement("button");
|
|
129
|
+
save.type = "submit";
|
|
130
|
+
save.className = "save";
|
|
131
|
+
save.disabled = true;
|
|
132
|
+
save.append(
|
|
133
|
+
icon([
|
|
134
|
+
[
|
|
135
|
+
"path",
|
|
136
|
+
{ d: "M2.25 8.5L5.49732 11.7473C5.90519 12.1552 6.57263 12.1344 6.95426 11.7018L13.75 4" }
|
|
137
|
+
]
|
|
138
|
+
])
|
|
139
|
+
);
|
|
140
|
+
save.title = options.copy.save;
|
|
141
|
+
save.setAttribute("aria-label", options.copy.save);
|
|
142
|
+
row.append(settings, input, save);
|
|
143
|
+
const targetLabel = doc.createElement("span");
|
|
144
|
+
targetLabel.className = "target";
|
|
145
|
+
const controls = doc.createElement("div");
|
|
146
|
+
controls.className = "controls";
|
|
147
|
+
function control(label, glyph) {
|
|
148
|
+
const button = doc.createElement("button");
|
|
149
|
+
button.type = "button";
|
|
150
|
+
button.title = label;
|
|
151
|
+
button.setAttribute("aria-label", label);
|
|
152
|
+
button.textContent = glyph + " " + label;
|
|
153
|
+
controls.append(button);
|
|
154
|
+
return button;
|
|
155
|
+
}
|
|
156
|
+
const parent = control(options.copy.parent, "\u2191");
|
|
157
|
+
const child = control(options.copy.child, "\u2193");
|
|
158
|
+
const cancel = control(options.copy.cancel, "\xD7");
|
|
159
|
+
const error = doc.createElement("div");
|
|
160
|
+
error.className = "error hidden";
|
|
161
|
+
error.setAttribute("role", "alert");
|
|
162
|
+
intentRow.append(intentLabel, intent);
|
|
163
|
+
const targetRow = doc.createElement("div");
|
|
164
|
+
targetRow.className = "target-row";
|
|
165
|
+
controls.className = "target-controls";
|
|
166
|
+
cancel.remove();
|
|
167
|
+
parent.textContent = "\u2191";
|
|
168
|
+
child.textContent = "\u2193";
|
|
169
|
+
targetRow.append(targetLabel, controls);
|
|
170
|
+
const properties = doc.createElement("div");
|
|
171
|
+
properties.className = "properties";
|
|
172
|
+
const styleHint = doc.createElement("p");
|
|
173
|
+
styleHint.className = "style-hint";
|
|
174
|
+
const fields = /* @__PURE__ */ new Map();
|
|
175
|
+
for (const [property, label] of [
|
|
176
|
+
["color", options.copy.color],
|
|
177
|
+
["background-color", options.copy.background],
|
|
178
|
+
["opacity", options.copy.opacity],
|
|
179
|
+
["font-family", options.copy.font],
|
|
180
|
+
["font-size", options.copy.fontSize],
|
|
181
|
+
["font-weight", options.copy.fontWeight],
|
|
182
|
+
["gap", options.copy.spacing]
|
|
183
|
+
]) {
|
|
184
|
+
const item = doc.createElement("label");
|
|
185
|
+
item.className = "property";
|
|
186
|
+
const labelText = doc.createElement("span");
|
|
187
|
+
labelText.textContent = label;
|
|
188
|
+
const field = doc.createElement("div");
|
|
189
|
+
field.className = "field";
|
|
190
|
+
const value = doc.createElement("input");
|
|
191
|
+
value.setAttribute("aria-label", label);
|
|
192
|
+
value.dataset.property = property;
|
|
193
|
+
value.maxLength = 1e3;
|
|
194
|
+
let swatch;
|
|
195
|
+
if (property === "color" || property === "background-color") {
|
|
196
|
+
swatch = doc.createElement("span");
|
|
197
|
+
swatch.className = "swatch";
|
|
198
|
+
swatch.setAttribute("aria-hidden", "true");
|
|
199
|
+
field.append(swatch);
|
|
200
|
+
}
|
|
201
|
+
field.append(value);
|
|
202
|
+
item.append(labelText, field);
|
|
203
|
+
properties.append(item);
|
|
204
|
+
fields.set(property, { input: value, before: "", ...swatch ? { swatch } : {} });
|
|
205
|
+
}
|
|
206
|
+
properties.append(styleHint);
|
|
207
|
+
const footer = doc.createElement("div");
|
|
208
|
+
footer.className = "footer hidden";
|
|
209
|
+
cancel.className = "cancel";
|
|
210
|
+
cancel.textContent = options.copy.cancel;
|
|
211
|
+
const expandedSave = save.cloneNode(true);
|
|
212
|
+
footer.append(cancel, expandedSave);
|
|
213
|
+
optionsPanel.append(targetRow, properties, intentRow);
|
|
214
|
+
editor.append(row, optionsPanel, error, footer);
|
|
215
|
+
shadow.append(style, outline, hint, editor);
|
|
216
|
+
const surface = doc.createElement("div");
|
|
217
|
+
surface.style.cssText = "position:fixed;inset:0;pointer-events:auto;cursor:crosshair;touch-action:none";
|
|
218
|
+
shadow.prepend(surface);
|
|
219
|
+
doc.documentElement.append(root);
|
|
220
|
+
let target = null;
|
|
221
|
+
let hovered = null;
|
|
222
|
+
let selection = null;
|
|
223
|
+
let gesture = null;
|
|
224
|
+
let suspended = false;
|
|
225
|
+
let suppressClick = false;
|
|
226
|
+
const descendants = [];
|
|
227
|
+
let animation = 0;
|
|
228
|
+
let ended = false;
|
|
229
|
+
let settle;
|
|
230
|
+
const result = new Promise((resolve) => {
|
|
231
|
+
settle = resolve;
|
|
232
|
+
});
|
|
233
|
+
const lifecycle = new AbortController();
|
|
234
|
+
function changes() {
|
|
235
|
+
if (selection?.kind !== "element") return {};
|
|
236
|
+
return Object.fromEntries(
|
|
237
|
+
[...fields].flatMap(([key, field]) => {
|
|
238
|
+
let after = field.input.value.trim();
|
|
239
|
+
if ((key === "font-size" || key === "gap") && /^\d+(\.\d+)?$/.test(after)) after += "px";
|
|
240
|
+
return after === field.before ? [] : [[key, { before: field.before, after }]];
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
function refreshProperties(element) {
|
|
245
|
+
const computed = element ? getComputedStyle(element) : null;
|
|
246
|
+
for (const [key, field] of fields) {
|
|
247
|
+
field.before = computed?.getPropertyValue(key) || "";
|
|
248
|
+
field.input.value = field.before;
|
|
249
|
+
field.input.disabled = !element;
|
|
250
|
+
if (field.swatch) field.swatch.style.background = field.before;
|
|
251
|
+
}
|
|
252
|
+
properties.classList.toggle("area", !element);
|
|
253
|
+
for (const field of fields.values())
|
|
254
|
+
field.input.closest(".property")?.classList.toggle("hidden", !element);
|
|
255
|
+
styleHint.textContent = element ? options.copy.styleHint : options.copy.elementOnly;
|
|
256
|
+
}
|
|
257
|
+
for (const [key, field] of fields) {
|
|
258
|
+
field.input.addEventListener(
|
|
259
|
+
"input",
|
|
260
|
+
() => {
|
|
261
|
+
if (field.swatch && CSS.supports(key, field.input.value))
|
|
262
|
+
field.swatch.style.background = field.input.value;
|
|
263
|
+
intent.value = "comment";
|
|
264
|
+
error.classList.add("hidden");
|
|
265
|
+
draw();
|
|
266
|
+
},
|
|
267
|
+
{ signal: lifecycle.signal }
|
|
268
|
+
);
|
|
269
|
+
field.input.addEventListener(
|
|
270
|
+
"keydown",
|
|
271
|
+
(event) => {
|
|
272
|
+
if (event.key === "Enter" && !event.isComposing && event.keyCode !== 229) {
|
|
273
|
+
event.preventDefault();
|
|
274
|
+
event.stopPropagation();
|
|
275
|
+
submit(event);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
{ signal: lifecycle.signal }
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const observer = new ResizeObserver(draw);
|
|
282
|
+
function finish(value) {
|
|
283
|
+
if (ended) return;
|
|
284
|
+
ended = true;
|
|
285
|
+
lifecycle.abort();
|
|
286
|
+
cancelAnimationFrame(animation);
|
|
287
|
+
observer.disconnect();
|
|
288
|
+
root.remove();
|
|
289
|
+
if (window.__DSH_PAGE_ANNOTATION_CANCEL__ === dismiss)
|
|
290
|
+
delete window.__DSH_PAGE_ANNOTATION_CANCEL__;
|
|
291
|
+
if (originalFocus instanceof HTMLElement && originalFocus.isConnected)
|
|
292
|
+
originalFocus.focus({ preventScroll: true });
|
|
293
|
+
settle(value);
|
|
294
|
+
}
|
|
295
|
+
function dismiss() {
|
|
296
|
+
finish({ cancelled: true });
|
|
297
|
+
}
|
|
298
|
+
window.__DSH_PAGE_ANNOTATION_CANCEL__ = dismiss;
|
|
299
|
+
function showError(message) {
|
|
300
|
+
error.textContent = message;
|
|
301
|
+
error.classList.remove("hidden");
|
|
302
|
+
}
|
|
303
|
+
function valid(element) {
|
|
304
|
+
return !!element && element !== doc.body && element !== doc.documentElement && !element.closest("[data-dsh-page-annotation]");
|
|
305
|
+
}
|
|
306
|
+
function hit(x, y) {
|
|
307
|
+
surface.style.pointerEvents = "none";
|
|
308
|
+
editor.style.pointerEvents = "none";
|
|
309
|
+
let node = doc.elementFromPoint(x, y);
|
|
310
|
+
while (node?.shadowRoot) {
|
|
311
|
+
const inner = node.shadowRoot.elementFromPoint(x, y);
|
|
312
|
+
if (!inner || inner === node) break;
|
|
313
|
+
node = inner;
|
|
314
|
+
}
|
|
315
|
+
surface.style.pointerEvents = suspended ? "none" : "auto";
|
|
316
|
+
editor.style.pointerEvents = "auto";
|
|
317
|
+
return valid(node) ? node : null;
|
|
318
|
+
}
|
|
319
|
+
function rectInDocument(element) {
|
|
320
|
+
const rect = element.getBoundingClientRect();
|
|
321
|
+
return { x: rect.x + scrollX, y: rect.y + scrollY, width: rect.width, height: rect.height };
|
|
322
|
+
}
|
|
323
|
+
function gestureRect(x, y) {
|
|
324
|
+
return {
|
|
325
|
+
x: Math.min(gesture.x, x),
|
|
326
|
+
y: Math.min(gesture.y, y),
|
|
327
|
+
width: Math.max(1, Math.abs(x - gesture.x)),
|
|
328
|
+
height: Math.max(1, Math.abs(y - gesture.y))
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function selector(element) {
|
|
332
|
+
const scope = element.getRootNode();
|
|
333
|
+
if (element.id) {
|
|
334
|
+
const id = "#" + CSS.escape(element.id);
|
|
335
|
+
if (scope.querySelectorAll(id).length === 1) return id;
|
|
336
|
+
}
|
|
337
|
+
const pieces = [];
|
|
338
|
+
let node = element;
|
|
339
|
+
while (node) {
|
|
340
|
+
let part = node.localName;
|
|
341
|
+
const peers = node.parentElement ? Array.from(node.parentElement.children).filter((x) => x.localName === node.localName) : [];
|
|
342
|
+
if (peers.length > 1) part += `:nth-of-type(${peers.indexOf(node) + 1})`;
|
|
343
|
+
pieces.unshift(part);
|
|
344
|
+
const path = pieces.join(" > ");
|
|
345
|
+
if (scope.querySelectorAll(path).length === 1 && scope.querySelector(path) === element)
|
|
346
|
+
return path;
|
|
347
|
+
node = node.parentElement;
|
|
348
|
+
}
|
|
349
|
+
return pieces.join(" > ");
|
|
350
|
+
}
|
|
351
|
+
function locator(element) {
|
|
352
|
+
const shadowHosts = [];
|
|
353
|
+
let scope = element.getRootNode();
|
|
354
|
+
while (scope instanceof ShadowRoot) {
|
|
355
|
+
shadowHosts.unshift(selector(scope.host));
|
|
356
|
+
scope = scope.host.getRootNode();
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
selector: selector(element),
|
|
360
|
+
shadowHosts,
|
|
361
|
+
matchCount: element.getRootNode().querySelectorAll(
|
|
362
|
+
selector(element)
|
|
363
|
+
).length
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function draw() {
|
|
367
|
+
if (ended) return;
|
|
368
|
+
if (location.href !== originalUrl) {
|
|
369
|
+
dismiss();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (suspended) return;
|
|
373
|
+
const node = target || hovered;
|
|
374
|
+
if (selection?.kind === "element" && !target?.isConnected) {
|
|
375
|
+
outline.classList.add("hidden");
|
|
376
|
+
showError(options.copy.targetGone);
|
|
377
|
+
save.disabled = expandedSave.disabled = true;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
save.disabled = expandedSave.disabled = !selection || !input.value.trim() && !Object.keys(changes()).length;
|
|
381
|
+
const documentRect = selection?.kind === "element" && target ? rectInDocument(target) : selection?.rect || (node?.isConnected ? rectInDocument(node) : null);
|
|
382
|
+
if (!documentRect) {
|
|
383
|
+
outline.classList.add("hidden");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const rect = {
|
|
387
|
+
left: documentRect.x - scrollX,
|
|
388
|
+
top: documentRect.y - scrollY,
|
|
389
|
+
width: documentRect.width,
|
|
390
|
+
height: documentRect.height,
|
|
391
|
+
bottom: documentRect.y - scrollY + documentRect.height
|
|
392
|
+
};
|
|
393
|
+
outline.classList.remove("hidden");
|
|
394
|
+
Object.assign(outline.style, {
|
|
395
|
+
left: `${rect.left - (selection?.kind === "point" ? 5 : 0)}px`,
|
|
396
|
+
top: `${rect.top - (selection?.kind === "point" ? 5 : 0)}px`,
|
|
397
|
+
width: `${selection?.kind === "point" ? 10 : rect.width}px`,
|
|
398
|
+
height: `${selection?.kind === "point" ? 10 : rect.height}px`,
|
|
399
|
+
borderStyle: selection?.kind === "region" ? "dashed" : "solid"
|
|
400
|
+
});
|
|
401
|
+
if (editor.classList.contains("hidden")) return;
|
|
402
|
+
const width = editor.getBoundingClientRect().width;
|
|
403
|
+
const height = editor.getBoundingClientRect().height;
|
|
404
|
+
const left = Math.max(12, Math.min(rect.left, innerWidth - width - 12));
|
|
405
|
+
const below = rect.bottom + 8;
|
|
406
|
+
const above = rect.top - height - 8;
|
|
407
|
+
const top = Math.max(
|
|
408
|
+
12,
|
|
409
|
+
Math.min(
|
|
410
|
+
below + height <= innerHeight - 12 ? below : above >= 12 ? above : rect.top + 8,
|
|
411
|
+
innerHeight - height - 12
|
|
412
|
+
)
|
|
413
|
+
);
|
|
414
|
+
Object.assign(editor.style, { left: `${left}px`, top: `${top}px` });
|
|
415
|
+
}
|
|
416
|
+
function setOptions(open, restoreFocus = false) {
|
|
417
|
+
optionsPanel.classList.toggle("hidden", !open);
|
|
418
|
+
editor.classList.toggle("expanded", open);
|
|
419
|
+
footer.classList.toggle("hidden", !open);
|
|
420
|
+
settings.setAttribute("aria-expanded", String(open));
|
|
421
|
+
draw();
|
|
422
|
+
if (open) settings.focus({ preventScroll: true });
|
|
423
|
+
else if (restoreFocus) settings.focus({ preventScroll: true });
|
|
424
|
+
}
|
|
425
|
+
settings.addEventListener(
|
|
426
|
+
"click",
|
|
427
|
+
() => {
|
|
428
|
+
setOptions(optionsPanel.classList.contains("hidden"));
|
|
429
|
+
},
|
|
430
|
+
{ signal: lifecycle.signal }
|
|
431
|
+
);
|
|
432
|
+
intent.addEventListener(
|
|
433
|
+
"change",
|
|
434
|
+
() => {
|
|
435
|
+
setOptions(false);
|
|
436
|
+
input.focus({ preventScroll: true });
|
|
437
|
+
},
|
|
438
|
+
{ signal: lifecycle.signal }
|
|
439
|
+
);
|
|
440
|
+
input.addEventListener(
|
|
441
|
+
"input",
|
|
442
|
+
() => {
|
|
443
|
+
input.style.height = "22px";
|
|
444
|
+
input.style.height = `${Math.min(110, input.scrollHeight)}px`;
|
|
445
|
+
error.classList.add("hidden");
|
|
446
|
+
draw();
|
|
447
|
+
},
|
|
448
|
+
{ signal: lifecycle.signal }
|
|
449
|
+
);
|
|
450
|
+
function track() {
|
|
451
|
+
draw();
|
|
452
|
+
if (!ended) animation = requestAnimationFrame(track);
|
|
453
|
+
}
|
|
454
|
+
function choose(element) {
|
|
455
|
+
if (!valid(element)) return;
|
|
456
|
+
observer.disconnect();
|
|
457
|
+
target = element;
|
|
458
|
+
selection = { kind: "element", rect: rectInDocument(element) };
|
|
459
|
+
observer.observe(element);
|
|
460
|
+
hint.classList.add("hidden");
|
|
461
|
+
editor.classList.remove("hidden");
|
|
462
|
+
error.classList.add("hidden");
|
|
463
|
+
const where = locator(element);
|
|
464
|
+
targetLabel.textContent = `<${element.localName}>`;
|
|
465
|
+
targetLabel.title = [...where.shadowHosts, where.selector].join(" \u203A ");
|
|
466
|
+
refreshProperties(element);
|
|
467
|
+
parent.disabled = !valid(element.parentElement) && !(element.getRootNode() instanceof ShadowRoot);
|
|
468
|
+
child.disabled = descendants.length === 0;
|
|
469
|
+
draw();
|
|
470
|
+
if (!animation) animation = requestAnimationFrame(track);
|
|
471
|
+
input.focus({ preventScroll: true });
|
|
472
|
+
}
|
|
473
|
+
function chooseArea(kind, rect) {
|
|
474
|
+
observer.disconnect();
|
|
475
|
+
target = null;
|
|
476
|
+
selection = { kind, rect };
|
|
477
|
+
hint.classList.add("hidden");
|
|
478
|
+
editor.classList.remove("hidden");
|
|
479
|
+
error.classList.add("hidden");
|
|
480
|
+
targetLabel.textContent = kind === "region" ? `${options.copy.region} \xB7 ${Math.round(rect.width)} \xD7 ${Math.round(rect.height)}` : options.copy.point;
|
|
481
|
+
parent.disabled = child.disabled = true;
|
|
482
|
+
targetLabel.title = "";
|
|
483
|
+
refreshProperties(null);
|
|
484
|
+
draw();
|
|
485
|
+
if (!animation) animation = requestAnimationFrame(track);
|
|
486
|
+
input.focus({ preventScroll: true });
|
|
487
|
+
}
|
|
488
|
+
function isOverlay(event) {
|
|
489
|
+
return event.composedPath().includes(root);
|
|
490
|
+
}
|
|
491
|
+
window.addEventListener(
|
|
492
|
+
"pointermove",
|
|
493
|
+
(event) => {
|
|
494
|
+
if (suspended || !editor.classList.contains("hidden")) return;
|
|
495
|
+
if (gesture && gesture.id === event.pointerId) {
|
|
496
|
+
gesture.dragging ||= Math.hypot(event.clientX - gesture.clientX, event.clientY - gesture.clientY) >= 5;
|
|
497
|
+
if (gesture.dragging) {
|
|
498
|
+
selection = {
|
|
499
|
+
kind: "region",
|
|
500
|
+
rect: gestureRect(event.clientX + scrollX, event.clientY + scrollY)
|
|
501
|
+
};
|
|
502
|
+
draw();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const node = hit(event.clientX, event.clientY);
|
|
507
|
+
hovered = event.shiftKey && valid(node?.parentElement || null) ? node.parentElement : node;
|
|
508
|
+
draw();
|
|
509
|
+
},
|
|
510
|
+
{ capture: true, signal: lifecycle.signal }
|
|
511
|
+
);
|
|
512
|
+
window.addEventListener(
|
|
513
|
+
"pointerdown",
|
|
514
|
+
(event) => {
|
|
515
|
+
if (suspended) {
|
|
516
|
+
suppressClick = false;
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
520
|
+
setOptions(false);
|
|
521
|
+
event.preventDefault();
|
|
522
|
+
event.stopImmediatePropagation();
|
|
523
|
+
suppressClick = true;
|
|
524
|
+
if (event.button !== 0 || !editor.classList.contains("hidden")) return;
|
|
525
|
+
gesture = {
|
|
526
|
+
id: event.pointerId,
|
|
527
|
+
x: event.clientX + scrollX,
|
|
528
|
+
y: event.clientY + scrollY,
|
|
529
|
+
clientX: event.clientX,
|
|
530
|
+
clientY: event.clientY,
|
|
531
|
+
dragging: false
|
|
532
|
+
};
|
|
533
|
+
surface.setPointerCapture(event.pointerId);
|
|
534
|
+
},
|
|
535
|
+
{ capture: true, signal: lifecycle.signal }
|
|
536
|
+
);
|
|
537
|
+
window.addEventListener(
|
|
538
|
+
"pointerup",
|
|
539
|
+
(event) => {
|
|
540
|
+
if (!gesture || gesture.id !== event.pointerId || suspended) return;
|
|
541
|
+
event.preventDefault();
|
|
542
|
+
event.stopImmediatePropagation();
|
|
543
|
+
const current = gesture;
|
|
544
|
+
const area = gestureRect(event.clientX + scrollX, event.clientY + scrollY);
|
|
545
|
+
gesture = null;
|
|
546
|
+
if (surface.hasPointerCapture(event.pointerId))
|
|
547
|
+
surface.releasePointerCapture(event.pointerId);
|
|
548
|
+
if (current.dragging || Math.hypot(event.clientX - current.clientX, event.clientY - current.clientY) >= 5)
|
|
549
|
+
chooseArea("region", area);
|
|
550
|
+
else {
|
|
551
|
+
const node = hit(event.clientX, event.clientY);
|
|
552
|
+
descendants.length = 0;
|
|
553
|
+
if (node) choose(event.shiftKey && valid(node.parentElement) ? node.parentElement : node);
|
|
554
|
+
else
|
|
555
|
+
chooseArea("point", {
|
|
556
|
+
x: event.clientX + scrollX,
|
|
557
|
+
y: event.clientY + scrollY,
|
|
558
|
+
width: 0,
|
|
559
|
+
height: 0
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
{ capture: true, signal: lifecycle.signal }
|
|
564
|
+
);
|
|
565
|
+
function abortGesture() {
|
|
566
|
+
if (!gesture) return;
|
|
567
|
+
if (surface.hasPointerCapture(gesture.id)) surface.releasePointerCapture(gesture.id);
|
|
568
|
+
gesture = null;
|
|
569
|
+
if (editor.classList.contains("hidden")) selection = null;
|
|
570
|
+
draw();
|
|
571
|
+
}
|
|
572
|
+
window.addEventListener("pointercancel", abortGesture, {
|
|
573
|
+
capture: true,
|
|
574
|
+
signal: lifecycle.signal
|
|
575
|
+
});
|
|
576
|
+
window.addEventListener(
|
|
577
|
+
"click",
|
|
578
|
+
(event) => {
|
|
579
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
580
|
+
if (suspended && !suppressClick) return;
|
|
581
|
+
event.preventDefault();
|
|
582
|
+
event.stopImmediatePropagation();
|
|
583
|
+
suppressClick = false;
|
|
584
|
+
},
|
|
585
|
+
{ capture: true, signal: lifecycle.signal }
|
|
586
|
+
);
|
|
587
|
+
function resume() {
|
|
588
|
+
if (!suspended) return;
|
|
589
|
+
suspended = false;
|
|
590
|
+
surface.style.pointerEvents = "auto";
|
|
591
|
+
hint.textContent = options.copy.select;
|
|
592
|
+
if (selection) {
|
|
593
|
+
editor.classList.remove("hidden");
|
|
594
|
+
hint.classList.add("hidden");
|
|
595
|
+
} else hint.classList.remove("hidden");
|
|
596
|
+
draw();
|
|
597
|
+
}
|
|
598
|
+
window.addEventListener(
|
|
599
|
+
"keydown",
|
|
600
|
+
(event) => {
|
|
601
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
602
|
+
if (event.key === "Escape") {
|
|
603
|
+
event.preventDefault();
|
|
604
|
+
event.stopImmediatePropagation();
|
|
605
|
+
if (optionsPanel.classList.contains("hidden")) dismiss();
|
|
606
|
+
else setOptions(false, true);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (isOverlay(event) && !event.composedPath().includes(surface)) return;
|
|
610
|
+
if (event.code === "Space" && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
|
611
|
+
event.preventDefault();
|
|
612
|
+
event.stopImmediatePropagation();
|
|
613
|
+
abortGesture();
|
|
614
|
+
suspended = true;
|
|
615
|
+
setOptions(false);
|
|
616
|
+
surface.style.pointerEvents = "none";
|
|
617
|
+
outline.classList.add("hidden");
|
|
618
|
+
editor.classList.add("hidden");
|
|
619
|
+
hint.classList.remove("hidden");
|
|
620
|
+
hint.textContent = options.copy.interact;
|
|
621
|
+
} else if (!suspended && !isOverlay(event)) {
|
|
622
|
+
event.preventDefault();
|
|
623
|
+
event.stopImmediatePropagation();
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
{ capture: true, signal: lifecycle.signal }
|
|
627
|
+
);
|
|
628
|
+
window.addEventListener(
|
|
629
|
+
"keyup",
|
|
630
|
+
(event) => {
|
|
631
|
+
if (event.code === "Space" && suspended) {
|
|
632
|
+
event.preventDefault();
|
|
633
|
+
event.stopImmediatePropagation();
|
|
634
|
+
resume();
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
{ capture: true, signal: lifecycle.signal }
|
|
638
|
+
);
|
|
639
|
+
window.addEventListener(
|
|
640
|
+
"blur",
|
|
641
|
+
() => {
|
|
642
|
+
abortGesture();
|
|
643
|
+
resume();
|
|
644
|
+
},
|
|
645
|
+
{ signal: lifecycle.signal }
|
|
646
|
+
);
|
|
647
|
+
doc.addEventListener(
|
|
648
|
+
"visibilitychange",
|
|
649
|
+
() => {
|
|
650
|
+
if (doc.hidden) {
|
|
651
|
+
abortGesture();
|
|
652
|
+
resume();
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
{ signal: lifecycle.signal }
|
|
656
|
+
);
|
|
657
|
+
window.addEventListener("pagehide", dismiss, { once: true, signal: lifecycle.signal });
|
|
658
|
+
parent.addEventListener(
|
|
659
|
+
"click",
|
|
660
|
+
() => {
|
|
661
|
+
if (!target) return;
|
|
662
|
+
const scope = target.getRootNode();
|
|
663
|
+
const node = target.parentElement || (scope instanceof ShadowRoot ? scope.host : null);
|
|
664
|
+
if (valid(node)) {
|
|
665
|
+
setOptions(false);
|
|
666
|
+
descendants.push(target);
|
|
667
|
+
choose(node);
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
{ signal: lifecycle.signal }
|
|
671
|
+
);
|
|
672
|
+
child.addEventListener(
|
|
673
|
+
"click",
|
|
674
|
+
() => {
|
|
675
|
+
const node = descendants.pop();
|
|
676
|
+
if (node?.isConnected) {
|
|
677
|
+
setOptions(false);
|
|
678
|
+
choose(node);
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
{ signal: lifecycle.signal }
|
|
682
|
+
);
|
|
683
|
+
cancel.addEventListener("click", dismiss, { signal: lifecycle.signal });
|
|
684
|
+
function evidence(element) {
|
|
685
|
+
const where = locator(element);
|
|
686
|
+
const computed = getComputedStyle(element);
|
|
687
|
+
const styles = {};
|
|
688
|
+
for (const name of [
|
|
689
|
+
"display",
|
|
690
|
+
"position",
|
|
691
|
+
"font-size",
|
|
692
|
+
"font-family",
|
|
693
|
+
"color",
|
|
694
|
+
"background-color",
|
|
695
|
+
"padding",
|
|
696
|
+
"margin",
|
|
697
|
+
"gap",
|
|
698
|
+
"opacity",
|
|
699
|
+
"font-weight"
|
|
700
|
+
])
|
|
701
|
+
styles[name] = computed.getPropertyValue(name).slice(0, 1e3);
|
|
702
|
+
const source = element.closest("[data-web-annotator-source]")?.getAttribute("data-web-annotator-source")?.slice(0, 1e3);
|
|
703
|
+
return {
|
|
704
|
+
...where,
|
|
705
|
+
tag: element.localName,
|
|
706
|
+
text: (element instanceof HTMLElement ? element.innerText : element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 400),
|
|
707
|
+
rect: rectInDocument(element),
|
|
708
|
+
styles,
|
|
709
|
+
...source ? { source } : {}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function candidates(rect) {
|
|
713
|
+
const found = /* @__PURE__ */ new Set();
|
|
714
|
+
for (let row2 = 0; row2 < 5; row2++)
|
|
715
|
+
for (let col = 0; col < 5; col++) {
|
|
716
|
+
const node = hit(
|
|
717
|
+
rect.x - scrollX + rect.width * (col + 0.5) / 5,
|
|
718
|
+
rect.y - scrollY + rect.height * (row2 + 0.5) / 5
|
|
719
|
+
);
|
|
720
|
+
if (node) found.add(node);
|
|
721
|
+
}
|
|
722
|
+
return [...found].slice(0, 12).map(evidence);
|
|
723
|
+
}
|
|
724
|
+
function submit(event) {
|
|
725
|
+
event.preventDefault();
|
|
726
|
+
if (!selection || selection.kind === "element" && !target?.isConnected || location.href !== originalUrl) {
|
|
727
|
+
showError(options.copy.targetGone);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const styleChanges = changes();
|
|
731
|
+
for (const [property, { after }] of Object.entries(styleChanges)) {
|
|
732
|
+
if (!CSS.supports(property, after) || property === "opacity" && !(Number(after) >= 0 && Number(after) <= 1)) {
|
|
733
|
+
showError(options.copy.invalidStyle);
|
|
734
|
+
setOptions(true);
|
|
735
|
+
fields.get(property)?.input.focus();
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (!input.value.trim() && !Object.keys(styleChanges).length) {
|
|
740
|
+
showError(options.copy.empty);
|
|
741
|
+
input.focus();
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const rect = selection.kind === "element" ? rectInDocument(target) : selection.rect;
|
|
745
|
+
const items = selection.kind === "element" ? [evidence(target)] : candidates(rect);
|
|
746
|
+
finish({
|
|
747
|
+
intent: intent.value === "question" ? "question" : "comment",
|
|
748
|
+
note: input.value.trim() || options.copy.applyStyles,
|
|
749
|
+
...Object.keys(styleChanges).length ? { styleChanges } : {},
|
|
750
|
+
url: originalUrl,
|
|
751
|
+
title: doc.title.slice(0, 300),
|
|
752
|
+
viewport: { width: innerWidth, height: innerHeight },
|
|
753
|
+
selection: {
|
|
754
|
+
kind: selection.kind,
|
|
755
|
+
rect,
|
|
756
|
+
viewportRect: { ...rect, x: rect.x - scrollX, y: rect.y - scrollY },
|
|
757
|
+
scroll: { x: scrollX, y: scrollY },
|
|
758
|
+
candidates: items
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
editor.addEventListener("submit", submit, { signal: lifecycle.signal });
|
|
763
|
+
input.addEventListener(
|
|
764
|
+
"keydown",
|
|
765
|
+
(event) => {
|
|
766
|
+
if (event.key === "Enter" && !event.shiftKey && !event.isComposing && event.keyCode !== 229) {
|
|
767
|
+
event.preventDefault();
|
|
768
|
+
event.stopPropagation();
|
|
769
|
+
submit(event);
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
{ signal: lifecycle.signal }
|
|
773
|
+
);
|
|
774
|
+
animation = requestAnimationFrame(track);
|
|
775
|
+
return result;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/browser/protocol.ts
|
|
779
|
+
function object(value) {
|
|
780
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
781
|
+
}
|
|
782
|
+
function string(value, limit) {
|
|
783
|
+
return typeof value === "string" && value.length <= limit;
|
|
784
|
+
}
|
|
785
|
+
function isAnnotationOptions(value) {
|
|
786
|
+
return object(value) && (value.intent === "comment" || value.intent === "question") && typeof value.dark === "boolean" && object(value.copy) && [
|
|
787
|
+
"select",
|
|
788
|
+
"comment",
|
|
789
|
+
"question",
|
|
790
|
+
"placeholder",
|
|
791
|
+
"settings",
|
|
792
|
+
"save",
|
|
793
|
+
"cancel",
|
|
794
|
+
"parent",
|
|
795
|
+
"child",
|
|
796
|
+
"targetGone",
|
|
797
|
+
"empty",
|
|
798
|
+
"region",
|
|
799
|
+
"point",
|
|
800
|
+
"interact",
|
|
801
|
+
"color",
|
|
802
|
+
"background",
|
|
803
|
+
"opacity",
|
|
804
|
+
"font",
|
|
805
|
+
"fontSize",
|
|
806
|
+
"fontWeight",
|
|
807
|
+
"spacing",
|
|
808
|
+
"elementOnly",
|
|
809
|
+
"invalidStyle",
|
|
810
|
+
"applyStyles",
|
|
811
|
+
"styleHint"
|
|
812
|
+
].every((key) => string(value.copy && object(value.copy) ? value.copy[key] : null, 500));
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/browser/bridge.ts
|
|
816
|
+
function isAllowedParentOrigin(origin, configured) {
|
|
817
|
+
try {
|
|
818
|
+
const url = new URL(origin);
|
|
819
|
+
if (url.origin !== origin) return false;
|
|
820
|
+
if (configured.includes(origin)) return ["http:", "https:"].includes(url.protocol);
|
|
821
|
+
return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(url.hostname));
|
|
822
|
+
} catch {
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function installAnnotationBridge(win, pick = annotatePage) {
|
|
827
|
+
const lifecycle = new AbortController();
|
|
828
|
+
const configured = win.__WEB_ANNOTATOR_CONFIG__?.allowedParentOrigins ?? [];
|
|
829
|
+
let current;
|
|
830
|
+
let disposed = false;
|
|
831
|
+
const release = (port) => {
|
|
832
|
+
port.onmessage = null;
|
|
833
|
+
port.close();
|
|
834
|
+
if (current === port) current = void 0;
|
|
835
|
+
};
|
|
836
|
+
const dispose = () => {
|
|
837
|
+
if (disposed) return;
|
|
838
|
+
disposed = true;
|
|
839
|
+
lifecycle.abort();
|
|
840
|
+
const port = current;
|
|
841
|
+
if (port) {
|
|
842
|
+
port.postMessage({ cancelled: true });
|
|
843
|
+
release(port);
|
|
844
|
+
}
|
|
845
|
+
win.__DSH_PAGE_ANNOTATION_CANCEL__?.();
|
|
846
|
+
};
|
|
847
|
+
win.addEventListener(
|
|
848
|
+
"message",
|
|
849
|
+
(event) => {
|
|
850
|
+
if (disposed || win === win.parent || event.source !== win.parent || !isAllowedParentOrigin(event.origin, configured) || event.data?.channel !== "dsh-browser-annotation-v1" || event.data?.type !== "start" || !isAnnotationOptions(event.data.options) || event.ports.length !== 1)
|
|
851
|
+
return;
|
|
852
|
+
const port = event.ports[0];
|
|
853
|
+
if (current) {
|
|
854
|
+
current.postMessage({ cancelled: true });
|
|
855
|
+
release(current);
|
|
856
|
+
}
|
|
857
|
+
win.__DSH_PAGE_ANNOTATION_CANCEL__?.();
|
|
858
|
+
current = port;
|
|
859
|
+
port.onmessage = (message) => {
|
|
860
|
+
if (message.data?.type === "cancel" && current === port)
|
|
861
|
+
win.__DSH_PAGE_ANNOTATION_CANCEL__?.();
|
|
862
|
+
};
|
|
863
|
+
port.postMessage({ ready: true });
|
|
864
|
+
const settle = (value) => {
|
|
865
|
+
if (disposed || current !== port) return;
|
|
866
|
+
port.postMessage(value);
|
|
867
|
+
release(port);
|
|
868
|
+
};
|
|
869
|
+
let started;
|
|
870
|
+
try {
|
|
871
|
+
started = pick(event.data.options);
|
|
872
|
+
} catch {
|
|
873
|
+
settle({ cancelled: true });
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
void started.then(settle, () => {
|
|
877
|
+
settle({ cancelled: true });
|
|
878
|
+
});
|
|
879
|
+
},
|
|
880
|
+
{ signal: lifecycle.signal }
|
|
881
|
+
);
|
|
882
|
+
win.addEventListener("pagehide", dispose, { once: true, signal: lifecycle.signal });
|
|
883
|
+
return dispose;
|
|
884
|
+
}
|
|
885
|
+
if (typeof window !== "undefined") {
|
|
886
|
+
window.__DSH_PAGE_ANNOTATION_BRIDGE_DISPOSE__?.();
|
|
887
|
+
window.__DSH_PAGE_ANNOTATION_BRIDGE_DISPOSE__ = installAnnotationBridge(window);
|
|
888
|
+
}
|
|
889
|
+
})();
|
|
890
|
+
//# sourceMappingURL=bridge.js.map
|