PageSourceSearch

https://astroknot.netlify.app/modules/UI/AiDialog.js

js astroknot.netlify.app collected 2026-10-03 10:30:32 UTC 3,473 bytes, 103 lines download raw bytes

1// ============================================================
2//  UI / AiDialog.js — AI 浮动对话框(拖拽与自由拉伸)
3// ============================================================
4
5// ==================== AI 浮窗拖拽与自由拉伸 ====================
6export function initAIFloatingDialog() {
7  const dialog = document.getElementById('aiFloatingDialog');
8  if (!dialog) return;
9
10  // 点击浮窗任意位置时置顶(始终在最上层)
11  dialog.addEventListener('mousedown', function () {
12    if (!window._aiZIndex) window._aiZIndex = 100000;
13    window._aiZIndex++;
14    dialog.style.zIndex = window._aiZIndex;
15  });
16
17  // ---------- 拖拽 ----------
18  const header = dialog.querySelector('.ai-drag-header');
19  let dragX = 0, dragY = 0, dragL = 0, dragT = 0;
20  let isDragging = false;
21
22  header.addEventListener('mousedown', (e) => {
23    if (e.target.tagName === 'SELECT' || e.target.tagName === 'BUTTON') return;
24    e.preventDefault();
25    isDragging = true;
26    dragL = dialog.offsetLeft;
27    dragT = dialog.offsetTop;
28    dragX = e.clientX;
29    dragY = e.clientY;
30    dialog.style.left = dragL + 'px';
31    dialog.style.top = dragT + 'px';
32    dialog.style.right = 'auto';
33    dialog.style.bottom = 'auto';
34    document.body.style.userSelect = 'none';
35  });
36
37  window.addEventListener('mousemove', (e) => {
38    if (!isDragging) return;
39    dialog.style.left = (dragL + e.clientX - dragX) + 'px';
40    dialog.style.top = (dragT + e.clientY - dragY) + 'px';
41  });
42
43  window.addEventListener('mouseup', () => {
44    if (isDragging) {
45      isDragging = false;
46      document.body.style.userSelect = '';
47    }
48  });
49
50  // ---------- 八方向拉伸 ----------
51  const handles = dialog.querySelectorAll('.floating-resize-handle');
52  let resizing = false;
53  let resizeHandle = '';
54  let startX = 0, startY = 0;
55  let startW = 0, startH = 0;
56  let startL = 0, startT = 0;
57
58  handles.forEach(h => {
59    h.addEventListener('mousedown', (e) => {
60      e.preventDefault();
61      e.stopPropagation();
62      resizing = true;
63      resizeHandle = h.getAttribute('data-handle');
64      startW = dialog.offsetWidth;
65      startH = dialog.offsetHeight;
66      startL = dialog.offsetLeft;
67      startT = dialog.offsetTop;
68      startX = e.clientX;
69      startY = e.clientY;
70      dialog.style.left = startL + 'px';
71      dialog.style.top = startT + 'px';
72      dialog.style.right = 'auto';
73      dialog.style.bottom = 'auto';
74      document.body.style.userSelect = 'none';
75      document.body.style.cursor = getComputedStyle(h).cursor;
76    });
77  });
78
79  window.addEventListener('mousemove', (e) => {
80    if (!resizing) return;
81    const dx = e.clientX - startX;
82    const dy = e.clientY - startY;
83    let newL = startL, newT = startT, newW = startW, newH = startH;
84
85    if (resizeHandle.includes('e')) newW = Math.max(240, startW + dx);
86    if (resizeHandle.includes('w')) { newW = Math.max(240, startW - dx); newL = startL + startW - newW; }
87    if (resizeHandle.includes('s')) newH = Math.max(200, startH + dy);
88    if (resizeHandle.includes('n')) { newH = Math.max(200, startH - dy); newT = startT + startH - newH; }
89
90    dialog.style.left = newL + 'px';
91    dialog.style.top = newT + 'px';
92    dialog.style.width = newW + 'px';
93    dialog.style.height = newH + 'px';
94  });
95
96  window.addEventListener('mouseup', () => {
97    if (resizing) {
98      resizing = false;
99      document.body.style.userSelect = '';
100      document.body.style.cursor = '';
101    }
102  });
103}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.