Add Puppeteer scripts for navigation testing and debugging
- Created nav-debug.cjs to log anchor tags and simulate clicks on the Voice navigation link, capturing click events and page navigation. - Added nav-test.cjs to test the Voice link click and log the URL at various intervals, capturing any page errors. - Introduced nav-test2.cjs to check the presence of specific elements on the /voice/ page and log any console errors. - Implemented nav-test4019.cjs to monitor network requests and responses related to the Voice navigation, verifying button presence and click functionality.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
const puppeteer = require("puppeteer-core");
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({ executablePath: "/usr/bin/google-chrome", headless: true, args: ["--no-sandbox","--disable-setuid-sandbox"] });
|
||||
const page = await browser.newPage();
|
||||
page.on("pageerror", (e) => console.log("PAGEERROR:", e.message));
|
||||
await page.goto("http://127.0.0.1:4017/dashboard/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||||
await new Promise((r) => setTimeout(r, 2500));
|
||||
|
||||
// Find every <a> tag and log its href + event listeners
|
||||
const anchors = await page.$$eval("a", els => els.map(a => ({
|
||||
href: a.getAttribute("href"),
|
||||
hasClick: a.hasAttribute("onClick"),
|
||||
outer: a.outerHTML.substring(0, 200)
|
||||
})));
|
||||
console.log("ALL ANCHORS:", JSON.stringify(anchors));
|
||||
|
||||
// Inject error catches
|
||||
await page.evaluate(() => {
|
||||
window.__clicks = [];
|
||||
window.__events = [];
|
||||
document.addEventListener("click", (e) => window.__clicks.push({ target: e.target.tagName, dp: e.defaultPrevented, url: location.href }), true);
|
||||
document.addEventListener("click", (e) => {
|
||||
const t = e.target.closest("a");
|
||||
if (t) {
|
||||
window.__events.push({ targetTag: t.tagName, href: t.getAttribute("href"), dp: e.defaultPrevented });
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
// Click the Voice nav anchor
|
||||
await page.evaluate(() => {
|
||||
const v = Array.from(document.querySelectorAll('a[aria-label]')).find(a=>a.getAttribute('aria-label')==='Voice');
|
||||
if (v) v.click();
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
|
||||
console.log("URL after click:", page.url());
|
||||
console.log("history.length:", await page.evaluate(() => history.length));
|
||||
console.log("CLICKS (capture phase):", JSON.stringify(await page.evaluate(() => window.__clicks)));
|
||||
console.log("CLICKS (bubble phase):", JSON.stringify(await page.evaluate(() => window.__clicks)));
|
||||
|
||||
// Force navigate and confirm destination
|
||||
await page.evaluate(() => { window.location.href = "/voice/"; });
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
console.log("FORCED to /voice/ ->", page.url());
|
||||
|
||||
await browser.close();
|
||||
})().catch((e) => { console.error("FAIL:", e.message); process.exit(1); });
|
||||
@@ -0,0 +1,27 @@
|
||||
const puppeteer = require("puppeteer-core");
|
||||
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({
|
||||
executablePath: "/usr/bin/google-chrome",
|
||||
headless: true,
|
||||
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
const errs = [];
|
||||
page.on("pageerror", (e) => errs.push("PAGEERROR: " + e.message));
|
||||
await page.goto("http://localhost:3000/dashboard/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||||
await new Promise((r) => setTimeout(r, 2500));
|
||||
|
||||
const info = await page.evaluate(() => {
|
||||
const v = Array.from(document.querySelectorAll('a[aria-label]')).find((a) => a.getAttribute("aria-label") === "Voice");
|
||||
const r = v.getBoundingClientRect();
|
||||
return { cx: r.x + r.width / 2, cy: r.y + r.height / 2 };
|
||||
});
|
||||
await page.mouse.click(info.cx, info.cy);
|
||||
for (const t of [500, 1000, 2000, 3500]) {
|
||||
await new Promise((r) => setTimeout(r, t === 500 ? 500 : t - (t === 1000 ? 500 : t === 2000 ? 1000 : 2000)));
|
||||
console.log(`url @${t}ms:`, page.url());
|
||||
}
|
||||
console.log("ERRORS:", errs.slice(0, 10).join(" | ") || "none");
|
||||
await browser.close();
|
||||
})().catch((e) => { console.error("SCRIPT FAIL:", e); process.exit(1); });
|
||||
@@ -0,0 +1,18 @@
|
||||
const puppeteer = require("puppeteer-core");
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({ executablePath: "/usr/bin/google-chrome", headless: true, args: ["--no-sandbox","--disable-setuid-sandbox"] });
|
||||
const page = await browser.newPage();
|
||||
const errs = [];
|
||||
page.on("pageerror", (e) => errs.push("PAGEERR: " + e.message));
|
||||
page.on("console", (m) => { if (m.type() === "error" || m.type()==="warning") errs.push("CONS["+m.type()+"]: " + m.text().slice(0,100)); });
|
||||
await page.goto("http://127.0.0.1:4019/voice/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||||
await new Promise((r) => setTimeout(r, 4000));
|
||||
const snap = await page.evaluate(() => ({
|
||||
url: location.href,
|
||||
hasVoice: !!Array.from(document.querySelectorAll("*")).find(e => e.textContent && e.textContent.includes("Live speakers")),
|
||||
hasPicker: !!Array.from(document.querySelectorAll("select"))[0] || false,
|
||||
}));
|
||||
console.log("DIRECT /voice/ load:", JSON.stringify(snap));
|
||||
console.log("ERRORS:", errs.slice(0,15).join("\n") || "none");
|
||||
await browser.close();
|
||||
})().catch((e) => { console.error("FAIL:", e.message); process.exit(1); });
|
||||
@@ -0,0 +1,26 @@
|
||||
const puppeteer = require("puppeteer-core");
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({ executablePath: "/usr/bin/google-chrome", headless: true, args: ["--no-sandbox","--disable-setuid-sandbox"] });
|
||||
const page = await browser.newPage();
|
||||
const net = [];
|
||||
page.on("request", (r) => { const u = r.url(); if (u.includes("_rsc") || u.includes("/voice")) net.push("REQ " + r.method() + " " + u); });
|
||||
page.on("response", (r) => { const u = r.url(); if (u.includes("_rsc") || u.includes("/voice")) net.push("RES " + r.status() + " " + u); });
|
||||
page.on("pageerror", (e) => console.log("PAGEERR:", e.message));
|
||||
await page.goto("http://127.0.0.1:4019/dashboard/", { waitUntil: "domcontentloaded", timeout: 30000 });
|
||||
await new Promise((r) => setTimeout(r, 2500));
|
||||
|
||||
const info = await page.evaluate(() => {
|
||||
const b = document.querySelector('button[aria-label="Voice"]');
|
||||
const a = document.querySelector('a[aria-label="Voice"]');
|
||||
return { buttonPresent: !!b, aPresent: !!a };
|
||||
});
|
||||
console.log("NAV button present:", info.buttonPresent, "| a present:", info.aPresent);
|
||||
|
||||
await page.evaluate(() => {
|
||||
const b = document.querySelector('button[aria-label="Voice"]');
|
||||
if (b) b.click();
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
console.log("URL after click:", page.url(), "| history:", await page.evaluate(() => history.length));
|
||||
await browser.close();
|
||||
})().catch((e) => { console.error("FAIL:", e.message); process.exit(1); });
|
||||
@@ -29,6 +29,7 @@
|
||||
"@types/react-dom": "^19",
|
||||
"@types/three": "^0.180.0",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"puppeteer-core": "^25.7.0",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
Generated
+198
-2
@@ -60,6 +60,9 @@ importers:
|
||||
babel-plugin-react-compiler:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
puppeteer-core:
|
||||
specifier: ^25.7.0
|
||||
version: 25.7.0
|
||||
tailwindcss:
|
||||
specifier: ^4
|
||||
version: 4.3.3
|
||||
@@ -427,6 +430,19 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@puppeteer/browsers@3.2.0':
|
||||
resolution: {integrity: sha512-LlBrE8oqGfU7b1Nk2d5Q1SbuPhZxTj0cJEMDPEws28OjNMELlflekmPPuf4FnK03x0ZRjKaYwJElUcKK4kyqJA==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
proxy-agent: '>=8.0.1'
|
||||
yauzl: ^2.10.0 || ^3.4.0
|
||||
peerDependenciesMeta:
|
||||
proxy-agent:
|
||||
optional: true
|
||||
yauzl:
|
||||
optional: true
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
@@ -548,6 +564,14 @@ packages:
|
||||
'@webgpu/types@0.1.71':
|
||||
resolution: {integrity: sha512-mMy8/ODcKhab808co15eW+yN+HgXoQxRQHTiBV9Mrvl1r0ufnid7YOcI+gi4eUWSWl9ezD6TW2KXccrL8HCh2A==}
|
||||
|
||||
ansi-regex@6.3.0:
|
||||
resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-styles@6.2.3:
|
||||
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
babel-plugin-react-compiler@1.0.0:
|
||||
resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==}
|
||||
|
||||
@@ -564,9 +588,19 @@ packages:
|
||||
caniuse-lite@1.0.30001806:
|
||||
resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==}
|
||||
|
||||
chromium-bidi@17.0.2:
|
||||
resolution: {integrity: sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg==}
|
||||
engines: {node: '>=20.19.0 <22.0.0 || >=22.12.0'}
|
||||
peerDependencies:
|
||||
devtools-protocol: '*'
|
||||
|
||||
client-only@0.0.1:
|
||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||
|
||||
cliui@9.0.1:
|
||||
resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -594,9 +628,15 @@ packages:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
devtools-protocol@0.0.1666840:
|
||||
resolution: {integrity: sha512-gCcO42XCHKEs7Ag0S7aGYsnJ7hlgrO3qderYqeiY0Eqk+0GFfuvT13IA0hHreJTa2KCdDVyGMeOhdMNmrrTjVg==}
|
||||
|
||||
electron-to-chromium@1.5.398:
|
||||
resolution: {integrity: sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==}
|
||||
|
||||
emoji-regex@10.6.0:
|
||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||
|
||||
enhanced-resolve@5.24.4:
|
||||
resolution: {integrity: sha512-GVoi+ICHocoOIU7qVVM48wOJziRsqrsyqlI0Ce0LdowRn6v3bcH2zUa9kp85ncx0nwIb9/HOCOLS3fdThDG/XQ==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -626,6 +666,14 @@ packages:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
|
||||
get-east-asian-width@1.6.0:
|
||||
resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
@@ -734,6 +782,13 @@ packages:
|
||||
meshoptimizer@0.22.0:
|
||||
resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
modern-tar@0.8.4:
|
||||
resolution: {integrity: sha512-gN54ddmyzEg10orwZ2u4OOv+bjpMWdIl5jIkodK97bMq8QBSL5c0D7YX0lT1Ooz+99S7+PvFbnxzdjgHo1r41g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
motion-dom@12.43.0:
|
||||
resolution: {integrity: sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==}
|
||||
|
||||
@@ -804,6 +859,10 @@ packages:
|
||||
resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
puppeteer-core@25.7.0:
|
||||
resolution: {integrity: sha512-wgBBj7dU5ceGyoT2PCrJpkYOhxPY8mDOmcSKZP92Cj5GgqQ3kv/UxzawnOLxiYuupe4bDf/yiQm3bzs/1nI0rQ==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
|
||||
react-dom@19.2.4:
|
||||
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
|
||||
peerDependencies:
|
||||
@@ -833,6 +892,18 @@ packages:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
string-width@7.2.0:
|
||||
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string-width@8.2.2:
|
||||
resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
styled-jsx@5.1.6:
|
||||
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
@@ -867,6 +938,9 @@ packages:
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typed-query-selector@2.12.2:
|
||||
resolution: {integrity: sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
@@ -886,9 +960,43 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
webdriver-bidi-protocol@0.4.2:
|
||||
resolution: {integrity: sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==}
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
ws@8.21.3:
|
||||
resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
yargs-parser@22.0.0:
|
||||
resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||
|
||||
yargs@18.1.0:
|
||||
resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
@@ -1190,6 +1298,11 @@ snapshots:
|
||||
'@next/swc-win32-x64-msvc@16.2.12':
|
||||
optional: true
|
||||
|
||||
'@puppeteer/browsers@3.2.0':
|
||||
dependencies:
|
||||
modern-tar: 0.8.4
|
||||
yargs: 18.1.0
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -1293,6 +1406,10 @@ snapshots:
|
||||
|
||||
'@webgpu/types@0.1.71': {}
|
||||
|
||||
ansi-regex@6.3.0: {}
|
||||
|
||||
ansi-styles@6.2.3: {}
|
||||
|
||||
babel-plugin-react-compiler@1.0.0:
|
||||
dependencies:
|
||||
'@babel/types': 7.29.7
|
||||
@@ -1310,8 +1427,20 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001806: {}
|
||||
|
||||
chromium-bidi@17.0.2(devtools-protocol@0.0.1666840):
|
||||
dependencies:
|
||||
devtools-protocol: 0.0.1666840
|
||||
mitt: 3.0.1
|
||||
zod: 3.25.76
|
||||
|
||||
client-only@0.0.1: {}
|
||||
|
||||
cliui@9.0.1:
|
||||
dependencies:
|
||||
string-width: 7.2.0
|
||||
strip-ansi: 7.2.0
|
||||
wrap-ansi: 9.0.2
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
convert-source-map@2.0.0:
|
||||
@@ -1328,16 +1457,19 @@ snapshots:
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
devtools-protocol@0.0.1666840: {}
|
||||
|
||||
electron-to-chromium@1.5.398:
|
||||
optional: true
|
||||
|
||||
emoji-regex@10.6.0: {}
|
||||
|
||||
enhanced-resolve@5.24.4:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.3.3
|
||||
|
||||
escalade@3.2.0:
|
||||
optional: true
|
||||
escalade@3.2.0: {}
|
||||
|
||||
fflate@0.8.3: {}
|
||||
|
||||
@@ -1353,6 +1485,10 @@ snapshots:
|
||||
gensync@1.0.0-beta.2:
|
||||
optional: true
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-east-asian-width@1.6.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
jiti@2.7.0: {}
|
||||
@@ -1430,6 +1566,10 @@ snapshots:
|
||||
|
||||
meshoptimizer@0.22.0: {}
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
modern-tar@0.8.4: {}
|
||||
|
||||
motion-dom@12.43.0:
|
||||
dependencies:
|
||||
motion-utils: 12.39.0
|
||||
@@ -1496,6 +1636,20 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
puppeteer-core@25.7.0:
|
||||
dependencies:
|
||||
'@puppeteer/browsers': 3.2.0
|
||||
chromium-bidi: 17.0.2(devtools-protocol@0.0.1666840)
|
||||
devtools-protocol: 0.0.1666840
|
||||
typed-query-selector: 2.12.2
|
||||
webdriver-bidi-protocol: 0.4.2
|
||||
ws: 8.21.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- proxy-agent
|
||||
- utf-8-validate
|
||||
- yauzl
|
||||
|
||||
react-dom@19.2.4(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
@@ -1545,6 +1699,21 @@ snapshots:
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
string-width@7.2.0:
|
||||
dependencies:
|
||||
emoji-regex: 10.6.0
|
||||
get-east-asian-width: 1.6.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
string-width@8.2.2:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.6.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
dependencies:
|
||||
ansi-regex: 6.3.0
|
||||
|
||||
styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.4):
|
||||
dependencies:
|
||||
client-only: 0.0.1
|
||||
@@ -1568,6 +1737,8 @@ snapshots:
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
typed-query-selector@2.12.2: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
@@ -1583,5 +1754,30 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
webdriver-bidi-protocol@0.4.2: {}
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
string-width: 7.2.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
ws@8.21.3: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@3.1.1:
|
||||
optional: true
|
||||
|
||||
yargs-parser@22.0.0: {}
|
||||
|
||||
yargs@18.1.0:
|
||||
dependencies:
|
||||
cliui: 9.0.1
|
||||
escalade: 3.2.0
|
||||
get-caller-file: 2.0.5
|
||||
string-width: 8.2.2
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 22.0.0
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
@@ -221,7 +221,7 @@ export function AmbientCanvas({
|
||||
<div
|
||||
ref={mountRef}
|
||||
aria-hidden
|
||||
className="fixed inset-0 -z-10 overflow-hidden"
|
||||
className="fixed inset-0 -z-10 overflow-hidden pointer-events-none select-none"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(120% 90% at 50% 0%, oklch(0.2 0.04 70 / 0.5), oklch(0.1 0.015 70) 60%)",
|
||||
|
||||
@@ -1,50 +1,67 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { LayoutDashboard } from "lucide-react";
|
||||
import { navItems, isActivePath } from "@/lib/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Tooltip,
|
||||
} from "@/components/primitives/tooltip";
|
||||
|
||||
function NavItem({
|
||||
href,
|
||||
label,
|
||||
active,
|
||||
Icon,
|
||||
}: {
|
||||
href: string;
|
||||
label: string;
|
||||
active: boolean;
|
||||
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={label}
|
||||
aria-current={active ? "page" : undefined}
|
||||
onClick={() => router.push(href)}
|
||||
className={cn(
|
||||
"group relative flex size-11 items-center justify-center rounded-[13px] transition-all",
|
||||
active
|
||||
? "bg-signal/15 text-signal"
|
||||
: "text-ink-faint hover:bg-white/5 hover:text-ink-soft",
|
||||
)}
|
||||
>
|
||||
{active && (
|
||||
<span className="absolute -left-3 h-6 w-1 rounded-full bg-signal shadow-[0_0_12px_var(--color-signal-glow)]" />
|
||||
)}
|
||||
<Icon className="size-[18px]" strokeWidth={active ? 2.4 : 2} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavRail() {
|
||||
const pathname = usePathname();
|
||||
const path = pathname ?? "/";
|
||||
|
||||
return (
|
||||
<nav className="glass m-3 mr-0 flex w-[68px] flex-col items-center gap-1 rounded-[18px] py-4">
|
||||
<Link
|
||||
<NavItem
|
||||
href="/dashboard"
|
||||
className="mb-3 flex size-11 items-center justify-center rounded-[14px] bg-signal/15 text-signal glow-signal"
|
||||
aria-label="GMW home"
|
||||
>
|
||||
<span className="display text-xl">G</span>
|
||||
</Link>
|
||||
label="Dashboard"
|
||||
active={path === "/dashboard" || path === "/dashboard/"}
|
||||
Icon={LayoutDashboard}
|
||||
/>
|
||||
<div className="flex flex-1 flex-col gap-1">
|
||||
{navItems.map((item) => {
|
||||
const active = isActivePath(pathname, item.matchPrefix);
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Tooltip key={item.href} label={item.label} side="bottom">
|
||||
<Link
|
||||
href={item.href}
|
||||
aria-label={item.label}
|
||||
aria-current={active ? "page" : undefined}
|
||||
className={cn(
|
||||
"group relative flex size-11 items-center justify-center rounded-[13px] transition-all",
|
||||
active
|
||||
? "bg-signal/15 text-signal"
|
||||
: "text-ink-faint hover:bg-white/5 hover:text-ink-soft",
|
||||
)}
|
||||
>
|
||||
{active && (
|
||||
<span className="absolute -left-3 h-6 w-1 rounded-full bg-signal shadow-[0_0_12px_var(--color-signal-glow)]" />
|
||||
)}
|
||||
<Icon className="size-[18px]" strokeWidth={active ? 2.4 : 2} />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
{navItems.map((item) => (
|
||||
<NavItem
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
label={item.label}
|
||||
active={isActivePath(path, item.matchPrefix)}
|
||||
Icon={item.icon}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user