bloggy 0.1.40__py3-none-any.whl → 0.2.3__py3-none-any.whl
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.
- bloggy/__init__.py +1 -1
- bloggy/build.py +15 -4
- bloggy/config.py +8 -0
- bloggy/core.py +723 -95
- bloggy/main.py +14 -2
- bloggy/static/scripts.js +667 -49
- bloggy-0.2.3.dist-info/METADATA +167 -0
- bloggy-0.2.3.dist-info/RECORD +13 -0
- bloggy-0.1.40.dist-info/METADATA +0 -926
- bloggy-0.1.40.dist-info/RECORD +0 -13
- {bloggy-0.1.40.dist-info → bloggy-0.2.3.dist-info}/WHEEL +0 -0
- {bloggy-0.1.40.dist-info → bloggy-0.2.3.dist-info}/entry_points.txt +0 -0
- {bloggy-0.1.40.dist-info → bloggy-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {bloggy-0.1.40.dist-info → bloggy-0.2.3.dist-info}/top_level.txt +0 -0
bloggy/static/scripts.js
CHANGED
|
@@ -1,12 +1,146 @@
|
|
|
1
1
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
2
2
|
|
|
3
3
|
const mermaidStates = {};
|
|
4
|
+
const mermaidDebugEnabled = () => (
|
|
5
|
+
window.BLOGGY_DEBUG_MERMAID === true ||
|
|
6
|
+
localStorage.getItem('bloggyDebugMermaid') === '1'
|
|
7
|
+
);
|
|
8
|
+
const mermaidDebugLog = (...args) => {
|
|
9
|
+
if (mermaidDebugEnabled()) {
|
|
10
|
+
console.log('[bloggy][mermaid]', ...args);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const mermaidDebugSnapshot = (label) => {
|
|
14
|
+
if (!mermaidDebugEnabled()) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
18
|
+
const withSvg = wrappers.filter(w => w.querySelector('svg'));
|
|
19
|
+
const interactive = wrappers.filter(w => w.dataset.mermaidInteractive === 'true');
|
|
20
|
+
const last = wrappers[wrappers.length - 1];
|
|
21
|
+
let lastRect = null;
|
|
22
|
+
if (last) {
|
|
23
|
+
const rect = last.getBoundingClientRect();
|
|
24
|
+
lastRect = {
|
|
25
|
+
id: last.id,
|
|
26
|
+
width: Math.round(rect.width),
|
|
27
|
+
height: Math.round(rect.height),
|
|
28
|
+
hasSvg: !!last.querySelector('svg'),
|
|
29
|
+
interactive: last.dataset.mermaidInteractive === 'true'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
mermaidDebugLog(label, {
|
|
33
|
+
total: wrappers.length,
|
|
34
|
+
withSvg: withSvg.length,
|
|
35
|
+
interactive: interactive.length,
|
|
36
|
+
last: lastRect
|
|
37
|
+
});
|
|
38
|
+
};
|
|
4
39
|
const GANTT_WIDTH = 1200;
|
|
5
40
|
|
|
41
|
+
function handleCodeCopyClick(event) {
|
|
42
|
+
const button = event.target.closest('.code-copy-button, .hljs-copy-button');
|
|
43
|
+
if (!button) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
event.stopPropagation();
|
|
48
|
+
const container = button.closest('.code-block') || button.closest('pre') || button.parentElement;
|
|
49
|
+
const textarea = container ? container.querySelector('textarea[id$="-clipboard"]') : null;
|
|
50
|
+
let text = '';
|
|
51
|
+
if (textarea && textarea.value) {
|
|
52
|
+
text = textarea.value;
|
|
53
|
+
} else {
|
|
54
|
+
const codeEl = (container && container.querySelector('pre > code')) ||
|
|
55
|
+
(container && container.querySelector('code')) ||
|
|
56
|
+
button.closest('pre');
|
|
57
|
+
if (!codeEl) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
text = codeEl.innerText || codeEl.textContent || '';
|
|
61
|
+
}
|
|
62
|
+
const showToast = () => {
|
|
63
|
+
let toast = document.getElementById('code-copy-toast');
|
|
64
|
+
if (!toast) {
|
|
65
|
+
toast = document.createElement('div');
|
|
66
|
+
toast.id = 'code-copy-toast';
|
|
67
|
+
toast.className = 'fixed top-6 right-6 z-[10000] text-xs bg-slate-900 text-white px-3 py-2 rounded shadow-lg opacity-0 transition-opacity duration-300';
|
|
68
|
+
toast.textContent = 'Copied';
|
|
69
|
+
document.body.appendChild(toast);
|
|
70
|
+
}
|
|
71
|
+
toast.classList.remove('opacity-0');
|
|
72
|
+
toast.classList.add('opacity-100');
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
toast.classList.remove('opacity-100');
|
|
75
|
+
toast.classList.add('opacity-0');
|
|
76
|
+
}, 1400);
|
|
77
|
+
};
|
|
78
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
79
|
+
navigator.clipboard.writeText(text).then(showToast).catch(() => {
|
|
80
|
+
const textarea = document.createElement('textarea');
|
|
81
|
+
textarea.value = text;
|
|
82
|
+
textarea.setAttribute('readonly', '');
|
|
83
|
+
textarea.style.position = 'absolute';
|
|
84
|
+
textarea.style.left = '-9999px';
|
|
85
|
+
document.body.appendChild(textarea);
|
|
86
|
+
textarea.select();
|
|
87
|
+
document.execCommand('copy');
|
|
88
|
+
document.body.removeChild(textarea);
|
|
89
|
+
showToast();
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
const textarea = document.createElement('textarea');
|
|
93
|
+
textarea.value = text;
|
|
94
|
+
textarea.setAttribute('readonly', '');
|
|
95
|
+
textarea.style.position = 'absolute';
|
|
96
|
+
textarea.style.left = '-9999px';
|
|
97
|
+
document.body.appendChild(textarea);
|
|
98
|
+
textarea.select();
|
|
99
|
+
document.execCommand('copy');
|
|
100
|
+
document.body.removeChild(textarea);
|
|
101
|
+
showToast();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
document.addEventListener('click', handleCodeCopyClick, true);
|
|
106
|
+
|
|
6
107
|
function initMermaidInteraction() {
|
|
7
|
-
document.querySelectorAll('.mermaid-wrapper')
|
|
108
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
109
|
+
if (mermaidDebugEnabled()) {
|
|
110
|
+
const pending = wrappers.filter(w => !w.querySelector('svg'));
|
|
111
|
+
const last = wrappers[wrappers.length - 1];
|
|
112
|
+
mermaidDebugLog('initMermaidInteraction: total', wrappers.length, 'pending', pending.length);
|
|
113
|
+
if (last) {
|
|
114
|
+
mermaidDebugLog('initMermaidInteraction: last wrapper', last.id, 'hasSvg', !!last.querySelector('svg'));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
wrappers.forEach((wrapper, idx) => {
|
|
8
118
|
const svg = wrapper.querySelector('svg');
|
|
9
|
-
|
|
119
|
+
const alreadyInteractive = wrapper.dataset.mermaidInteractive === 'true';
|
|
120
|
+
if (mermaidDebugEnabled()) {
|
|
121
|
+
mermaidDebugLog(
|
|
122
|
+
'initMermaidInteraction: wrapper',
|
|
123
|
+
idx,
|
|
124
|
+
wrapper.id,
|
|
125
|
+
'hasSvg',
|
|
126
|
+
!!svg,
|
|
127
|
+
'interactive',
|
|
128
|
+
alreadyInteractive
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const getSvg = () => wrapper.querySelector('svg');
|
|
132
|
+
const applySvgState = (currentSvg) => {
|
|
133
|
+
if (!currentSvg) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
currentSvg.style.pointerEvents = 'none';
|
|
137
|
+
currentSvg.style.transform = `translate(${state.translateX}px, ${state.translateY}px) scale(${state.scale})`;
|
|
138
|
+
currentSvg.style.transformOrigin = 'center center';
|
|
139
|
+
};
|
|
140
|
+
if (svg) {
|
|
141
|
+
svg.style.pointerEvents = 'none';
|
|
142
|
+
}
|
|
143
|
+
if (!svg || alreadyInteractive) return;
|
|
10
144
|
|
|
11
145
|
// DEBUG: Log initial state
|
|
12
146
|
console.group(`🔍 initMermaidInteraction: ${wrapper.id}`);
|
|
@@ -26,16 +160,28 @@ function initMermaidInteraction() {
|
|
|
26
160
|
|
|
27
161
|
// For very wide diagrams (like Gantt charts), prefer width scaling even if it exceeds height
|
|
28
162
|
const aspectRatio = svgRect.width / svgRect.height;
|
|
163
|
+
const maxUpscale = 1;
|
|
29
164
|
let initialScale;
|
|
30
165
|
if (aspectRatio > 3) {
|
|
31
|
-
// Wide diagram: scale to fit width,
|
|
32
|
-
initialScale = scaleX;
|
|
166
|
+
// Wide diagram: scale to fit width, but do not upscale by default
|
|
167
|
+
initialScale = Math.min(scaleX, maxUpscale);
|
|
33
168
|
console.log('Wide diagram detected (aspect ratio > 3):', aspectRatio, 'Using scaleX:', initialScale);
|
|
34
169
|
} else {
|
|
35
|
-
// Normal diagram: fit to smaller dimension, but
|
|
36
|
-
initialScale = Math.min(scaleX, scaleY,
|
|
170
|
+
// Normal diagram: fit to smaller dimension, but do not upscale by default
|
|
171
|
+
initialScale = Math.min(scaleX, scaleY, maxUpscale);
|
|
37
172
|
console.log('Normal diagram (aspect ratio <=3):', aspectRatio, 'Using min scale:', initialScale);
|
|
38
173
|
}
|
|
174
|
+
|
|
175
|
+
if (mermaidDebugEnabled()) {
|
|
176
|
+
mermaidDebugLog('initMermaidInteraction: sizing', {
|
|
177
|
+
id: wrapper.id,
|
|
178
|
+
wrapperWidth: wrapperRect.width,
|
|
179
|
+
wrapperHeight: wrapperRect.height,
|
|
180
|
+
svgWidth: svgRect.width,
|
|
181
|
+
svgHeight: svgRect.height,
|
|
182
|
+
initialScale
|
|
183
|
+
});
|
|
184
|
+
}
|
|
39
185
|
|
|
40
186
|
const state = {
|
|
41
187
|
scale: initialScale,
|
|
@@ -46,22 +192,46 @@ function initMermaidInteraction() {
|
|
|
46
192
|
startY: 0
|
|
47
193
|
};
|
|
48
194
|
mermaidStates[wrapper.id] = state;
|
|
195
|
+
wrapper.dataset.mermaidInteractive = 'true';
|
|
49
196
|
console.log('Final state:', state);
|
|
50
197
|
console.groupEnd();
|
|
198
|
+
|
|
199
|
+
if (mermaidDebugEnabled() && !wrapper.dataset.mermaidDebugBound) {
|
|
200
|
+
wrapper.dataset.mermaidDebugBound = 'true';
|
|
201
|
+
const logEvent = (name, event) => {
|
|
202
|
+
const target = event.target && event.target.tagName ? event.target.tagName : 'unknown';
|
|
203
|
+
mermaidDebugLog(`${name} on ${wrapper.id}`, { type: event.type, target });
|
|
204
|
+
};
|
|
205
|
+
wrapper.addEventListener('pointerdown', (e) => logEvent('pointerdown', e));
|
|
206
|
+
wrapper.addEventListener('pointermove', (e) => logEvent('pointermove', e));
|
|
207
|
+
wrapper.addEventListener('pointerup', (e) => logEvent('pointerup', e));
|
|
208
|
+
wrapper.addEventListener('wheel', (e) => logEvent('wheel', e));
|
|
209
|
+
}
|
|
51
210
|
|
|
52
211
|
function updateTransform() {
|
|
53
|
-
|
|
54
|
-
svg.style.transformOrigin = 'center center';
|
|
212
|
+
applySvgState(getSvg());
|
|
55
213
|
}
|
|
56
214
|
|
|
57
215
|
// Apply initial scale
|
|
58
216
|
updateTransform();
|
|
217
|
+
|
|
218
|
+
if (!wrapper.dataset.mermaidObserver) {
|
|
219
|
+
const observer = new MutationObserver(() => {
|
|
220
|
+
applySvgState(getSvg());
|
|
221
|
+
});
|
|
222
|
+
observer.observe(wrapper, { childList: true, subtree: true });
|
|
223
|
+
wrapper.dataset.mermaidObserver = 'true';
|
|
224
|
+
}
|
|
59
225
|
|
|
60
226
|
// Mouse wheel zoom (zooms towards cursor position)
|
|
61
227
|
wrapper.addEventListener('wheel', (e) => {
|
|
62
228
|
e.preventDefault();
|
|
63
229
|
|
|
64
|
-
const
|
|
230
|
+
const currentSvg = getSvg();
|
|
231
|
+
if (!currentSvg) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const rect = currentSvg.getBoundingClientRect();
|
|
65
235
|
|
|
66
236
|
// Mouse position relative to SVG's current position
|
|
67
237
|
const mouseX = e.clientX - rect.left - rect.width / 2;
|
|
@@ -81,34 +251,75 @@ function initMermaidInteraction() {
|
|
|
81
251
|
updateTransform();
|
|
82
252
|
}, { passive: false });
|
|
83
253
|
|
|
84
|
-
// Pan with mouse
|
|
85
|
-
wrapper.
|
|
86
|
-
|
|
254
|
+
// Pan with pointer drag (mouse + touch)
|
|
255
|
+
wrapper.style.cursor = 'grab';
|
|
256
|
+
wrapper.style.touchAction = 'none';
|
|
257
|
+
wrapper.addEventListener('pointerdown', (e) => {
|
|
258
|
+
if (e.pointerType === 'mouse' && e.button !== 0) return;
|
|
87
259
|
state.isPanning = true;
|
|
88
260
|
state.startX = e.clientX - state.translateX;
|
|
89
261
|
state.startY = e.clientY - state.translateY;
|
|
262
|
+
wrapper.setPointerCapture(e.pointerId);
|
|
90
263
|
wrapper.style.cursor = 'grabbing';
|
|
91
264
|
e.preventDefault();
|
|
92
265
|
});
|
|
93
266
|
|
|
94
|
-
|
|
267
|
+
wrapper.addEventListener('pointermove', (e) => {
|
|
95
268
|
if (!state.isPanning) return;
|
|
96
269
|
state.translateX = e.clientX - state.startX;
|
|
97
270
|
state.translateY = e.clientY - state.startY;
|
|
98
271
|
updateTransform();
|
|
272
|
+
if (mermaidDebugEnabled()) {
|
|
273
|
+
mermaidDebugLog('pan update', wrapper.id, {
|
|
274
|
+
translateX: state.translateX,
|
|
275
|
+
translateY: state.translateY,
|
|
276
|
+
scale: state.scale,
|
|
277
|
+
svgTransform: (getSvg() && getSvg().style.transform) || ''
|
|
278
|
+
});
|
|
279
|
+
}
|
|
99
280
|
});
|
|
100
281
|
|
|
101
|
-
|
|
102
|
-
if (state.isPanning)
|
|
103
|
-
|
|
104
|
-
|
|
282
|
+
const stopPanning = (e) => {
|
|
283
|
+
if (!state.isPanning) return;
|
|
284
|
+
state.isPanning = false;
|
|
285
|
+
try {
|
|
286
|
+
wrapper.releasePointerCapture(e.pointerId);
|
|
287
|
+
} catch {
|
|
288
|
+
// Ignore if pointer capture is not active
|
|
105
289
|
}
|
|
106
|
-
|
|
290
|
+
wrapper.style.cursor = 'grab';
|
|
291
|
+
};
|
|
107
292
|
|
|
108
|
-
wrapper.
|
|
293
|
+
wrapper.addEventListener('pointerup', stopPanning);
|
|
294
|
+
wrapper.addEventListener('pointercancel', stopPanning);
|
|
109
295
|
});
|
|
110
296
|
}
|
|
111
297
|
|
|
298
|
+
function scheduleMermaidInteraction({ maxAttempts = 12, delayMs = 80, onReady } = {}) {
|
|
299
|
+
let attempt = 0;
|
|
300
|
+
const check = () => {
|
|
301
|
+
const wrappers = Array.from(document.querySelectorAll('.mermaid-wrapper'));
|
|
302
|
+
const pending = wrappers.filter(wrapper => !wrapper.querySelector('svg'));
|
|
303
|
+
if (mermaidDebugEnabled()) {
|
|
304
|
+
const last = wrappers[wrappers.length - 1];
|
|
305
|
+
mermaidDebugLog('scheduleMermaidInteraction attempt', attempt, 'pending', pending.length);
|
|
306
|
+
if (last) {
|
|
307
|
+
mermaidDebugLog('scheduleMermaidInteraction last wrapper', last.id, 'hasSvg', !!last.querySelector('svg'));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (pending.length === 0 || attempt >= maxAttempts) {
|
|
311
|
+
initMermaidInteraction();
|
|
312
|
+
if (typeof onReady === 'function') {
|
|
313
|
+
onReady();
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
attempt += 1;
|
|
318
|
+
setTimeout(check, delayMs);
|
|
319
|
+
};
|
|
320
|
+
check();
|
|
321
|
+
}
|
|
322
|
+
|
|
112
323
|
window.resetMermaidZoom = function(id) {
|
|
113
324
|
const state = mermaidStates[id];
|
|
114
325
|
if (state) {
|
|
@@ -263,6 +474,11 @@ function reinitializeMermaid() {
|
|
|
263
474
|
});
|
|
264
475
|
|
|
265
476
|
// Find all mermaid wrappers and re-render them
|
|
477
|
+
const shouldLockHeight = (wrapper) => {
|
|
478
|
+
const height = (wrapper.style.height || '').trim();
|
|
479
|
+
return height && height !== 'auto' && height !== 'initial' && height !== 'unset';
|
|
480
|
+
};
|
|
481
|
+
|
|
266
482
|
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
267
483
|
const originalCode = wrapper.getAttribute('data-mermaid-code');
|
|
268
484
|
if (originalCode) {
|
|
@@ -271,12 +487,15 @@ function reinitializeMermaid() {
|
|
|
271
487
|
console.log('BEFORE clear - wrapper rect:', wrapper.getBoundingClientRect());
|
|
272
488
|
|
|
273
489
|
// Preserve the current computed height before clearing (height should already be set explicitly)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
490
|
+
if (shouldLockHeight(wrapper)) {
|
|
491
|
+
const currentHeight = wrapper.getBoundingClientRect().height;
|
|
492
|
+
console.log('Preserving height:', currentHeight);
|
|
493
|
+
wrapper.style.height = currentHeight + 'px';
|
|
494
|
+
}
|
|
277
495
|
|
|
278
496
|
// Delete the old state so it can be recreated
|
|
279
497
|
delete mermaidStates[wrapper.id];
|
|
498
|
+
delete wrapper.dataset.mermaidInteractive;
|
|
280
499
|
|
|
281
500
|
// Decode HTML entities
|
|
282
501
|
const textarea = document.createElement('textarea');
|
|
@@ -298,11 +517,12 @@ function reinitializeMermaid() {
|
|
|
298
517
|
|
|
299
518
|
// Re-run mermaid
|
|
300
519
|
mermaid.run().then(() => {
|
|
301
|
-
console.log('Mermaid re-render complete,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
520
|
+
console.log('Mermaid re-render complete, scheduling initMermaidInteraction');
|
|
521
|
+
scheduleMermaidInteraction({
|
|
522
|
+
onReady: () => {
|
|
523
|
+
console.groupEnd();
|
|
524
|
+
}
|
|
525
|
+
});
|
|
306
526
|
});
|
|
307
527
|
}
|
|
308
528
|
|
|
@@ -312,7 +532,7 @@ const initialGanttWidth = getDynamicGanttWidth();
|
|
|
312
532
|
console.log('Using initial Gantt width:', initialGanttWidth);
|
|
313
533
|
|
|
314
534
|
mermaid.initialize({
|
|
315
|
-
startOnLoad:
|
|
535
|
+
startOnLoad: false,
|
|
316
536
|
theme: getCurrentTheme(),
|
|
317
537
|
gantt: {
|
|
318
538
|
useWidth: initialGanttWidth,
|
|
@@ -324,20 +544,32 @@ mermaid.initialize({
|
|
|
324
544
|
let isInitialLoad = true;
|
|
325
545
|
|
|
326
546
|
// Initialize interaction after mermaid renders
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
547
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
548
|
+
mermaidDebugSnapshot('before mermaid.run (DOMContentLoaded)');
|
|
549
|
+
mermaid.run().then(() => {
|
|
550
|
+
mermaidDebugSnapshot('after mermaid.run (DOMContentLoaded)');
|
|
551
|
+
console.log('Initial mermaid render complete');
|
|
552
|
+
scheduleMermaidInteraction({
|
|
553
|
+
onReady: () => {
|
|
554
|
+
console.log('Calling initial initMermaidInteraction');
|
|
555
|
+
|
|
556
|
+
// After initial render, set explicit heights on all wrappers so theme switching works
|
|
557
|
+
const shouldLockHeight = (wrapper) => {
|
|
558
|
+
const height = (wrapper.style.height || '').trim();
|
|
559
|
+
return height && height !== 'auto' && height !== 'initial' && height !== 'unset';
|
|
560
|
+
};
|
|
561
|
+
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
562
|
+
if (!shouldLockHeight(wrapper)) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const currentHeight = wrapper.getBoundingClientRect().height;
|
|
566
|
+
console.log(`Setting initial height for ${wrapper.id}:`, currentHeight);
|
|
567
|
+
wrapper.style.height = currentHeight + 'px';
|
|
568
|
+
});
|
|
569
|
+
isInitialLoad = false;
|
|
570
|
+
}
|
|
338
571
|
});
|
|
339
|
-
|
|
340
|
-
}, 100);
|
|
572
|
+
});
|
|
341
573
|
});
|
|
342
574
|
|
|
343
575
|
// Reveal current file in sidebar
|
|
@@ -346,7 +578,8 @@ function revealInSidebar(rootElement = document) {
|
|
|
346
578
|
return;
|
|
347
579
|
}
|
|
348
580
|
|
|
349
|
-
|
|
581
|
+
// Decode the URL path to handle special characters and spaces
|
|
582
|
+
const currentPath = decodeURIComponent(window.location.pathname.replace(/^\/posts\//, ''));
|
|
350
583
|
const activeLink = rootElement.querySelector(`.post-link[data-path="${currentPath}"]`);
|
|
351
584
|
|
|
352
585
|
if (activeLink) {
|
|
@@ -375,20 +608,33 @@ function revealInSidebar(rootElement = document) {
|
|
|
375
608
|
}
|
|
376
609
|
|
|
377
610
|
// Highlight the active link temporarily
|
|
378
|
-
activeLink.classList.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
611
|
+
activeLink.classList.remove('fade-out');
|
|
612
|
+
activeLink.classList.add('sidebar-highlight');
|
|
613
|
+
requestAnimationFrame(() => {
|
|
614
|
+
setTimeout(() => {
|
|
615
|
+
activeLink.classList.add('fade-out');
|
|
616
|
+
setTimeout(() => {
|
|
617
|
+
activeLink.classList.remove('sidebar-highlight', 'fade-out');
|
|
618
|
+
}, 10000);
|
|
619
|
+
}, 1000);
|
|
620
|
+
});
|
|
382
621
|
}
|
|
383
622
|
}
|
|
384
623
|
|
|
385
624
|
function initPostsSidebarAutoReveal() {
|
|
386
625
|
const postSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
626
|
+
|
|
387
627
|
postSidebars.forEach((sidebar) => {
|
|
388
628
|
if (sidebar.dataset.revealBound === 'true') {
|
|
389
629
|
return;
|
|
390
630
|
}
|
|
391
631
|
sidebar.dataset.revealBound = 'true';
|
|
632
|
+
|
|
633
|
+
// Reveal immediately if sidebar is already open
|
|
634
|
+
if (sidebar.open) {
|
|
635
|
+
revealInSidebar(sidebar);
|
|
636
|
+
}
|
|
637
|
+
|
|
392
638
|
sidebar.addEventListener('toggle', () => {
|
|
393
639
|
if (!sidebar.open) {
|
|
394
640
|
return;
|
|
@@ -404,6 +650,214 @@ function initFolderChevronState(rootElement = document) {
|
|
|
404
650
|
});
|
|
405
651
|
}
|
|
406
652
|
|
|
653
|
+
function initSearchPlaceholderCycle(rootElement = document) {
|
|
654
|
+
const inputs = rootElement.querySelectorAll('input[data-placeholder-cycle]');
|
|
655
|
+
inputs.forEach((input) => {
|
|
656
|
+
if (input.dataset.placeholderCycleBound === 'true') {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
input.dataset.placeholderCycleBound = 'true';
|
|
660
|
+
const primary = input.dataset.placeholderPrimary || input.getAttribute('placeholder') || '';
|
|
661
|
+
const alt = input.dataset.placeholderAlt || '';
|
|
662
|
+
if (!alt) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
let showAlt = false;
|
|
666
|
+
setInterval(() => {
|
|
667
|
+
if (input.value) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
showAlt = !showAlt;
|
|
671
|
+
input.setAttribute('placeholder', showAlt ? alt : primary);
|
|
672
|
+
}, 10000);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function initCodeBlockCopyButtons(rootElement = document) {
|
|
677
|
+
const buttons = rootElement.querySelectorAll('.code-copy-button');
|
|
678
|
+
buttons.forEach((button) => {
|
|
679
|
+
if (button.dataset.copyBound === 'true') {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
button.dataset.copyBound = 'true';
|
|
683
|
+
button.addEventListener('click', () => {
|
|
684
|
+
const container = button.closest('.code-block');
|
|
685
|
+
const codeEl = container ? container.querySelector('pre > code') : null;
|
|
686
|
+
if (!codeEl) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const text = codeEl.innerText || codeEl.textContent || '';
|
|
690
|
+
const done = () => {
|
|
691
|
+
button.classList.add('is-copied');
|
|
692
|
+
setTimeout(() => button.classList.remove('is-copied'), 1200);
|
|
693
|
+
};
|
|
694
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
695
|
+
navigator.clipboard.writeText(text).then(done).catch(() => {
|
|
696
|
+
const textarea = document.createElement('textarea');
|
|
697
|
+
textarea.value = text;
|
|
698
|
+
textarea.setAttribute('readonly', '');
|
|
699
|
+
textarea.style.position = 'absolute';
|
|
700
|
+
textarea.style.left = '-9999px';
|
|
701
|
+
document.body.appendChild(textarea);
|
|
702
|
+
textarea.select();
|
|
703
|
+
document.execCommand('copy');
|
|
704
|
+
document.body.removeChild(textarea);
|
|
705
|
+
done();
|
|
706
|
+
});
|
|
707
|
+
} else {
|
|
708
|
+
const textarea = document.createElement('textarea');
|
|
709
|
+
textarea.value = text;
|
|
710
|
+
textarea.setAttribute('readonly', '');
|
|
711
|
+
textarea.style.position = 'absolute';
|
|
712
|
+
textarea.style.left = '-9999px';
|
|
713
|
+
document.body.appendChild(textarea);
|
|
714
|
+
textarea.select();
|
|
715
|
+
document.execCommand('copy');
|
|
716
|
+
document.body.removeChild(textarea);
|
|
717
|
+
done();
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function initPostsSearchPersistence(rootElement = document) {
|
|
724
|
+
const input = rootElement.querySelector('.posts-search-block input[type="search"][name="q"]');
|
|
725
|
+
const results = rootElement.querySelector('.posts-search-results');
|
|
726
|
+
if (!input || !results) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (input.dataset.searchPersistenceBound === 'true') {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
input.dataset.searchPersistenceBound = 'true';
|
|
733
|
+
const termKey = 'bloggy:postsSearchTerm';
|
|
734
|
+
const resultsKey = 'bloggy:postsSearchResults';
|
|
735
|
+
const enhanceGatherLink = () => {
|
|
736
|
+
const gatherLink = results.querySelector('a[href^="/search/gather"]');
|
|
737
|
+
if (!gatherLink) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const href = gatherLink.getAttribute('href');
|
|
741
|
+
if (!href) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
gatherLink.setAttribute('hx_get', href);
|
|
745
|
+
gatherLink.setAttribute('hx_target', '#main-content');
|
|
746
|
+
gatherLink.setAttribute('hx_push_url', 'true');
|
|
747
|
+
gatherLink.setAttribute('hx_swap', 'outerHTML show:window:top settle:0.1s');
|
|
748
|
+
};
|
|
749
|
+
let storedTerm = '';
|
|
750
|
+
let storedResults = null;
|
|
751
|
+
try {
|
|
752
|
+
storedTerm = localStorage.getItem(termKey) || '';
|
|
753
|
+
storedResults = localStorage.getItem(resultsKey);
|
|
754
|
+
} catch (err) {
|
|
755
|
+
storedTerm = '';
|
|
756
|
+
storedResults = null;
|
|
757
|
+
}
|
|
758
|
+
if (storedTerm && !input.value) {
|
|
759
|
+
input.value = storedTerm;
|
|
760
|
+
}
|
|
761
|
+
if (storedResults && input.value) {
|
|
762
|
+
try {
|
|
763
|
+
const payload = JSON.parse(storedResults);
|
|
764
|
+
if (payload && payload.term === input.value && payload.html) {
|
|
765
|
+
results.innerHTML = payload.html;
|
|
766
|
+
enhanceGatherLink();
|
|
767
|
+
}
|
|
768
|
+
} catch (err) {
|
|
769
|
+
// Ignore malformed cached payloads.
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const persistTerm = () => {
|
|
773
|
+
try {
|
|
774
|
+
if (input.value) {
|
|
775
|
+
localStorage.setItem(termKey, input.value);
|
|
776
|
+
} else {
|
|
777
|
+
localStorage.removeItem(termKey);
|
|
778
|
+
localStorage.removeItem(resultsKey);
|
|
779
|
+
}
|
|
780
|
+
} catch (err) {
|
|
781
|
+
// Ignore storage failures.
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
input.addEventListener('input', persistTerm);
|
|
785
|
+
const fetchResults = (query) => {
|
|
786
|
+
return fetch(`/_sidebar/posts/search?q=${query}`)
|
|
787
|
+
.then((response) => response.text())
|
|
788
|
+
.then((html) => {
|
|
789
|
+
results.innerHTML = html;
|
|
790
|
+
enhanceGatherLink();
|
|
791
|
+
try {
|
|
792
|
+
localStorage.setItem(resultsKey, JSON.stringify({
|
|
793
|
+
term: input.value,
|
|
794
|
+
html: results.innerHTML
|
|
795
|
+
}));
|
|
796
|
+
} catch (err) {
|
|
797
|
+
// Ignore storage failures.
|
|
798
|
+
}
|
|
799
|
+
})
|
|
800
|
+
.catch(() => {});
|
|
801
|
+
};
|
|
802
|
+
document.body.addEventListener('htmx:afterSwap', (event) => {
|
|
803
|
+
if (event.target !== results) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
enhanceGatherLink();
|
|
807
|
+
try {
|
|
808
|
+
localStorage.setItem(resultsKey, JSON.stringify({
|
|
809
|
+
term: input.value,
|
|
810
|
+
html: results.innerHTML
|
|
811
|
+
}));
|
|
812
|
+
} catch (err) {
|
|
813
|
+
// Ignore storage failures.
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
if (input.value) {
|
|
817
|
+
const query = encodeURIComponent(input.value);
|
|
818
|
+
if (window.htmx && typeof window.htmx.ajax === 'function') {
|
|
819
|
+
window.htmx.ajax('GET', `/_sidebar/posts/search?q=${query}`, { target: results, swap: 'innerHTML' });
|
|
820
|
+
} else {
|
|
821
|
+
fetchResults(query);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function initSearchClearButtons(rootElement = document) {
|
|
827
|
+
const blocks = rootElement.querySelectorAll('.posts-search-block');
|
|
828
|
+
blocks.forEach((block) => {
|
|
829
|
+
const input = block.querySelector('input[type="search"][name="q"]');
|
|
830
|
+
const button = block.querySelector('.posts-search-clear-button');
|
|
831
|
+
const results = block.querySelector('.posts-search-results');
|
|
832
|
+
if (!input || !button) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (button.dataset.clearBound === 'true') {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
button.dataset.clearBound = 'true';
|
|
839
|
+
const updateVisibility = () => {
|
|
840
|
+
button.style.opacity = input.value ? '1' : '0';
|
|
841
|
+
button.style.pointerEvents = input.value ? 'auto' : 'none';
|
|
842
|
+
};
|
|
843
|
+
updateVisibility();
|
|
844
|
+
input.addEventListener('input', updateVisibility);
|
|
845
|
+
button.addEventListener('click', () => {
|
|
846
|
+
input.value = '';
|
|
847
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
848
|
+
if (results) {
|
|
849
|
+
results.innerHTML = '';
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
localStorage.removeItem('bloggy:postsSearchTerm');
|
|
853
|
+
localStorage.removeItem('bloggy:postsSearchResults');
|
|
854
|
+
} catch (err) {
|
|
855
|
+
// Ignore storage failures.
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
|
|
407
861
|
document.addEventListener('toggle', (event) => {
|
|
408
862
|
const details = event.target;
|
|
409
863
|
if (!(details instanceof HTMLDetailsElement)) {
|
|
@@ -431,17 +885,27 @@ function updateActivePostLink() {
|
|
|
431
885
|
}
|
|
432
886
|
|
|
433
887
|
// Update active TOC link based on scroll position
|
|
888
|
+
let lastActiveTocAnchor = null;
|
|
434
889
|
function updateActiveTocLink() {
|
|
435
890
|
const headings = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');
|
|
436
891
|
const tocLinks = document.querySelectorAll('.toc-link');
|
|
437
892
|
|
|
438
893
|
let activeHeading = null;
|
|
894
|
+
let nearestBelow = null;
|
|
895
|
+
let nearestBelowTop = Infinity;
|
|
896
|
+
const offset = 140;
|
|
439
897
|
headings.forEach(heading => {
|
|
440
898
|
const rect = heading.getBoundingClientRect();
|
|
441
|
-
if (rect.top <=
|
|
899
|
+
if (rect.top <= offset) {
|
|
442
900
|
activeHeading = heading;
|
|
901
|
+
} else if (rect.top < nearestBelowTop) {
|
|
902
|
+
nearestBelowTop = rect.top;
|
|
903
|
+
nearestBelow = heading;
|
|
443
904
|
}
|
|
444
905
|
});
|
|
906
|
+
if (!activeHeading && nearestBelow) {
|
|
907
|
+
activeHeading = nearestBelow;
|
|
908
|
+
}
|
|
445
909
|
|
|
446
910
|
tocLinks.forEach(link => {
|
|
447
911
|
const anchor = link.getAttribute('data-anchor');
|
|
@@ -451,6 +915,14 @@ function updateActiveTocLink() {
|
|
|
451
915
|
link.classList.remove('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-600', 'dark:text-blue-400', 'font-semibold');
|
|
452
916
|
}
|
|
453
917
|
});
|
|
918
|
+
|
|
919
|
+
const activeId = activeHeading ? activeHeading.id : null;
|
|
920
|
+
if (activeId && activeId !== lastActiveTocAnchor) {
|
|
921
|
+
document.querySelectorAll(`.toc-link[data-anchor="${activeId}"]`).forEach(link => {
|
|
922
|
+
link.scrollIntoView({ block: 'nearest' });
|
|
923
|
+
});
|
|
924
|
+
lastActiveTocAnchor = activeId;
|
|
925
|
+
}
|
|
454
926
|
}
|
|
455
927
|
|
|
456
928
|
// Listen for scroll events to update active TOC link
|
|
@@ -465,16 +937,71 @@ window.addEventListener('scroll', () => {
|
|
|
465
937
|
}
|
|
466
938
|
});
|
|
467
939
|
|
|
940
|
+
// Sync TOC highlight on hash changes and TOC clicks
|
|
941
|
+
window.addEventListener('hashchange', () => {
|
|
942
|
+
requestAnimationFrame(updateActiveTocLink);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
document.addEventListener('click', (event) => {
|
|
946
|
+
const link = event.target.closest('.toc-link');
|
|
947
|
+
if (!link) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const anchor = link.getAttribute('data-anchor');
|
|
951
|
+
if (!anchor) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
requestAnimationFrame(() => {
|
|
955
|
+
document.querySelectorAll('.toc-link').forEach(item => {
|
|
956
|
+
item.classList.toggle(
|
|
957
|
+
'bg-blue-50',
|
|
958
|
+
item.getAttribute('data-anchor') === anchor
|
|
959
|
+
);
|
|
960
|
+
item.classList.toggle(
|
|
961
|
+
'dark:bg-blue-900/20',
|
|
962
|
+
item.getAttribute('data-anchor') === anchor
|
|
963
|
+
);
|
|
964
|
+
item.classList.toggle(
|
|
965
|
+
'text-blue-600',
|
|
966
|
+
item.getAttribute('data-anchor') === anchor
|
|
967
|
+
);
|
|
968
|
+
item.classList.toggle(
|
|
969
|
+
'dark:text-blue-400',
|
|
970
|
+
item.getAttribute('data-anchor') === anchor
|
|
971
|
+
);
|
|
972
|
+
item.classList.toggle(
|
|
973
|
+
'font-semibold',
|
|
974
|
+
item.getAttribute('data-anchor') === anchor
|
|
975
|
+
);
|
|
976
|
+
});
|
|
977
|
+
lastActiveTocAnchor = anchor;
|
|
978
|
+
updateActiveTocLink();
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
|
|
468
982
|
// Re-run mermaid on HTMX content swaps
|
|
469
|
-
document.body.addEventListener('htmx:afterSwap', function() {
|
|
983
|
+
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
984
|
+
mermaidDebugSnapshot('before mermaid.run (htmx:afterSwap)');
|
|
985
|
+
document.querySelectorAll('.mermaid-wrapper').forEach(wrapper => {
|
|
986
|
+
if (!wrapper.id) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
// HTMX swaps can trigger a mermaid re-run that replaces SVGs.
|
|
990
|
+
// Clear interaction state so we always re-bind after mermaid.run().
|
|
991
|
+
delete mermaidStates[wrapper.id];
|
|
992
|
+
delete wrapper.dataset.mermaidInteractive;
|
|
993
|
+
});
|
|
470
994
|
mermaid.run().then(() => {
|
|
471
|
-
|
|
995
|
+
mermaidDebugSnapshot('after mermaid.run (htmx:afterSwap)');
|
|
996
|
+
scheduleMermaidInteraction();
|
|
472
997
|
});
|
|
473
998
|
updateActivePostLink();
|
|
474
999
|
updateActiveTocLink();
|
|
475
1000
|
initMobileMenus(); // Reinitialize mobile menu handlers
|
|
476
1001
|
initPostsSidebarAutoReveal();
|
|
477
1002
|
initFolderChevronState();
|
|
1003
|
+
initSearchPlaceholderCycle(event.target || document);
|
|
1004
|
+
initCodeBlockCopyButtons(event.target || document);
|
|
478
1005
|
});
|
|
479
1006
|
|
|
480
1007
|
// Watch for theme changes and re-render mermaid diagrams
|
|
@@ -574,6 +1101,79 @@ function initMobileMenus() {
|
|
|
574
1101
|
}
|
|
575
1102
|
}
|
|
576
1103
|
|
|
1104
|
+
// Keyboard shortcuts for toggling sidebars
|
|
1105
|
+
function initKeyboardShortcuts() {
|
|
1106
|
+
// Prewarm the selectors to avoid lazy compilation delays
|
|
1107
|
+
const postsSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
1108
|
+
const tocSidebar = document.querySelector('#toc-sidebar details');
|
|
1109
|
+
|
|
1110
|
+
document.addEventListener('keydown', (e) => {
|
|
1111
|
+
// Skip if user is typing in an input field
|
|
1112
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Z: Toggle posts panel
|
|
1117
|
+
if (e.key === 'z' || e.key === 'Z') {
|
|
1118
|
+
e.preventDefault();
|
|
1119
|
+
const postsSidebars = document.querySelectorAll('details[data-sidebar="posts"]');
|
|
1120
|
+
postsSidebars.forEach(sidebar => {
|
|
1121
|
+
sidebar.open = !sidebar.open;
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// X: Toggle TOC panel
|
|
1126
|
+
if (e.key === 'x' || e.key === 'X') {
|
|
1127
|
+
e.preventDefault();
|
|
1128
|
+
const tocSidebar = document.querySelector('#toc-sidebar details');
|
|
1129
|
+
if (tocSidebar) {
|
|
1130
|
+
tocSidebar.open = !tocSidebar.open;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function syncPdfFocusButtons(root = document) {
|
|
1137
|
+
const isFocused = document.body.classList.contains('pdf-focus');
|
|
1138
|
+
root.querySelectorAll('[data-pdf-focus-toggle]').forEach((button) => {
|
|
1139
|
+
const focusLabel = button.getAttribute('data-pdf-focus-label') || 'Focus PDF';
|
|
1140
|
+
const exitLabel = button.getAttribute('data-pdf-exit-label') || 'Exit focus';
|
|
1141
|
+
button.textContent = isFocused ? exitLabel : focusLabel;
|
|
1142
|
+
button.setAttribute('aria-pressed', isFocused ? 'true' : 'false');
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function ensurePdfFocusState() {
|
|
1147
|
+
const hasPdfViewer = document.querySelector('.pdf-viewer') || document.querySelector('[data-pdf-focus-toggle]');
|
|
1148
|
+
if (!hasPdfViewer) {
|
|
1149
|
+
document.body.classList.remove('pdf-focus');
|
|
1150
|
+
}
|
|
1151
|
+
syncPdfFocusButtons(document);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function initPdfFocusToggle() {
|
|
1155
|
+
document.addEventListener('click', (event) => {
|
|
1156
|
+
const button = event.target.closest('[data-pdf-focus-toggle]');
|
|
1157
|
+
if (!button) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
event.preventDefault();
|
|
1161
|
+
document.body.classList.toggle('pdf-focus');
|
|
1162
|
+
syncPdfFocusButtons(document);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
document.addEventListener('keydown', (event) => {
|
|
1166
|
+
if (event.key !== 'Escape') {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (!document.body.classList.contains('pdf-focus')) {
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
document.body.classList.remove('pdf-focus');
|
|
1173
|
+
syncPdfFocusButtons(document);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
577
1177
|
// Initialize on page load
|
|
578
1178
|
document.addEventListener('DOMContentLoaded', () => {
|
|
579
1179
|
updateActivePostLink();
|
|
@@ -581,4 +1181,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
581
1181
|
initMobileMenus();
|
|
582
1182
|
initPostsSidebarAutoReveal();
|
|
583
1183
|
initFolderChevronState();
|
|
1184
|
+
initKeyboardShortcuts();
|
|
1185
|
+
initPdfFocusToggle();
|
|
1186
|
+
initSearchPlaceholderCycle(document);
|
|
1187
|
+
initPostsSearchPersistence(document);
|
|
1188
|
+
initCodeBlockCopyButtons(document);
|
|
1189
|
+
initSearchClearButtons(document);
|
|
1190
|
+
ensurePdfFocusState();
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
document.body.addEventListener('htmx:afterSwap', (event) => {
|
|
1194
|
+
if (!event.target) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
initSearchPlaceholderCycle(event.target);
|
|
1198
|
+
initPostsSearchPersistence(event.target);
|
|
1199
|
+
initCodeBlockCopyButtons(event.target);
|
|
1200
|
+
initSearchClearButtons(event.target);
|
|
1201
|
+
ensurePdfFocusState();
|
|
584
1202
|
});
|