stellavault 0.1.0 → 0.2.0

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.
@@ -0,0 +1 @@
1
+ (function(){"use strict";const q={iterations:200,repulsion:800,attraction:.005,damping:.92,brainScale:[250,180,200]};self.onmessage=m=>{if(m.data.type!=="init")return;const{nodes:u,edges:A,options:F}=m.data,i={...q,...F},d=u.length;if(d===0){self.postMessage({type:"done",positions:[]});return}const y=new Map;u.forEach((o,f)=>y.set(o.id,f));const I=A.map(o=>({s:y.get(o.source)??-1,t:y.get(o.target)??-1,w:o.weight})).filter(o=>o.s>=0&&o.t>=0),s=new Float64Array(d*3),e=new Float64Array(d*3),[x,w,z]=i.brainScale;for(let o=0;o<d;o++){const f=Math.acos(2*Math.random()-1),p=Math.random()*2*Math.PI,t=.5+.5*Math.random(),n=1+.1*Math.sin(5*f)*Math.cos(3*p);s[o*3]=x*t*Math.sin(f)*Math.cos(p)*n,s[o*3+1]=w*t*Math.cos(f)*n,s[o*3+2]=z*t*Math.sin(f)*Math.sin(p)*n;const a=u[o].clusterId/10*2*Math.PI;s[o*3]+=30*Math.cos(a),s[o*3+2]+=30*Math.sin(a)}for(let o=0;o<i.iterations;o++){const f=1-o/i.iterations,p=i.repulsion*f;for(let t=0;t<d;t++)for(let n=t+1;n<d;n++){const r=s[n*3]-s[t*3],a=s[n*3+1]-s[t*3+1],l=s[n*3+2]-s[t*3+2],c=r*r+a*a+l*l+.01,h=p/c,M=r*h/Math.sqrt(c),g=a*h/Math.sqrt(c),b=l*h/Math.sqrt(c);e[t*3]-=M,e[t*3+1]-=g,e[t*3+2]-=b,e[n*3]+=M,e[n*3+1]+=g,e[n*3+2]+=b}for(const t of I){const n=s[t.t*3]-s[t.s*3],r=s[t.t*3+1]-s[t.s*3+1],a=s[t.t*3+2]-s[t.s*3+2],l=Math.sqrt(n*n+r*r+a*a)+.01,c=i.attraction*l*t.w,h=n/l*c,M=r/l*c,g=a/l*c;e[t.s*3]+=h,e[t.s*3+1]+=M,e[t.s*3+2]+=g,e[t.t*3]-=h,e[t.t*3+1]-=M,e[t.t*3+2]-=g}for(let t=0;t<d;t++){const n=s[t*3],r=s[t*3+1],a=s[t*3+2],l=(n/x)**2+(r/w)**2+(a/z)**2;if(l>1){const c=.3*(l-1);e[t*3]-=n*c*.01,e[t*3+1]-=r*c*.01,e[t*3+2]-=a*c*.01}s[t*3]+=e[t*3],s[t*3+1]+=e[t*3+1],s[t*3+2]+=e[t*3+2],e[t*3]*=i.damping,e[t*3+1]*=i.damping,e[t*3+2]*=i.damping}if(o%20===0||o===i.iterations-1){const t=[];for(let n=0;n<d;n++)t.push([s[n*3],s[n*3+1],s[n*3+2]]);self.postMessage({type:o===i.iterations-1?"done":"progress",positions:t,iteration:o,total:i.iterations})}}}})();
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Evan Knowledge Hub — 3D Knowledge Graph</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ html, body, #root { width: 100%; height: 100%; overflow: hidden; }
10
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0f; color: #e0e0e0; }
11
+ </style>
12
+ <script type="module" crossorigin src="/assets/index-DMEe2diW.js"></script>
13
+ </head>
14
+ <body>
15
+ <div id="root"></div>
16
+ </body>
17
+ </html>
@@ -1,49 +1,49 @@
1
- import { chromium } from 'playwright';
2
-
3
- const browser = await chromium.launch({ headless: false });
4
- const page = await browser.newPage();
5
- await page.goto('http://localhost:5173');
6
- await page.waitForTimeout(4000);
7
-
8
- const canvas = await page.locator('canvas').boundingBox();
9
- const cx = canvas.x + canvas.width / 2;
10
- const cy = canvas.y + canvas.height / 2;
11
-
12
- // 노드 찾기: 넓은 범위 스캔
13
- let found = false;
14
- for (let dx = -200; dx <= 200; dx += 30) {
15
- for (let dy = -150; dy <= 150; dy += 30) {
16
- await page.mouse.move(cx + dx, cy + dy);
17
- await page.waitForTimeout(50);
18
- const cursor = await page.evaluate(() => document.body.style.cursor);
19
- if (cursor === 'pointer') {
20
- console.log(`NODE FOUND at [${dx}, ${dy}]`);
21
-
22
- // 호버 확인
23
- await page.waitForTimeout(200);
24
-
25
- // 클릭 (pointerdown 방식)
26
- await page.mouse.click(cx + dx, cy + dy);
27
- await page.waitForTimeout(1500);
28
-
29
- // 사이드패널 확인
30
- const result = await page.evaluate(() => {
31
- const text = document.body.innerText;
32
- return {
33
- hasPanel: text.includes('DOCUMENT PREVIEW') || text.includes('Document Preview'),
34
- hasExplore: text.includes('Explore connections'),
35
- snippet: text.slice(0, 400),
36
- };
37
- });
38
- console.log('PANEL:', JSON.stringify(result, null, 2));
39
- found = true;
40
- break;
41
- }
42
- }
43
- if (found) break;
44
- }
45
-
46
- if (!found) console.log('No node found in scan range');
47
-
48
- await page.waitForTimeout(2000);
49
- await browser.close();
1
+ import { chromium } from 'playwright';
2
+
3
+ const browser = await chromium.launch({ headless: false });
4
+ const page = await browser.newPage();
5
+ await page.goto('http://localhost:5173');
6
+ await page.waitForTimeout(4000);
7
+
8
+ const canvas = await page.locator('canvas').boundingBox();
9
+ const cx = canvas.x + canvas.width / 2;
10
+ const cy = canvas.y + canvas.height / 2;
11
+
12
+ // 노드 찾기: 넓은 범위 스캔
13
+ let found = false;
14
+ for (let dx = -200; dx <= 200; dx += 30) {
15
+ for (let dy = -150; dy <= 150; dy += 30) {
16
+ await page.mouse.move(cx + dx, cy + dy);
17
+ await page.waitForTimeout(50);
18
+ const cursor = await page.evaluate(() => document.body.style.cursor);
19
+ if (cursor === 'pointer') {
20
+ console.log(`NODE FOUND at [${dx}, ${dy}]`);
21
+
22
+ // 호버 확인
23
+ await page.waitForTimeout(200);
24
+
25
+ // 클릭 (pointerdown 방식)
26
+ await page.mouse.click(cx + dx, cy + dy);
27
+ await page.waitForTimeout(1500);
28
+
29
+ // 사이드패널 확인
30
+ const result = await page.evaluate(() => {
31
+ const text = document.body.innerText;
32
+ return {
33
+ hasPanel: text.includes('DOCUMENT PREVIEW') || text.includes('Document Preview'),
34
+ hasExplore: text.includes('Explore connections'),
35
+ snippet: text.slice(0, 400),
36
+ };
37
+ });
38
+ console.log('PANEL:', JSON.stringify(result, null, 2));
39
+ found = true;
40
+ break;
41
+ }
42
+ }
43
+ if (found) break;
44
+ }
45
+
46
+ if (!found) console.log('No node found in scan range');
47
+
48
+ await page.waitForTimeout(2000);
49
+ await browser.close();
@@ -1,102 +1,102 @@
1
- import { chromium } from 'playwright';
2
-
3
- const browser = await chromium.launch({ headless: false });
4
- const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
- page.on('console', m => console.log(`[${m.type()}]`, m.text()));
6
- await page.goto('http://localhost:5173');
7
- await page.waitForTimeout(5000);
8
-
9
- const canvas = await page.locator('canvas').boundingBox();
10
- const cx = canvas.x + canvas.width / 2;
11
- const cy = canvas.y + canvas.height / 2;
12
-
13
- // 1. 노드 찾아서 클릭
14
- let nodeX = 0, nodeY = 0;
15
- for (let dx = -250; dx <= 250; dx += 15) {
16
- for (let dy = -200; dy <= 200; dy += 15) {
17
- await page.mouse.move(cx + dx, cy + dy);
18
- await page.waitForTimeout(20);
19
- if (await page.evaluate(() => document.body.style.cursor) === 'pointer') {
20
- nodeX = cx + dx; nodeY = cy + dy;
21
- break;
22
- }
23
- }
24
- if (nodeX) break;
25
- }
26
- console.log('Node at', nodeX - cx, nodeY - cy);
27
-
28
- // 클릭 → 패널 열기
29
- await page.mouse.move(nodeX, nodeY);
30
- await page.waitForTimeout(200);
31
- await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
32
- await page.waitForTimeout(1500);
33
-
34
- const panelOpen = await page.evaluate(() => document.body.innerText.includes('Explore connections'));
35
- console.log('Panel open:', panelOpen);
36
-
37
- if (panelOpen) {
38
- // 2. Explore 클릭
39
- console.log('Clicking Explore...');
40
- const btn = page.locator('button', { hasText: 'Explore connections' });
41
- await btn.click();
42
-
43
- // 3. 3초 동안 상태 모니터링
44
- for (let i = 0; i < 15; i++) {
45
- await page.waitForTimeout(200);
46
- const state = await page.evaluate(() => {
47
- const cv = document.querySelector('canvas');
48
- return {
49
- canvasVisible: cv ? cv.offsetWidth > 0 : false,
50
- bodyText: document.body.innerText.slice(0, 100),
51
- };
52
- });
53
- if (i % 3 === 0) console.log(` tick ${i}: canvas=${state.canvasVisible}`);
54
- }
55
-
56
- // 4. 다른 노드 클릭 시도
57
- console.log('Trying to click another node...');
58
- let found2 = false;
59
- for (let dx = -200; dx <= 200; dx += 20) {
60
- for (let dy = -150; dy <= 150; dy += 20) {
61
- await page.mouse.move(cx + dx, cy + dy);
62
- await page.waitForTimeout(20);
63
- if (await page.evaluate(() => document.body.style.cursor) === 'pointer') {
64
- console.log('Found second node at', dx, dy);
65
- await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
66
- await page.waitForTimeout(1000);
67
-
68
- const afterSecond = await page.evaluate(() => {
69
- const cv = document.querySelector('canvas');
70
- return {
71
- canvasVisible: cv ? cv.offsetWidth > 0 : false,
72
- canvasW: cv?.offsetWidth ?? 0,
73
- bodyText: document.body.innerText.slice(0, 150),
74
- };
75
- });
76
- console.log('After second click:', JSON.stringify(afterSecond));
77
- found2 = true;
78
- break;
79
- }
80
- }
81
- if (found2) break;
82
- }
83
-
84
- // 5. 빈 곳 클릭
85
- console.log('Clicking empty space...');
86
- await page.mouse.move(cx + 300, cy + 250);
87
- await page.waitForTimeout(200);
88
- await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
89
- await page.waitForTimeout(1000);
90
-
91
- const afterEmpty = await page.evaluate(() => {
92
- const cv = document.querySelector('canvas');
93
- return {
94
- canvasVisible: cv ? cv.offsetWidth > 0 : false,
95
- canvasW: cv?.offsetWidth ?? 0,
96
- };
97
- });
98
- console.log('After empty click:', JSON.stringify(afterEmpty));
99
- }
100
-
101
- await page.waitForTimeout(2000);
102
- await browser.close();
1
+ import { chromium } from 'playwright';
2
+
3
+ const browser = await chromium.launch({ headless: false });
4
+ const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
+ page.on('console', m => console.log(`[${m.type()}]`, m.text()));
6
+ await page.goto('http://localhost:5173');
7
+ await page.waitForTimeout(5000);
8
+
9
+ const canvas = await page.locator('canvas').boundingBox();
10
+ const cx = canvas.x + canvas.width / 2;
11
+ const cy = canvas.y + canvas.height / 2;
12
+
13
+ // 1. 노드 찾아서 클릭
14
+ let nodeX = 0, nodeY = 0;
15
+ for (let dx = -250; dx <= 250; dx += 15) {
16
+ for (let dy = -200; dy <= 200; dy += 15) {
17
+ await page.mouse.move(cx + dx, cy + dy);
18
+ await page.waitForTimeout(20);
19
+ if (await page.evaluate(() => document.body.style.cursor) === 'pointer') {
20
+ nodeX = cx + dx; nodeY = cy + dy;
21
+ break;
22
+ }
23
+ }
24
+ if (nodeX) break;
25
+ }
26
+ console.log('Node at', nodeX - cx, nodeY - cy);
27
+
28
+ // 클릭 → 패널 열기
29
+ await page.mouse.move(nodeX, nodeY);
30
+ await page.waitForTimeout(200);
31
+ await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
32
+ await page.waitForTimeout(1500);
33
+
34
+ const panelOpen = await page.evaluate(() => document.body.innerText.includes('Explore connections'));
35
+ console.log('Panel open:', panelOpen);
36
+
37
+ if (panelOpen) {
38
+ // 2. Explore 클릭
39
+ console.log('Clicking Explore...');
40
+ const btn = page.locator('button', { hasText: 'Explore connections' });
41
+ await btn.click();
42
+
43
+ // 3. 3초 동안 상태 모니터링
44
+ for (let i = 0; i < 15; i++) {
45
+ await page.waitForTimeout(200);
46
+ const state = await page.evaluate(() => {
47
+ const cv = document.querySelector('canvas');
48
+ return {
49
+ canvasVisible: cv ? cv.offsetWidth > 0 : false,
50
+ bodyText: document.body.innerText.slice(0, 100),
51
+ };
52
+ });
53
+ if (i % 3 === 0) console.log(` tick ${i}: canvas=${state.canvasVisible}`);
54
+ }
55
+
56
+ // 4. 다른 노드 클릭 시도
57
+ console.log('Trying to click another node...');
58
+ let found2 = false;
59
+ for (let dx = -200; dx <= 200; dx += 20) {
60
+ for (let dy = -150; dy <= 150; dy += 20) {
61
+ await page.mouse.move(cx + dx, cy + dy);
62
+ await page.waitForTimeout(20);
63
+ if (await page.evaluate(() => document.body.style.cursor) === 'pointer') {
64
+ console.log('Found second node at', dx, dy);
65
+ await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
66
+ await page.waitForTimeout(1000);
67
+
68
+ const afterSecond = await page.evaluate(() => {
69
+ const cv = document.querySelector('canvas');
70
+ return {
71
+ canvasVisible: cv ? cv.offsetWidth > 0 : false,
72
+ canvasW: cv?.offsetWidth ?? 0,
73
+ bodyText: document.body.innerText.slice(0, 150),
74
+ };
75
+ });
76
+ console.log('After second click:', JSON.stringify(afterSecond));
77
+ found2 = true;
78
+ break;
79
+ }
80
+ }
81
+ if (found2) break;
82
+ }
83
+
84
+ // 5. 빈 곳 클릭
85
+ console.log('Clicking empty space...');
86
+ await page.mouse.move(cx + 300, cy + 250);
87
+ await page.waitForTimeout(200);
88
+ await page.mouse.down(); await page.waitForTimeout(50); await page.mouse.up();
89
+ await page.waitForTimeout(1000);
90
+
91
+ const afterEmpty = await page.evaluate(() => {
92
+ const cv = document.querySelector('canvas');
93
+ return {
94
+ canvasVisible: cv ? cv.offsetWidth > 0 : false,
95
+ canvasW: cv?.offsetWidth ?? 0,
96
+ };
97
+ });
98
+ console.log('After empty click:', JSON.stringify(afterEmpty));
99
+ }
100
+
101
+ await page.waitForTimeout(2000);
102
+ await browser.close();
@@ -1,61 +1,61 @@
1
- import { chromium } from 'playwright';
2
-
3
- const browser = await chromium.launch({ headless: false });
4
- const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
- await page.goto('http://localhost:5173');
6
- await page.waitForTimeout(5000);
7
-
8
- const canvas = await page.locator('canvas').boundingBox();
9
- const cx = canvas.x + canvas.width / 2;
10
- const cy = canvas.y + canvas.height / 2;
11
-
12
- for (let dx = -250; dx <= 250; dx += 20) {
13
- for (let dy = -200; dy <= 200; dy += 20) {
14
- await page.mouse.move(cx + dx, cy + dy);
15
- await page.waitForTimeout(30);
16
- const cursor = await page.evaluate(() => document.body.style.cursor);
17
- if (cursor === 'pointer') {
18
- console.log(`Node at [${dx}, ${dy}]`);
19
-
20
- // 클릭
21
- await page.mouse.click(cx + dx, cy + dy);
22
- await page.waitForTimeout(1500);
23
-
24
- // 확인
25
- const result = await page.evaluate(() => {
26
- const text = document.body.innerText;
27
- const has380 = !!Array.from(document.querySelectorAll('div')).find(d => d.style.width === '380px');
28
- return {
29
- hasPanel: has380,
30
- hasPreview: text.includes('DOCUMENT PREVIEW') || text.includes('Document Preview'),
31
- hasExplore: text.includes('Explore connections'),
32
- };
33
- });
34
- console.log('Result:', JSON.stringify(result));
35
-
36
- if (result.hasPanel) {
37
- console.log('SUCCESS - Panel is visible!');
38
- await page.screenshot({ path: 'success.png', fullPage: true });
39
- } else {
40
- console.log('FAIL - Panel not found');
41
-
42
- // 디버그: selectedNodeId 확인
43
- const debug = await page.evaluate(() => {
44
- // zustand 내부 상태에 접근할 수 없으므로 DOM 기반으로 확인
45
- return {
46
- allText: document.body.innerText.slice(0, 300),
47
- divCount: document.querySelectorAll('div').length,
48
- };
49
- });
50
- console.log('Debug:', JSON.stringify(debug, null, 2));
51
- }
52
-
53
- await page.waitForTimeout(2000);
54
- await browser.close();
55
- process.exit(0);
56
- }
57
- }
58
- }
59
-
60
- console.log('No node found');
61
- await browser.close();
1
+ import { chromium } from 'playwright';
2
+
3
+ const browser = await chromium.launch({ headless: false });
4
+ const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
+ await page.goto('http://localhost:5173');
6
+ await page.waitForTimeout(5000);
7
+
8
+ const canvas = await page.locator('canvas').boundingBox();
9
+ const cx = canvas.x + canvas.width / 2;
10
+ const cy = canvas.y + canvas.height / 2;
11
+
12
+ for (let dx = -250; dx <= 250; dx += 20) {
13
+ for (let dy = -200; dy <= 200; dy += 20) {
14
+ await page.mouse.move(cx + dx, cy + dy);
15
+ await page.waitForTimeout(30);
16
+ const cursor = await page.evaluate(() => document.body.style.cursor);
17
+ if (cursor === 'pointer') {
18
+ console.log(`Node at [${dx}, ${dy}]`);
19
+
20
+ // 클릭
21
+ await page.mouse.click(cx + dx, cy + dy);
22
+ await page.waitForTimeout(1500);
23
+
24
+ // 확인
25
+ const result = await page.evaluate(() => {
26
+ const text = document.body.innerText;
27
+ const has380 = !!Array.from(document.querySelectorAll('div')).find(d => d.style.width === '380px');
28
+ return {
29
+ hasPanel: has380,
30
+ hasPreview: text.includes('DOCUMENT PREVIEW') || text.includes('Document Preview'),
31
+ hasExplore: text.includes('Explore connections'),
32
+ };
33
+ });
34
+ console.log('Result:', JSON.stringify(result));
35
+
36
+ if (result.hasPanel) {
37
+ console.log('SUCCESS - Panel is visible!');
38
+ await page.screenshot({ path: 'success.png', fullPage: true });
39
+ } else {
40
+ console.log('FAIL - Panel not found');
41
+
42
+ // 디버그: selectedNodeId 확인
43
+ const debug = await page.evaluate(() => {
44
+ // zustand 내부 상태에 접근할 수 없으므로 DOM 기반으로 확인
45
+ return {
46
+ allText: document.body.innerText.slice(0, 300),
47
+ divCount: document.querySelectorAll('div').length,
48
+ };
49
+ });
50
+ console.log('Debug:', JSON.stringify(debug, null, 2));
51
+ }
52
+
53
+ await page.waitForTimeout(2000);
54
+ await browser.close();
55
+ process.exit(0);
56
+ }
57
+ }
58
+ }
59
+
60
+ console.log('No node found');
61
+ await browser.close();
@@ -1,48 +1,48 @@
1
- import { chromium } from 'playwright';
2
-
3
- const browser = await chromium.launch({ headless: false });
4
- const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
- await page.goto('http://localhost:5173');
6
- await page.waitForTimeout(5000);
7
-
8
- const canvas = await page.locator('canvas').boundingBox();
9
- const cx = canvas.x + canvas.width / 2;
10
- const cy = canvas.y + canvas.height / 2;
11
-
12
- // 1. 노드 찾아서 호버만 (클릭 없이)
13
- for (let dx = -250; dx <= 250; dx += 20) {
14
- for (let dy = -200; dy <= 200; dy += 20) {
15
- await page.mouse.move(cx + dx, cy + dy);
16
- await page.waitForTimeout(30);
17
- const cursor = await page.evaluate(() => document.body.style.cursor);
18
- if (cursor === 'pointer') {
19
- console.log(`Node at [${dx}, ${dy}] — hovering (no click)`);
20
- await page.waitForTimeout(1500);
21
-
22
- const result = await page.evaluate(() => {
23
- const text = document.body.innerText;
24
- return {
25
- hasPreview: text.includes('Document Preview') || text.includes('DOCUMENT PREVIEW'),
26
- hasExplore: text.includes('Explore connections'),
27
- snippet: text.slice(0, 300),
28
- };
29
- });
30
- console.log('Hover result:', JSON.stringify(result, null, 2));
31
-
32
- // 2. 마우스를 빈 곳으로 이동 → 패널 사라지는지
33
- await page.mouse.move(cx + 250, cy + 200);
34
- await page.waitForTimeout(800);
35
- const afterLeave = await page.evaluate(() => {
36
- return document.body.innerText.includes('Document Preview');
37
- });
38
- console.log('After leave:', afterLeave ? 'Panel still visible' : 'Panel hidden');
39
-
40
- await page.waitForTimeout(2000);
41
- await browser.close();
42
- process.exit(0);
43
- }
44
- }
45
- }
46
-
47
- console.log('No node found');
48
- await browser.close();
1
+ import { chromium } from 'playwright';
2
+
3
+ const browser = await chromium.launch({ headless: false });
4
+ const page = await browser.newPage({ viewport: { width: 1400, height: 800 } });
5
+ await page.goto('http://localhost:5173');
6
+ await page.waitForTimeout(5000);
7
+
8
+ const canvas = await page.locator('canvas').boundingBox();
9
+ const cx = canvas.x + canvas.width / 2;
10
+ const cy = canvas.y + canvas.height / 2;
11
+
12
+ // 1. 노드 찾아서 호버만 (클릭 없이)
13
+ for (let dx = -250; dx <= 250; dx += 20) {
14
+ for (let dy = -200; dy <= 200; dy += 20) {
15
+ await page.mouse.move(cx + dx, cy + dy);
16
+ await page.waitForTimeout(30);
17
+ const cursor = await page.evaluate(() => document.body.style.cursor);
18
+ if (cursor === 'pointer') {
19
+ console.log(`Node at [${dx}, ${dy}] — hovering (no click)`);
20
+ await page.waitForTimeout(1500);
21
+
22
+ const result = await page.evaluate(() => {
23
+ const text = document.body.innerText;
24
+ return {
25
+ hasPreview: text.includes('Document Preview') || text.includes('DOCUMENT PREVIEW'),
26
+ hasExplore: text.includes('Explore connections'),
27
+ snippet: text.slice(0, 300),
28
+ };
29
+ });
30
+ console.log('Hover result:', JSON.stringify(result, null, 2));
31
+
32
+ // 2. 마우스를 빈 곳으로 이동 → 패널 사라지는지
33
+ await page.mouse.move(cx + 250, cy + 200);
34
+ await page.waitForTimeout(800);
35
+ const afterLeave = await page.evaluate(() => {
36
+ return document.body.innerText.includes('Document Preview');
37
+ });
38
+ console.log('After leave:', afterLeave ? 'Panel still visible' : 'Panel hidden');
39
+
40
+ await page.waitForTimeout(2000);
41
+ await browser.close();
42
+ process.exit(0);
43
+ }
44
+ }
45
+ }
46
+
47
+ console.log('No node found');
48
+ await browser.close();