pake-cli 3.11.4 → 3.11.6
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/README.md +3 -3
- package/dist/cli.js +335 -161
- package/dist/pake-badge-test.html +1 -0
- package/dist/test-local.html +1 -0
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +7 -4
- package/src-tauri/Cargo.toml +6 -1
- package/src-tauri/build.rs +2 -0
- package/src-tauri/gen/schemas/acl-manifests.json +1 -0
- package/src-tauri/gen/schemas/capabilities.json +1 -0
- package/src-tauri/gen/schemas/desktop-schema.json +3331 -0
- package/src-tauri/gen/schemas/macOS-schema.json +3331 -0
- package/src-tauri/pake.json +1 -0
- package/src-tauri/src/app/config.rs +2 -0
- package/src-tauri/src/app/invoke.rs +85 -0
- package/src-tauri/src/app/menu.rs +56 -5
- package/src-tauri/src/app/setup.rs +39 -34
- package/src-tauri/src/app/window.rs +64 -17
- package/src-tauri/src/inject/event.js +98 -12
- package/src-tauri/src/inject/find.js +708 -0
- package/src-tauri/src/inject/{component.js → fullscreen.js} +7 -41
- package/src-tauri/src/inject/toast.js +22 -0
- package/src-tauri/src/lib.rs +13 -5
- package/src-tauri/src/util.rs +131 -24
- package/src-tauri/tauri.conf.json +1 -1
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
if (window.__PAKE_FIND_SCRIPT__) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
window.__PAKE_FIND_SCRIPT__ = true;
|
|
6
|
+
|
|
7
|
+
const PANEL_ID = "pake-find-panel";
|
|
8
|
+
const STYLE_ID = "pake-find-style";
|
|
9
|
+
const MARK_ATTR = "data-pake-find";
|
|
10
|
+
const ACTIVE_ATTR = "data-pake-find-active";
|
|
11
|
+
const MATCH_HIGHLIGHT = "pake-find-match";
|
|
12
|
+
const ACTIVE_HIGHLIGHT = "pake-find-active";
|
|
13
|
+
const MAX_MATCHES = 1000;
|
|
14
|
+
const SEARCH_DEBOUNCE_MS = 120;
|
|
15
|
+
const SKIPPED_TAGS = new Set([
|
|
16
|
+
"script",
|
|
17
|
+
"style",
|
|
18
|
+
"noscript",
|
|
19
|
+
"input",
|
|
20
|
+
"textarea",
|
|
21
|
+
"select",
|
|
22
|
+
"option",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const state = {
|
|
26
|
+
enabled: window.pakeConfig?.enable_find === true,
|
|
27
|
+
panel: null,
|
|
28
|
+
input: null,
|
|
29
|
+
counter: null,
|
|
30
|
+
status: null,
|
|
31
|
+
matches: [],
|
|
32
|
+
activeIndex: -1,
|
|
33
|
+
query: "",
|
|
34
|
+
truncated: false,
|
|
35
|
+
domMarks: [],
|
|
36
|
+
observer: null,
|
|
37
|
+
searchTimer: null,
|
|
38
|
+
isOpen: false,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function getState() {
|
|
42
|
+
return {
|
|
43
|
+
enabled: state.enabled,
|
|
44
|
+
isOpen: state.isOpen,
|
|
45
|
+
query: state.query,
|
|
46
|
+
matchCount: state.matches.length,
|
|
47
|
+
activeIndex: state.activeIndex,
|
|
48
|
+
truncated: state.truncated,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function noop() {
|
|
53
|
+
return getState();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!state.enabled) {
|
|
57
|
+
window.pakeFind = {
|
|
58
|
+
open: noop,
|
|
59
|
+
close: noop,
|
|
60
|
+
next: noop,
|
|
61
|
+
previous: noop,
|
|
62
|
+
search: noop,
|
|
63
|
+
getState,
|
|
64
|
+
getFindShortcutAction: () => "",
|
|
65
|
+
};
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getNodeFilter() {
|
|
70
|
+
return (
|
|
71
|
+
window.NodeFilter ||
|
|
72
|
+
globalThis.NodeFilter || {
|
|
73
|
+
SHOW_TEXT: 4,
|
|
74
|
+
FILTER_ACCEPT: 1,
|
|
75
|
+
FILTER_REJECT: 2,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function supportsCustomHighlight() {
|
|
81
|
+
return (
|
|
82
|
+
typeof CSS !== "undefined" &&
|
|
83
|
+
CSS.highlights &&
|
|
84
|
+
typeof Highlight === "function"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isFindPanelNode(node) {
|
|
89
|
+
const element =
|
|
90
|
+
node?.nodeType === 1 ? node : node?.parentElement || node?.parentNode;
|
|
91
|
+
if (!element) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (element.id === PANEL_ID) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return element.closest?.(`#${PANEL_ID}`) != null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function shouldSkipElement(element) {
|
|
101
|
+
for (let current = element; current; current = current.parentElement) {
|
|
102
|
+
if (current.id === PANEL_ID) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const tagName = current.tagName?.toLowerCase();
|
|
107
|
+
if (tagName && SKIPPED_TAGS.has(tagName)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
current.isContentEditable ||
|
|
113
|
+
current.getAttribute?.("contenteditable") === "true"
|
|
114
|
+
) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (current.hidden || current.getAttribute?.("aria-hidden") === "true") {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getSearchableTextNodes(root = document.body) {
|
|
127
|
+
if (!root || !document.createTreeWalker) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const nodeFilter = getNodeFilter();
|
|
132
|
+
const walker = document.createTreeWalker(root, nodeFilter.SHOW_TEXT, {
|
|
133
|
+
acceptNode(node) {
|
|
134
|
+
if (!node.nodeValue || node.nodeValue.length === 0) {
|
|
135
|
+
return nodeFilter.FILTER_REJECT;
|
|
136
|
+
}
|
|
137
|
+
if (shouldSkipElement(node.parentElement)) {
|
|
138
|
+
return nodeFilter.FILTER_REJECT;
|
|
139
|
+
}
|
|
140
|
+
return nodeFilter.FILTER_ACCEPT;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const nodes = [];
|
|
145
|
+
let current = walker.nextNode();
|
|
146
|
+
while (current) {
|
|
147
|
+
nodes.push(current);
|
|
148
|
+
current = walker.nextNode();
|
|
149
|
+
}
|
|
150
|
+
return nodes;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function createRange(node, start, end) {
|
|
154
|
+
const range = document.createRange();
|
|
155
|
+
range.setStart(node, start);
|
|
156
|
+
range.setEnd(node, end);
|
|
157
|
+
return range;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function collectMatches(query) {
|
|
161
|
+
const matches = [];
|
|
162
|
+
const normalizedQuery = query.toLocaleLowerCase();
|
|
163
|
+
if (!normalizedQuery) {
|
|
164
|
+
return { matches, truncated: false };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const node of getSearchableTextNodes()) {
|
|
168
|
+
const text = node.nodeValue || "";
|
|
169
|
+
const normalizedText = text.toLocaleLowerCase();
|
|
170
|
+
let searchFrom = 0;
|
|
171
|
+
|
|
172
|
+
while (searchFrom <= normalizedText.length) {
|
|
173
|
+
const index = normalizedText.indexOf(normalizedQuery, searchFrom);
|
|
174
|
+
if (index === -1) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
matches.push({
|
|
179
|
+
node,
|
|
180
|
+
start: index,
|
|
181
|
+
end: index + query.length,
|
|
182
|
+
range: createRange(node, index, index + query.length),
|
|
183
|
+
mark: null,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (matches.length >= MAX_MATCHES) {
|
|
187
|
+
return { matches, truncated: true };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
searchFrom = index + Math.max(query.length, 1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { matches, truncated: false };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function ensureStyle() {
|
|
198
|
+
if (document.getElementById(STYLE_ID)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const style = document.createElement("style");
|
|
203
|
+
style.id = STYLE_ID;
|
|
204
|
+
style.textContent = `
|
|
205
|
+
#${PANEL_ID} {
|
|
206
|
+
position: fixed;
|
|
207
|
+
top: 14px;
|
|
208
|
+
right: 14px;
|
|
209
|
+
z-index: 2147483647;
|
|
210
|
+
display: none;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 6px;
|
|
213
|
+
box-sizing: border-box;
|
|
214
|
+
min-width: 278px;
|
|
215
|
+
max-width: min(420px, calc(100vw - 28px));
|
|
216
|
+
padding: 8px;
|
|
217
|
+
border: 1px solid rgba(0, 0, 0, 0.14);
|
|
218
|
+
border-radius: 8px;
|
|
219
|
+
background: rgba(255, 255, 255, 0.96);
|
|
220
|
+
color: #1f2328;
|
|
221
|
+
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
|
|
222
|
+
font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
223
|
+
backdrop-filter: blur(16px);
|
|
224
|
+
}
|
|
225
|
+
#${PANEL_ID}[data-visible="true"] {
|
|
226
|
+
display: flex;
|
|
227
|
+
}
|
|
228
|
+
#${PANEL_ID} input {
|
|
229
|
+
min-width: 0;
|
|
230
|
+
flex: 1 1 auto;
|
|
231
|
+
height: 28px;
|
|
232
|
+
box-sizing: border-box;
|
|
233
|
+
border: 1px solid rgba(0, 0, 0, 0.16);
|
|
234
|
+
border-radius: 6px;
|
|
235
|
+
padding: 0 8px;
|
|
236
|
+
background: #fff;
|
|
237
|
+
color: #1f2328;
|
|
238
|
+
font: inherit;
|
|
239
|
+
outline: none;
|
|
240
|
+
}
|
|
241
|
+
#${PANEL_ID} input:focus {
|
|
242
|
+
border-color: #3b82f6;
|
|
243
|
+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.16);
|
|
244
|
+
}
|
|
245
|
+
#${PANEL_ID} [data-pake-find-counter] {
|
|
246
|
+
flex: 0 0 auto;
|
|
247
|
+
min-width: 42px;
|
|
248
|
+
color: #5f6b7a;
|
|
249
|
+
text-align: center;
|
|
250
|
+
font-size: 12px;
|
|
251
|
+
white-space: nowrap;
|
|
252
|
+
}
|
|
253
|
+
#${PANEL_ID} button {
|
|
254
|
+
flex: 0 0 auto;
|
|
255
|
+
width: 28px;
|
|
256
|
+
height: 28px;
|
|
257
|
+
border: 0;
|
|
258
|
+
border-radius: 6px;
|
|
259
|
+
background: transparent;
|
|
260
|
+
color: #30363d;
|
|
261
|
+
font: 15px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
}
|
|
264
|
+
#${PANEL_ID} button:hover {
|
|
265
|
+
background: rgba(0, 0, 0, 0.08);
|
|
266
|
+
}
|
|
267
|
+
#${PANEL_ID} [data-pake-find-status] {
|
|
268
|
+
position: absolute;
|
|
269
|
+
left: 10px;
|
|
270
|
+
top: calc(100% + 4px);
|
|
271
|
+
color: #d1242f;
|
|
272
|
+
font-size: 12px;
|
|
273
|
+
white-space: nowrap;
|
|
274
|
+
}
|
|
275
|
+
@media (prefers-color-scheme: dark) {
|
|
276
|
+
#${PANEL_ID} {
|
|
277
|
+
border-color: rgba(255, 255, 255, 0.16);
|
|
278
|
+
background: rgba(31, 35, 40, 0.94);
|
|
279
|
+
color: #f0f3f6;
|
|
280
|
+
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.36);
|
|
281
|
+
}
|
|
282
|
+
#${PANEL_ID} input {
|
|
283
|
+
border-color: rgba(255, 255, 255, 0.18);
|
|
284
|
+
background: rgba(255, 255, 255, 0.08);
|
|
285
|
+
color: #f0f3f6;
|
|
286
|
+
}
|
|
287
|
+
#${PANEL_ID} [data-pake-find-counter] {
|
|
288
|
+
color: #b7c0cc;
|
|
289
|
+
}
|
|
290
|
+
#${PANEL_ID} button {
|
|
291
|
+
color: #f0f3f6;
|
|
292
|
+
}
|
|
293
|
+
#${PANEL_ID} button:hover {
|
|
294
|
+
background: rgba(255, 255, 255, 0.12);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
::highlight(${MATCH_HIGHLIGHT}) {
|
|
298
|
+
background: rgba(255, 214, 10, 0.58);
|
|
299
|
+
color: inherit;
|
|
300
|
+
}
|
|
301
|
+
::highlight(${ACTIVE_HIGHLIGHT}) {
|
|
302
|
+
background: rgba(255, 149, 0, 0.9);
|
|
303
|
+
color: inherit;
|
|
304
|
+
}
|
|
305
|
+
mark[${MARK_ATTR}] {
|
|
306
|
+
background: rgba(255, 214, 10, 0.58);
|
|
307
|
+
color: inherit;
|
|
308
|
+
padding: 0;
|
|
309
|
+
}
|
|
310
|
+
mark[${MARK_ATTR}][${ACTIVE_ATTR}] {
|
|
311
|
+
background: rgba(255, 149, 0, 0.9);
|
|
312
|
+
}
|
|
313
|
+
`;
|
|
314
|
+
|
|
315
|
+
(document.head || document.body || document.documentElement)?.appendChild(
|
|
316
|
+
style,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function createButton(label, title, onClick) {
|
|
321
|
+
const button = document.createElement("button");
|
|
322
|
+
button.type = "button";
|
|
323
|
+
button.textContent = label;
|
|
324
|
+
button.title = title;
|
|
325
|
+
button.setAttribute("aria-label", title);
|
|
326
|
+
button.addEventListener("click", onClick);
|
|
327
|
+
return button;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function ensurePanel() {
|
|
331
|
+
if (state.panel) {
|
|
332
|
+
return state.panel;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
ensureStyle();
|
|
336
|
+
|
|
337
|
+
const panel = document.createElement("div");
|
|
338
|
+
panel.id = PANEL_ID;
|
|
339
|
+
panel.setAttribute("role", "search");
|
|
340
|
+
panel.setAttribute("aria-label", "Find in page");
|
|
341
|
+
|
|
342
|
+
const input = document.createElement("input");
|
|
343
|
+
input.type = "search";
|
|
344
|
+
input.autocomplete = "off";
|
|
345
|
+
input.spellcheck = false;
|
|
346
|
+
input.placeholder = "Find";
|
|
347
|
+
input.setAttribute("aria-label", "Find in page");
|
|
348
|
+
|
|
349
|
+
const counter = document.createElement("span");
|
|
350
|
+
counter.setAttribute("data-pake-find-counter", "");
|
|
351
|
+
counter.textContent = "0/0";
|
|
352
|
+
|
|
353
|
+
const previousButton = createButton("<", "Find Previous", () => previous());
|
|
354
|
+
const nextButton = createButton(">", "Find Next", () => next());
|
|
355
|
+
const closeButton = createButton("x", "Close Find", () => close());
|
|
356
|
+
|
|
357
|
+
const status = document.createElement("span");
|
|
358
|
+
status.setAttribute("data-pake-find-status", "");
|
|
359
|
+
|
|
360
|
+
input.addEventListener("input", () => {
|
|
361
|
+
debounceSearch(input.value);
|
|
362
|
+
});
|
|
363
|
+
input.addEventListener("keydown", (event) => {
|
|
364
|
+
if (event.key === "Enter") {
|
|
365
|
+
event.preventDefault();
|
|
366
|
+
event.stopPropagation();
|
|
367
|
+
if (event.shiftKey) {
|
|
368
|
+
previous();
|
|
369
|
+
} else {
|
|
370
|
+
next();
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (event.key === "Escape") {
|
|
376
|
+
event.preventDefault();
|
|
377
|
+
event.stopPropagation();
|
|
378
|
+
close();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
panel.append(
|
|
383
|
+
input,
|
|
384
|
+
counter,
|
|
385
|
+
previousButton,
|
|
386
|
+
nextButton,
|
|
387
|
+
closeButton,
|
|
388
|
+
status,
|
|
389
|
+
);
|
|
390
|
+
(document.body || document.documentElement).appendChild(panel);
|
|
391
|
+
|
|
392
|
+
state.panel = panel;
|
|
393
|
+
state.input = input;
|
|
394
|
+
state.counter = counter;
|
|
395
|
+
state.status = status;
|
|
396
|
+
|
|
397
|
+
return panel;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function clearCustomHighlights() {
|
|
401
|
+
if (!supportsCustomHighlight()) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
CSS.highlights.delete(MATCH_HIGHLIGHT);
|
|
406
|
+
CSS.highlights.delete(ACTIVE_HIGHLIGHT);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function clearDomMarks() {
|
|
410
|
+
const marks = Array.from(
|
|
411
|
+
document.querySelectorAll?.(`mark[${MARK_ATTR}]`) || state.domMarks,
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
for (const mark of marks) {
|
|
415
|
+
const parent = mark.parentNode;
|
|
416
|
+
const text = document.createTextNode(mark.textContent || "");
|
|
417
|
+
mark.replaceWith?.(text);
|
|
418
|
+
parent?.normalize?.();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
state.domMarks = [];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function clearHighlights() {
|
|
425
|
+
clearCustomHighlights();
|
|
426
|
+
clearDomMarks();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function applyCustomHighlights() {
|
|
430
|
+
if (!supportsCustomHighlight()) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const ranges = state.matches.map((match) => match.range);
|
|
435
|
+
CSS.highlights.set(MATCH_HIGHLIGHT, new Highlight(...ranges));
|
|
436
|
+
updateActiveHighlight();
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function applyDomHighlights() {
|
|
441
|
+
const grouped = new Map();
|
|
442
|
+
for (const match of state.matches) {
|
|
443
|
+
const nodeMatches = grouped.get(match.node) || [];
|
|
444
|
+
nodeMatches.push(match);
|
|
445
|
+
grouped.set(match.node, nodeMatches);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
for (const nodeMatches of grouped.values()) {
|
|
449
|
+
nodeMatches.sort((a, b) => b.start - a.start);
|
|
450
|
+
for (const match of nodeMatches) {
|
|
451
|
+
try {
|
|
452
|
+
const mark = document.createElement("mark");
|
|
453
|
+
mark.setAttribute(MARK_ATTR, "");
|
|
454
|
+
match.range.surroundContents(mark);
|
|
455
|
+
match.mark = mark;
|
|
456
|
+
state.domMarks.push(mark);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
// Some browser-generated text ranges cannot be wrapped safely.
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
updateDomActiveMark();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function updateDomActiveMark() {
|
|
467
|
+
state.matches.forEach((match, index) => {
|
|
468
|
+
const mark = match.mark;
|
|
469
|
+
if (!mark) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (mark.toggleAttribute) {
|
|
474
|
+
mark.toggleAttribute(ACTIVE_ATTR, index === state.activeIndex);
|
|
475
|
+
} else if (index === state.activeIndex) {
|
|
476
|
+
mark.setAttribute(ACTIVE_ATTR, "");
|
|
477
|
+
} else {
|
|
478
|
+
mark.removeAttribute?.(ACTIVE_ATTR);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function updateActiveHighlight() {
|
|
484
|
+
if (!supportsCustomHighlight()) {
|
|
485
|
+
updateDomActiveMark();
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
CSS.highlights.delete(ACTIVE_HIGHLIGHT);
|
|
490
|
+
if (state.activeIndex >= 0 && state.matches[state.activeIndex]) {
|
|
491
|
+
CSS.highlights.set(
|
|
492
|
+
ACTIVE_HIGHLIGHT,
|
|
493
|
+
new Highlight(state.matches[state.activeIndex].range),
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function scrollActiveIntoView() {
|
|
499
|
+
const active = state.matches[state.activeIndex];
|
|
500
|
+
if (!active) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const target = active.mark || active.range.startContainer?.parentElement;
|
|
505
|
+
if (target?.scrollIntoView) {
|
|
506
|
+
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function updateCounter() {
|
|
511
|
+
if (!state.counter) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const total = state.matches.length;
|
|
516
|
+
const active = state.activeIndex >= 0 ? state.activeIndex + 1 : 0;
|
|
517
|
+
state.counter.textContent = `${active}/${total}${state.truncated ? "+" : ""}`;
|
|
518
|
+
|
|
519
|
+
if (state.status) {
|
|
520
|
+
state.status.textContent = state.query && total === 0 ? "No results" : "";
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function runSearch(query = state.query) {
|
|
525
|
+
state.query = query;
|
|
526
|
+
clearHighlights();
|
|
527
|
+
|
|
528
|
+
if (!query) {
|
|
529
|
+
state.matches = [];
|
|
530
|
+
state.activeIndex = -1;
|
|
531
|
+
state.truncated = false;
|
|
532
|
+
updateCounter();
|
|
533
|
+
return getState();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const result = collectMatches(query);
|
|
537
|
+
state.matches = result.matches;
|
|
538
|
+
state.truncated = result.truncated;
|
|
539
|
+
state.activeIndex = state.matches.length > 0 ? 0 : -1;
|
|
540
|
+
|
|
541
|
+
if (!applyCustomHighlights()) {
|
|
542
|
+
applyDomHighlights();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
updateCounter();
|
|
546
|
+
scrollActiveIntoView();
|
|
547
|
+
return getState();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function debounceSearch(query) {
|
|
551
|
+
clearTimeout(state.searchTimer);
|
|
552
|
+
state.searchTimer = setTimeout(() => runSearch(query), SEARCH_DEBOUNCE_MS);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function next() {
|
|
556
|
+
if (!state.query && state.input?.value) {
|
|
557
|
+
runSearch(state.input.value);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (state.matches.length === 0) {
|
|
561
|
+
return getState();
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
state.activeIndex = (state.activeIndex + 1) % state.matches.length;
|
|
565
|
+
updateActiveHighlight();
|
|
566
|
+
updateCounter();
|
|
567
|
+
scrollActiveIntoView();
|
|
568
|
+
return getState();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function previous() {
|
|
572
|
+
if (!state.query && state.input?.value) {
|
|
573
|
+
runSearch(state.input.value);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (state.matches.length === 0) {
|
|
577
|
+
return getState();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
state.activeIndex =
|
|
581
|
+
(state.activeIndex - 1 + state.matches.length) % state.matches.length;
|
|
582
|
+
updateActiveHighlight();
|
|
583
|
+
updateCounter();
|
|
584
|
+
scrollActiveIntoView();
|
|
585
|
+
return getState();
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function observeDocumentChanges() {
|
|
589
|
+
if (
|
|
590
|
+
state.observer ||
|
|
591
|
+
!document.body ||
|
|
592
|
+
typeof MutationObserver !== "function"
|
|
593
|
+
) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
state.observer = new MutationObserver((mutations) => {
|
|
598
|
+
if (!state.isOpen || !state.query) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (mutations.every((mutation) => isFindPanelNode(mutation.target))) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
debounceSearch(state.query);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
state.observer.observe(document.body, {
|
|
608
|
+
childList: true,
|
|
609
|
+
characterData: true,
|
|
610
|
+
subtree: true,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function stopObservingDocumentChanges() {
|
|
615
|
+
state.observer?.disconnect();
|
|
616
|
+
state.observer = null;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function open() {
|
|
620
|
+
if (!state.enabled) {
|
|
621
|
+
return getState();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const panel = ensurePanel();
|
|
625
|
+
panel.setAttribute("data-visible", "true");
|
|
626
|
+
state.isOpen = true;
|
|
627
|
+
observeDocumentChanges();
|
|
628
|
+
|
|
629
|
+
requestAnimationFrame(() => {
|
|
630
|
+
state.input?.focus();
|
|
631
|
+
state.input?.select();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
if (state.input?.value) {
|
|
635
|
+
runSearch(state.input.value);
|
|
636
|
+
} else {
|
|
637
|
+
updateCounter();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return getState();
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function close() {
|
|
644
|
+
clearTimeout(state.searchTimer);
|
|
645
|
+
state.isOpen = false;
|
|
646
|
+
state.panel?.removeAttribute("data-visible");
|
|
647
|
+
clearHighlights();
|
|
648
|
+
stopObservingDocumentChanges();
|
|
649
|
+
state.matches = [];
|
|
650
|
+
state.activeIndex = -1;
|
|
651
|
+
state.truncated = false;
|
|
652
|
+
updateCounter();
|
|
653
|
+
return getState();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function search(query) {
|
|
657
|
+
if (state.input) {
|
|
658
|
+
state.input.value = query;
|
|
659
|
+
}
|
|
660
|
+
return runSearch(query);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function getFindShortcutAction(event) {
|
|
664
|
+
const userAgent = navigator.userAgent || "";
|
|
665
|
+
const isMac = /macintosh|mac os x/i.test(userAgent);
|
|
666
|
+
const hasModifier = isMac
|
|
667
|
+
? event.metaKey && !event.ctrlKey
|
|
668
|
+
: event.ctrlKey && !event.metaKey;
|
|
669
|
+
|
|
670
|
+
if (!hasModifier || event.altKey) {
|
|
671
|
+
return "";
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const key = event.key?.toLowerCase();
|
|
675
|
+
if (key === "f" && !event.shiftKey) {
|
|
676
|
+
return "open";
|
|
677
|
+
}
|
|
678
|
+
if (key === "g") {
|
|
679
|
+
return event.shiftKey ? "previous" : "next";
|
|
680
|
+
}
|
|
681
|
+
return "";
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function handleFindShortcut(event) {
|
|
685
|
+
const action = getFindShortcutAction(event);
|
|
686
|
+
if (!action) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
event.preventDefault();
|
|
691
|
+
event.stopPropagation();
|
|
692
|
+
window.pakeFind[action]();
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
window.pakeFind = {
|
|
696
|
+
open,
|
|
697
|
+
close,
|
|
698
|
+
next,
|
|
699
|
+
previous,
|
|
700
|
+
search,
|
|
701
|
+
getState,
|
|
702
|
+
getFindShortcutAction,
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
if (state.enabled) {
|
|
706
|
+
document.addEventListener("keydown", handleFindShortcut, true);
|
|
707
|
+
}
|
|
708
|
+
})();
|