@govtechsg/oobee 0.10.69 → 0.10.72

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.
Files changed (94) hide show
  1. package/DETAILS.md +0 -1
  2. package/README.md +13 -1
  3. package/S3_UPLOAD_README.md +172 -0
  4. package/dev/runGenerateJustHtmlReport.ts +25 -0
  5. package/package.json +4 -2
  6. package/src/combine.ts +71 -14
  7. package/src/constants/common.ts +89 -91
  8. package/src/constants/constants.ts +535 -60
  9. package/src/crawlers/crawlDomain.ts +313 -305
  10. package/src/crawlers/crawlIntelligentSitemap.ts +24 -18
  11. package/src/crawlers/crawlLocalFile.ts +31 -25
  12. package/src/crawlers/crawlSitemap.ts +264 -253
  13. package/src/crawlers/custom/utils.ts +809 -119
  14. package/src/crawlers/guards/urlGuard.ts +77 -0
  15. package/src/crawlers/runCustom.ts +32 -4
  16. package/src/generateHtmlReport.ts +224 -0
  17. package/src/mergeAxeResults.ts +94 -44
  18. package/src/runGenerateJustHtmlReport.ts +20 -0
  19. package/src/services/s3Uploader.ts +184 -0
  20. package/src/static/ejs/partials/components/allIssues/AllIssues.ejs +9 -0
  21. package/src/static/ejs/partials/components/allIssues/CategoryBadges.ejs +82 -0
  22. package/src/static/ejs/partials/components/allIssues/FilterBar.ejs +33 -0
  23. package/src/static/ejs/partials/components/allIssues/IssuesTable.ejs +41 -0
  24. package/src/static/ejs/partials/components/header/SiteInfo.ejs +119 -0
  25. package/src/static/ejs/partials/components/header/aboutScanModal/AboutScanModal.ejs +15 -0
  26. package/src/static/ejs/partials/components/header/aboutScanModal/ScanConfiguration.ejs +44 -0
  27. package/src/static/ejs/partials/components/header/aboutScanModal/ScanDetails.ejs +142 -0
  28. package/src/static/ejs/partials/components/prioritiseIssues/IssueDetailCard.ejs +36 -0
  29. package/src/static/ejs/partials/components/prioritiseIssues/PrioritiseIssues.ejs +47 -0
  30. package/src/static/ejs/partials/components/ruleModal/ruleOffcanvas.ejs +196 -0
  31. package/src/static/ejs/partials/components/scannedPagesSegmentedTabs.ejs +48 -0
  32. package/src/static/ejs/partials/components/shared/InfoAlert.ejs +3 -0
  33. package/src/static/ejs/partials/components/{topFive.ejs → topTen.ejs} +2 -2
  34. package/src/static/ejs/partials/components/wcagCompliance/FailedCriteria.ejs +47 -0
  35. package/src/static/ejs/partials/components/wcagCompliance/WcagCompliance.ejs +16 -0
  36. package/src/static/ejs/partials/components/wcagCompliance/WcagGaugeBar.ejs +16 -0
  37. package/src/static/ejs/partials/components/wcagCoverageDetails.ejs +18 -0
  38. package/src/static/ejs/partials/footer.ejs +1 -1
  39. package/src/static/ejs/partials/header.ejs +7 -223
  40. package/src/static/ejs/partials/main.ejs +12 -23
  41. package/src/static/ejs/partials/scripts/allIssues/AllIssues.ejs +376 -0
  42. package/src/static/ejs/partials/scripts/categorySummary.ejs +1 -1
  43. package/src/static/ejs/partials/scripts/header/SiteInfo.ejs +44 -0
  44. package/src/static/ejs/partials/scripts/header/aboutScanModal/AboutScanModal.ejs +51 -0
  45. package/src/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +127 -0
  46. package/src/static/ejs/partials/scripts/header/aboutScanModal/ScanDetails.ejs +60 -0
  47. package/src/static/ejs/partials/scripts/prioritiseIssues/IssueDetailCard.ejs +137 -0
  48. package/src/static/ejs/partials/scripts/prioritiseIssues/PrioritiseIssues.ejs +214 -0
  49. package/src/static/ejs/partials/scripts/prioritiseIssues/wcagSvgMap.ejs +861 -0
  50. package/src/static/ejs/partials/scripts/ruleModal/constants.ejs +949 -0
  51. package/src/static/ejs/partials/scripts/ruleModal/itemCardRenderer.ejs +352 -0
  52. package/src/static/ejs/partials/scripts/ruleModal/pageAccordionBuilder.ejs +468 -0
  53. package/src/static/ejs/partials/scripts/ruleModal/ruleOffcanvas.ejs +306 -0
  54. package/src/static/ejs/partials/scripts/ruleModal/utilities.ejs +483 -0
  55. package/src/static/ejs/partials/scripts/scannedPagesSegmentedTabs.ejs +35 -0
  56. package/src/static/ejs/partials/scripts/screenshotLightbox.ejs +61 -57
  57. package/src/static/ejs/partials/scripts/topTen.ejs +61 -0
  58. package/src/static/ejs/partials/scripts/utils.ejs +15 -0
  59. package/src/static/ejs/partials/scripts/wcagCompliance/FailedCriteria.ejs +103 -0
  60. package/src/static/ejs/partials/scripts/wcagCompliance/WcagGaugeBar.ejs +47 -0
  61. package/src/static/ejs/partials/scripts/wcagCompliance.ejs +15 -0
  62. package/src/static/ejs/partials/scripts/wcagCoverageDetails.ejs +75 -0
  63. package/src/static/ejs/partials/styles/allIssues/AllIssues.ejs +384 -0
  64. package/src/static/ejs/partials/styles/bootstrap.ejs +17 -1
  65. package/src/static/ejs/partials/styles/header/SiteInfo.ejs +121 -0
  66. package/src/static/ejs/partials/styles/header/aboutScanModal/AboutScanModal.ejs +82 -0
  67. package/src/static/ejs/partials/styles/header/aboutScanModal/ScanConfiguration.ejs +50 -0
  68. package/src/static/ejs/partials/styles/header/aboutScanModal/ScanDetails.ejs +149 -0
  69. package/src/static/ejs/partials/styles/header.ejs +7 -0
  70. package/src/static/ejs/partials/styles/prioritiseIssues/IssueDetailCard.ejs +141 -0
  71. package/src/static/ejs/partials/styles/prioritiseIssues/PrioritiseIssues.ejs +204 -0
  72. package/src/static/ejs/partials/styles/ruleModal/ruleOffcanvas.ejs +456 -0
  73. package/src/static/ejs/partials/styles/scannedPagesSegmentedTabs.ejs +46 -0
  74. package/src/static/ejs/partials/styles/shared/InfoAlert.ejs +12 -0
  75. package/src/static/ejs/partials/styles/styles.ejs +198 -470
  76. package/src/static/ejs/partials/styles/topTenCard.ejs +44 -0
  77. package/src/static/ejs/partials/styles/wcagCompliance/FailedCriteria.ejs +59 -0
  78. package/src/static/ejs/partials/styles/wcagCompliance/WcagGaugeBar.ejs +62 -0
  79. package/src/static/ejs/partials/styles/wcagCompliance.ejs +36 -0
  80. package/src/static/ejs/partials/styles/wcagCoverageDetails.ejs +33 -0
  81. package/src/static/ejs/report.ejs +42 -259
  82. package/src/static/ejs/summary.ejs +1 -1
  83. package/src/utils.ts +30 -0
  84. package/src/static/ejs/partials/components/categorySelector.ejs +0 -4
  85. package/src/static/ejs/partials/components/categorySelectorDropdown.ejs +0 -57
  86. package/src/static/ejs/partials/components/pagesScannedModal.ejs +0 -70
  87. package/src/static/ejs/partials/components/reportSearch.ejs +0 -47
  88. package/src/static/ejs/partials/components/ruleOffcanvas.ejs +0 -105
  89. package/src/static/ejs/partials/components/scanAbout.ejs +0 -328
  90. package/src/static/ejs/partials/components/wcagCompliance.ejs +0 -52
  91. package/src/static/ejs/partials/scripts/categorySelectorDropdownScript.ejs +0 -190
  92. package/src/static/ejs/partials/scripts/reportSearch.ejs +0 -287
  93. package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +0 -804
  94. package/src/static/ejs/partials/scripts/scanAboutScript.ejs +0 -38
@@ -6,7 +6,18 @@ import path from 'path';
6
6
  import { runAxeScript } from '../commonCrawlerFunc.js';
7
7
  import { consoleLogger, guiInfoLog, silentLogger } from '../../logs.js';
8
8
  import { guiInfoStatusTypes } from '../../constants/constants.js';
9
- import { isSkippedUrl } from '../../constants/common.js';
9
+ import { isSkippedUrl, validateCustomFlowLabel } from '../../constants/common.js';
10
+
11
+ declare global {
12
+ interface Window {
13
+ handleOnScanClick?: () => Promise<void> | void;
14
+ handleOnStopClick?: () => Promise<void> | void;
15
+ oobeeSetCollapsed?: (val: boolean) => void;
16
+ oobeeShowStopModal?: () => Promise<{ confirmed: boolean; label: string }>;
17
+ oobeeHideStopModal?: () => void;
18
+ updateMenuPos?: (pos: 'LEFT' | 'RIGHT') => void;
19
+ }
20
+ }
10
21
 
11
22
  //! For Cypress Test
12
23
  // env to check if Cypress test is running
@@ -77,7 +88,6 @@ export const screenshotFullPage = async (page, screenshotsDir: string, screensho
77
88
  window.scrollTo(0, 0);
78
89
  });
79
90
 
80
- consoleLogger.info(`Screenshot page at: ${page.url()}`);
81
91
  consoleLogger.info(`Screenshot page at: ${page.url()}`);
82
92
 
83
93
  await page.screenshot({
@@ -115,9 +125,19 @@ export const runAxeScan = async (
115
125
 
116
126
  await dataset.pushData(result);
117
127
 
128
+ const rawTitle = result.pageTitle ?? '';
129
+ let pageTitleTextOnly = rawTitle; // Note: The original pageTitle contains the index and is being used in top 10 issues
130
+
131
+ if (typeof result.pageIndex === 'number') {
132
+ const re = new RegExp(`^\\s*${result.pageIndex}\\s*:\\s*`);
133
+ pageTitleTextOnly = rawTitle.replace(re, '');
134
+ } else {
135
+ pageTitleTextOnly = rawTitle.replace(/^\s*\d+\s*:\s*/, '');
136
+ }
137
+
118
138
  urlsCrawled.scanned.push({
119
139
  url: page.url(),
120
- pageTitle: result.pageTitle,
140
+ pageTitle: pageTitleTextOnly,
121
141
  pageImagePath: customFlowDetails.pageImagePath,
122
142
  });
123
143
  };
@@ -196,6 +216,10 @@ export const processPage = async (page, processPageParams) => {
196
216
  urlsCrawled,
197
217
  );
198
218
 
219
+ if (includeScreenshots) {
220
+ consoleLogger.info(`Successfully screenshot page at: ${page.url()}`);
221
+ }
222
+
199
223
  guiInfoLog(guiInfoStatusTypes.SCANNED, {
200
224
  numScanned: urlsCrawled.scanned.length,
201
225
  urlScanned: pageUrl,
@@ -210,8 +234,14 @@ export const processPage = async (page, processPageParams) => {
210
234
  };
211
235
 
212
236
  export const MENU_POSITION = {
213
- top: 'TOP',
214
- bottom: 'BOTTOM',
237
+ left: 'LEFT',
238
+ right: 'RIGHT',
239
+ };
240
+
241
+ type OverlayOpts = {
242
+ inProgress?: boolean;
243
+ collapsed?: boolean;
244
+ hideStopInput?: boolean;
215
245
  };
216
246
 
217
247
  export const updateMenu = async (page, urlsCrawled) => {
@@ -232,132 +262,726 @@ export const updateMenu = async (page, urlsCrawled) => {
232
262
  consoleLogger.info(`Overlay menu updated`);
233
263
  };
234
264
 
235
- export const addOverlayMenu = async (page, urlsCrawled, menuPos) => {
265
+
266
+ export const addOverlayMenu = async (
267
+ page,
268
+ urlsCrawled,
269
+ menuPos,
270
+ opts: OverlayOpts = {
271
+ inProgress: false,
272
+ collapsed: false,
273
+ },
274
+ ) => {
236
275
  await page.waitForLoadState('domcontentloaded');
237
276
  consoleLogger.info(`Overlay menu: adding to ${menuPos}...`);
238
- interface CustomWindow extends Window {
239
- updateMenuPos: (newPos: any) => void;
240
- handleOnScanClick: () => void;
241
- }
242
277
 
243
278
  // Add the overlay menu with initial styling
244
279
  return page
245
280
  .evaluate(
246
281
  async vars => {
247
- const menu = document.createElement('div');
248
- menu.className = 'oobee-menu';
249
- if (vars.menuPos === vars.MENU_POSITION.top) {
250
- menu.style.top = '0';
251
- } else {
252
- menu.style.bottom = '0';
253
- }
254
- let isDragging = false;
255
- let initialY;
256
- let offsetY;
257
-
258
- menu.addEventListener('mousedown', e => {
259
- // The EvenTarget Object is the grandfather interface for Element
260
- // In order to get the tagName of the item you would need polymorph it into Element
261
- const targetElement: Element = e.target as Element;
262
- if (targetElement.tagName.toLowerCase() !== 'button') {
263
- e.preventDefault();
264
- isDragging = true;
265
- initialY = e.clientY - menu.getBoundingClientRect().top;
282
+ const customWindow: Window = window as unknown as Window;
283
+ const inProgress = !!(vars?.opts && vars.opts.inProgress);
284
+ const collapsedOption = !!(vars?.opts && vars.opts.collapsed);
285
+
286
+ const panel = document.createElement('aside');
287
+ panel.className = 'oobee-panel';
288
+
289
+ const minBtn = document.createElement('button');
290
+ minBtn.type = 'button';
291
+ minBtn.className = 'oobee-minbtn';
292
+ minBtn.setAttribute('aria-label', 'Minimize/expand panel');
293
+
294
+ const MINBTN_SVG = `
295
+ <svg class="oobee-minbtn__icon" xmlns="http://www.w3.org/2000/svg"
296
+ width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
297
+ <g clip-path="url(#clip0_59_3691)">
298
+ <path d="M6.41 6L5 7.41L9.58 12L5 16.59L6.41 18L12.41 12L6.41 6Z" fill="#9021A6"/>
299
+ <path d="M14.41 6L13 7.41L17.58 12L13 16.59L14.41 18L20.41 12L14.41 6Z" fill="#9021A6"/>
300
+ </g>
301
+ <defs>
302
+ <clipPath id="clip0_59_3691">
303
+ <rect width="24" height="24" fill="white"/>
304
+ </clipPath>
305
+ </defs>
306
+ </svg>
307
+ `;
308
+ minBtn.innerHTML = MINBTN_SVG;
309
+
310
+ let currentPos: 'LEFT' | 'RIGHT' = (vars.menuPos || 'RIGHT');
311
+ const isCollapsed = () => panel.classList.contains('collapsed');
312
+
313
+ const setPosClass = (pos: 'LEFT' | 'RIGHT') => {
314
+ panel.classList.remove('pos-left', 'pos-right');
315
+ minBtn.classList.remove('pos-left', 'pos-right');
316
+ if (pos === 'LEFT') {
317
+ panel.classList.add('pos-left');
318
+ minBtn.classList.add('pos-left');
319
+ } else {
320
+ panel.classList.add('pos-right');
321
+ minBtn.classList.add('pos-right');
266
322
  }
267
- });
268
-
269
- document.addEventListener('mousemove', e => {
270
- if (isDragging) {
271
- menu.style.removeProperty('bottom');
272
- offsetY = e.clientY - initialY;
273
- menu.style.top = `${offsetY}px`;
323
+ positionMinimizeBtn();
324
+ setDraggableSidebarMenu();
325
+ };
326
+
327
+ const toggleCollapsed = (force?: boolean) => {
328
+ const willCollapse = (typeof force === 'boolean') ? force : !isCollapsed();
329
+ if (willCollapse) {
330
+ panel.classList.add('collapsed');
331
+ localStorage.setItem('oobee:overlay-collapsed', '1');
332
+ customWindow.oobeeSetCollapsed?.(true);
333
+ } else {
334
+ panel.classList.remove('collapsed');
335
+ localStorage.setItem('oobee:overlay-collapsed', '0');
336
+ customWindow.oobeeSetCollapsed?.(false);
274
337
  }
275
- });
276
- const customWindow = window as unknown as CustomWindow;
277
-
278
- document.addEventListener('mouseup', () => {
279
- // need to tell typeScript to defer first because down int he script updateMenuPos is defined
280
- if (isDragging) {
281
- // Snap the menu when it is below half the screen
282
- const halfScreenHeight: number = window.innerHeight / 2;
283
- const isTopHalf: boolean = offsetY < halfScreenHeight;
284
- if (isTopHalf) {
285
- menu.style.removeProperty('bottom');
286
- menu.style.top = '0';
287
- customWindow.updateMenuPos(vars.MENU_POSITION.top);
288
- } else {
289
- menu.style.removeProperty('top');
290
- menu.style.bottom = '0';
291
- customWindow.updateMenuPos(vars.MENU_POSITION.top);
292
- }
293
-
294
- isDragging = false;
338
+ positionMinimizeBtn();
339
+ setDraggableSidebarMenu();
340
+ };
341
+
342
+ setPosClass(currentPos);
343
+ const persisted = localStorage.getItem('oobee:overlay-collapsed');
344
+ const startCollapsed = persisted != null ? persisted === '1' : collapsedOption;
345
+ if (startCollapsed) panel.classList.add('collapsed');
346
+
347
+ const header = document.createElement('div');
348
+ header.className = 'oobee-header';
349
+
350
+ const grip = document.createElement('button');
351
+ grip.type = 'button';
352
+ grip.className = 'oobee-grip';
353
+ grip.setAttribute('aria-label', 'Drag to move panel left or right');
354
+
355
+ const GRIP_SVG = `
356
+ <svg class="oobee-grip__icon" xmlns="http://www.w3.org/2000/svg"
357
+ width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
358
+ <path d="M6 11C4.9 11 4 10.1 4 9C4 7.9 4.9 7 6 7C7.1 7 8 7.9 8 9C8 10.1 7.1 11 6 11ZM14 9C14 7.9 13.1 7 12 7C10.9 7 10 7.9 10 9C10 10.1 10.9 11 12 11C13.1 11 14 10.1 14 9ZM20 9C20 7.9 19.1 7 18 7C16.9 7 16 7.9 16 9C16 10.1 16.9 11 18 11C19.1 11 20 10.1 20 9ZM16 15C16 16.1 16.9 17 18 17C19.1 17 20 16.1 20 15C20 13.9 19.1 13 18 13C16.9 13 16 13.9 16 15ZM14 15C14 13.9 13.1 13 12 13C10.9 13 10 13.9 10 15C10 16.1 10.9 17 12 17C13.1 17 14 16.1 14 15ZM8 15C8 13.9 7.1 13 6 13C4.9 13 4 13.9 4 15C4 16.1 4.9 17 6 17C7.1 17 8 16.1 8 15Z" fill="#AFAFB0"/>
359
+ </svg>
360
+ `;
361
+ grip.innerHTML = GRIP_SVG;
362
+
363
+ const leftSpacer = document.createElement('div');
364
+ leftSpacer.className = 'oobee-spacer';
365
+ const rightSpacer = document.createElement('div');
366
+ rightSpacer.className = 'oobee-spacer';
367
+
368
+ header.appendChild(leftSpacer);
369
+ header.appendChild(grip);
370
+ header.appendChild(rightSpacer);
371
+
372
+ const body = document.createElement('div');
373
+ body.className = 'oobee-body';
374
+
375
+ const h2 = document.createElement('h2');
376
+ h2.id = 'oobeeHPagesScanned';
377
+ h2.className = 'oobee-section-title';
378
+ h2.textContent = 'Pages Scanned';
379
+
380
+ const scanBtn = document.createElement('button');
381
+ scanBtn.id = 'oobeeBtnScan';
382
+ scanBtn.className = 'oobee-btn oobee-btn-primary';
383
+ scanBtn.innerText = 'Scan this page';
384
+ scanBtn.disabled = inProgress;
385
+ scanBtn.addEventListener('click', async () => customWindow.handleOnScanClick?.());
386
+
387
+ const stopBtn = document.createElement('button');
388
+ stopBtn.id = 'oobeeBtnStop';
389
+ stopBtn.className = 'oobee-btn oobee-btn-secondary';
390
+ stopBtn.innerText = 'Stop scan';
391
+ stopBtn.addEventListener('click', async () => customWindow.handleOnStopClick?.());
392
+
393
+ const btnGroup = document.createElement('div');
394
+ btnGroup.className = 'oobee-actions';
395
+ btnGroup.appendChild(scanBtn);
396
+ btnGroup.appendChild(stopBtn);
397
+
398
+ const listWrap = document.createElement('div');
399
+ listWrap.id = 'oobeeList';
400
+ listWrap.className = 'oobee-list';
401
+
402
+ const renderList = () => {
403
+ const scanned = vars.urlsCrawled.scanned || [];
404
+ listWrap.innerHTML = '';
405
+
406
+ if (scanned.length === 0) {
407
+ const empty = document.createElement('div');
408
+ empty.className = 'oobee-empty';
409
+ empty.textContent = 'Scan a page to start';
410
+ listWrap.appendChild(empty);
411
+ return;
295
412
  }
296
- });
297
413
 
298
- const p = document.createElement('p');
299
- p.id = 'oobee-p-pages-scanned';
300
- p.innerText = `Pages Scanned: ${vars.urlsCrawled.scanned.length || 0}`;
414
+ const ol = document.createElement('ol');
415
+ ol.className = 'oobee-ol';
301
416
 
302
- const button = document.createElement('button');
303
- button.innerText = 'Scan this page';
304
- button.addEventListener('click', async () => {
305
- customWindow.handleOnScanClick();
306
- });
417
+ scanned.forEach((item) => {
418
+ const li = document.createElement('li');
419
+ li.className = 'oobee-li';
420
+
421
+ const title = document.createElement('div');
422
+ title.className = 'oobee-item-title';
423
+ title.textContent = (item.pageTitle && item.pageTitle.trim()) ? item.pageTitle : item.url;
424
+
425
+ const url = document.createElement('div');
426
+ url.className = 'oobee-item-url';
427
+ url.textContent = item.url;
428
+
429
+ li.appendChild(title);
430
+ li.appendChild(url);
431
+ ol.appendChild(li);
432
+ });
433
+
434
+ listWrap.appendChild(ol);
435
+ };
436
+ renderList();
307
437
 
308
- menu.appendChild(p);
309
- menu.appendChild(button);
438
+ body.appendChild(btnGroup);
439
+ body.appendChild(h2);
440
+ body.appendChild(listWrap);
441
+
442
+ panel.appendChild(header);
443
+ panel.appendChild(body);
310
444
 
311
445
  const sheet = new CSSStyleSheet();
312
446
  // TODO: separate out into css file if this gets too big
313
447
  sheet.replaceSync(`
314
- .oobee-menu {
315
- position: fixed;
316
- left: 0;
317
- width: 100%;
318
- box-sizing: border-box;
319
- background-color: rgba(0, 0, 0, 0.8);
320
- display: flex;
321
- justify-content: space-between;
322
- padding: 10px;
323
- z-index: 2147483647;
324
- cursor: grab;
325
- color: #fff;
326
- }
327
-
328
- .oobee-menu button {
329
- background-color: #9021a6;
330
- color: #fff;
331
- border: none;
332
- border-radius: 50rem;
333
- padding: 10px 20px;
334
- cursor: pointer;
335
- }
448
+ .oobee-panel{
449
+ position: fixed;
450
+ top: 0;
451
+ height: 100vh;
452
+ width: 320px;
453
+ box-sizing: border-box;
454
+ background: #fff;
455
+ color: #111;
456
+ z-index: 2147483647;
457
+ display: flex;
458
+ flex-direction: column;
459
+ border: 1px solid rgba(0,0,0,.08);border-left: none;border-right: none;
460
+ box-shadow: 0 6px 24px rgba(0,0,0,.08);
461
+ transition: width .16s ease,left .16s ease,right .16s ease
462
+ }
463
+ .oobee-panel.pos-right {
464
+ right: 0;
465
+ border-left: 1px solid rgba(0,0,0,.08)
466
+ }
467
+ .oobee-panel.pos-left {
468
+ left: 0;
469
+ border-right: 1px solid rgba(0,0,0,.08)
470
+ }
471
+ .oobee-panel.collapsed {
472
+ width: 56px;
473
+ overflow: hidden
474
+ }
475
+
476
+ :host {
477
+ --oobee-gap: 8px; /* distance from panel edge */
478
+ --oobee-panel-offset: 320px; /* overwritten by JS to actual width */
479
+ }
480
+
481
+ /* external minimize button (always OUTSIDE the panel) */
482
+ .oobee-minbtn {
483
+ position: fixed;
484
+ top: 0;
485
+ z-index: 2147483647;
486
+ width: 32px;
487
+ height: 32px;
488
+ border: none;
489
+ background: #fff;
490
+ cursor: pointer;
491
+ }
492
+
493
+ /* right-docked: button sits to the LEFT of the panel */
494
+ .oobee-minbtn.pos-right{
495
+ right: calc(var(--oobee-panel-offset) + var(--oobee-gap));
496
+ }
497
+ /* left-docked: button sits to the RIGHT of the panel */
498
+ .oobee-minbtn.pos-left{
499
+ left: calc(var(--oobee-panel-offset) + var(--oobee-gap));
500
+ }
501
+ .oobee-minbtn:hover {
502
+ box-shadow:0 4px 12px rgba(0,0,0,.12);
503
+ }
504
+ .oobee-minbtn:active {
505
+ transform:translateY(1px);
506
+ }
507
+ .oobee-minbtn:focus-visible {
508
+ outline: 2px solid #7b4dff;
509
+ outline-offset: 2px;
510
+ }
511
+
512
+ .oobee-header {
513
+ position: relative;
514
+ display: flex;
515
+ align-items: center;
516
+ justify-content: space-between;
517
+ }
518
+
519
+ .oobee-spacer {
520
+ width:28px;
521
+ height:28px;
522
+ }
523
+
524
+ .oobee-grip{
525
+ border: 0;
526
+ background: #FFFFFF;
527
+ cursor: grab;
528
+ margin-top: 0.4rem;
529
+ }
530
+ .oobee-grip:active {
531
+ cursor:grabbing;
532
+ }
533
+
534
+ .oobee-body {
535
+ display: flex;
536
+ flex-direction: column;
537
+ flex: 1;
538
+ min-height: 0;
539
+ overflow: hidden;
540
+ }
541
+
542
+ .oobee-actions {
543
+ display: flex;
544
+ flex-direction: column;
545
+ gap: 12px;
546
+ padding: 1rem;
547
+ }
548
+
549
+ /* Base button */
550
+ .oobee-btn {
551
+ width: 100%;
552
+ min-height: 44px;
553
+ border-radius: 999px;
554
+ padding: 12px 16px;
555
+ font-size: 16px;
556
+ line-height: 1.2;
557
+ font-weight: 400;
558
+ cursor: pointer;
559
+ transition: {
560
+ box-shadow .12s ease,
561
+ transform .02s ease,
562
+ background-color .12s ease,
563
+ color .12s ease,
564
+ border-color .12s ease;
565
+ }
566
+ }
567
+ .oobee-btn:disabled {
568
+ opacity:.6;
569
+ cursor:not-allowed
570
+ }
571
+
572
+ /* Primary (filled) */
573
+ .oobee-btn-primary {
574
+ background: #9021a6;
575
+ color: #fff;
576
+ border: 1px solid transparent;
577
+ }
578
+ .oobee-btn-primary:hover:not(:disabled) {
579
+ box-shadow:0 2px 10px rgba(0,0,0,.12);
580
+ }
581
+ .oobee-btn-primary:active:not(:disabled) {
582
+ transform:translateY(1px);
583
+ }
584
+ .oobee-btn-primary:focus-visible {
585
+ outline:2px solid #7b4dff;
586
+ outline-offset:2px;
587
+ }
588
+
589
+ /* Stop button */
590
+ .oobee-btn-secondary{
591
+ background: #fff;
592
+ color: #9021A6;
593
+ border: 1px solid #9021A6;
594
+ }
595
+ .oobee-btn-secondary:active:not(:disabled) {
596
+ transform:translateY(1px);
597
+ }
598
+ .oobee-btn-secondary:focus-visible{
599
+ outline: 2px solid #7b4dff;
600
+ outline-offset:2px;
601
+ }
602
+
603
+ /* Text for empty scans */
604
+ .oobee-empty{
605
+ display: flex;
606
+ justify-content: center;
607
+ align-items: center;
608
+ height: 100%;
609
+ font-size: 14px;
610
+ color: #555555;
611
+ }
612
+
613
+ .oobee-list {
614
+ flex: 1;
615
+ min-height: 0;
616
+ overflow-y: auto;
617
+ padding-left: 1rem;
618
+ padding-right: 1rem;
619
+ padding-bottom: 1rem;
620
+ padding-top: 0;
621
+ }
622
+
623
+ .oobee-panel.collapsed .oobee-list {
624
+ display: none;
625
+ }
626
+
627
+ #oobeeStopOverlay[hidden] {
628
+ display:none !important;
629
+ }
630
+ #oobeeStopOverlay {
631
+ display:grid;
632
+ }
633
+
634
+ .oobee-section-title {
635
+ font-size: 16px;
636
+ font-weight: 700;
637
+ color: #161616;
638
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
639
+ padding: 1rem;
640
+ margin: 0;
641
+ }
642
+
643
+ .oobee-panel.collapsed .oobee-section-title {
644
+ display: none;
645
+ }
646
+
647
+ .oobee-ol {
648
+ margin: 0;
649
+ padding-left: 1.25rem;
650
+ display: flex;
651
+ flex-direction: column;
652
+ gap: 10px;
653
+ }
654
+
655
+ .oobee-li {
656
+ list-style: decimal;
657
+ font-size: 14px;
658
+ }
659
+
660
+ .oobee-item-title {
661
+ font-size: 14px;
662
+ color: #161616;
663
+ white-space: nowrap;
664
+ overflow: hidden;
665
+ text-overflow: ellipsis;
666
+ }
667
+
668
+ .oobee-item-url {
669
+ font-size: 12px;
670
+ color: #6b7280;
671
+ white-space: nowrap;
672
+ overflow: hidden;
673
+ text-overflow: ellipsis;
674
+ direction: rtl;
675
+ text-align: left;
676
+ }
677
+
678
+ .oobee-minbtn__icon {
679
+ transition: transform .18s ease;
680
+ transform: rotate(0deg);
681
+ }
682
+ .oobee-minbtn__icon.is-left {
683
+ transform: rotate(180deg);
684
+ }
685
+
686
+ :host-context(.oobee-snap) .oobee-panel,
687
+ :host-context(.oobee-snap) .oobee-minbtn { display:none !important; }
688
+
689
+ @media (max-width:1024px) {
690
+ .oobee-panel {
691
+ width:280px
692
+ }
693
+ }
694
+ @media (max-width:768px) {
695
+ .oobee-panel {
696
+ width: 92vw;
697
+ height: 100vh;
698
+ top: 0;
699
+ bottom: 0;
700
+ border-radius: 0;
701
+ }
702
+ .oobee-panel.collapsed {
703
+ width: auto;
704
+ height: auto;
705
+ padding: 0;
706
+ display: flex;
707
+ align-items: center;
708
+ justify-content: center;
709
+ border-radius: 999px;
710
+ box-shadow: 0 6px 24px rgba(0,0,0,.4);
711
+ top: auto;
712
+ bottom: max(16px, env(safe-area-inset-bottom,0px))
713
+ }
714
+ }
336
715
  `);
337
716
 
338
- // shadow dom used to avoid styling from page
717
+ document.documentElement.classList.remove('oobee-snap');
339
718
  const shadowHost = document.createElement('div');
340
- shadowHost.id = 'oobee-shadow-host';
719
+ shadowHost.id = 'oobeeShadowHost';
341
720
  const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
342
721
 
343
722
  shadowRoot.adoptedStyleSheets = [sheet];
344
723
 
345
- shadowRoot.appendChild(menu);
724
+ shadowRoot.appendChild(panel);
725
+ shadowRoot.appendChild(minBtn);
346
726
 
347
- let currentNode = document.body;
348
- if (document.body) {
349
- // The <body> element exists
350
- if (document.body.nodeName.toLowerCase() === 'frameset') {
351
- // if currentNode is a <frameset>
352
- // Move the variable outside the frameset then appendChild the component
353
- while (currentNode.nodeName.toLowerCase() === 'frameset') {
354
- currentNode = currentNode.parentElement;
355
- }
356
- currentNode.appendChild(shadowHost);
727
+ function setDraggableSidebarMenu() {
728
+ const icon = minBtn.querySelector<SVGElement>('.oobee-minbtn__icon');
729
+ if (!icon) return;
730
+
731
+ const closed = isCollapsed();
732
+ const arrowPointsRight =
733
+ (currentPos === 'RIGHT' && !closed) ||
734
+ (currentPos === 'LEFT' && closed);
735
+
736
+ icon.classList.toggle('is-left', !arrowPointsRight);
737
+ minBtn.setAttribute('aria-label', closed ? 'Expand panel' : 'Collapse panel');
738
+ }
739
+
740
+ function positionMinimizeBtn() {
741
+ const OPEN_OFFSET = 318;
742
+ const COLLAPSED_OFFSET = 55;
743
+ const offset = isCollapsed() ? COLLAPSED_OFFSET : OPEN_OFFSET;
744
+
745
+ minBtn.style.left = '';
746
+ minBtn.style.right = '';
747
+
748
+ if (currentPos === 'RIGHT') {
749
+ minBtn.style.right = `${offset}px`;
357
750
  } else {
358
- // currentNode is a <body>
359
- currentNode.appendChild(shadowHost);
751
+ minBtn.style.left = `${offset}px`;
752
+ }
753
+ }
754
+ positionMinimizeBtn();
755
+ setDraggableSidebarMenu();
756
+
757
+ minBtn.addEventListener('click', () => toggleCollapsed());
758
+
759
+ let startX = 0;
760
+ const THRESH = 40;
761
+
762
+ grip.addEventListener('pointerdown', (e: PointerEvent) => {
763
+ startX = e.clientX;
764
+ grip.setPointerCapture(e.pointerId); // <-- use the button
765
+ });
766
+
767
+ grip.addEventListener('pointermove', (e: PointerEvent) => {
768
+ if (!grip.hasPointerCapture?.(e.pointerId)) return; // <-- check the button
769
+ const dx = e.clientX - startX;
770
+ if (Math.abs(dx) >= THRESH) {
771
+ const nextPos: 'LEFT' | 'RIGHT' = dx < 0 ? 'LEFT' : 'RIGHT';
772
+ if (nextPos !== currentPos) {
773
+ currentPos = nextPos;
774
+ setPosClass(currentPos);
775
+ window.updateMenuPos?.(currentPos);
776
+ }
777
+ startX = e.clientX;
778
+ }
779
+ });
780
+
781
+ grip.addEventListener('pointerup', (e: PointerEvent) => {
782
+ try { grip.releasePointerCapture(e.pointerId); } catch {}
783
+ });
784
+
785
+ const stopDialog = document.createElement('dialog');
786
+ stopDialog.id = 'oobeeStopDialog';
787
+ Object.assign(stopDialog.style, {
788
+ width: 'min(560px, calc(100vw - 32px))',
789
+ border: 'none',
790
+ padding: '0',
791
+ borderRadius: '16px',
792
+ overflow: 'hidden',
793
+ boxShadow: '0 10px 40px rgba(0,0,0,.35)',
794
+ fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif'
795
+ });
796
+ const dialogSheet = new CSSStyleSheet();
797
+ dialogSheet.replaceSync(`
798
+ #oobeeStopDialog::backdrop {
799
+ background: rgba(0,0,0,.55);
800
+ }
801
+
802
+ /* primary button hover/focus */
803
+ .oobee-stop-primary:hover {
804
+ filter: brightness(0.95);
805
+ }
806
+ .oobee-stop-primary:focus-visible {
807
+ outline: 2px solid #7b4dff; outline-offset: 2px;
808
+ }
809
+
810
+ /* cancel link hover */
811
+ .oobee-stop-cancel {
812
+ color: #9021A6;
813
+ text-decoration: underline;
814
+ }
815
+ .oobee-stop-cancel:hover {
816
+ filter: brightness(0.95);
817
+ }
818
+
819
+ /* close “X” hover ring */
820
+ .oobee-stop-close:hover {
821
+ background: #f3f4f6;
822
+ }
823
+ `);
824
+ shadowRoot.adoptedStyleSheets = [sheet, dialogSheet];
825
+
826
+ const head = document.createElement('div');
827
+ Object.assign(head.style, {
828
+ padding: '20px 20px 8px 20px',
829
+ display: 'flex',
830
+ alignItems: 'center',
831
+ justifyContent: 'space-between',
832
+ gap: '8px'
833
+ });
834
+
835
+ const title = document.createElement('h2');
836
+ title.id = 'oobee-stop-title';
837
+ title.textContent = 'Are you sure you want to stop this scan?';
838
+ Object.assign(title.style, { margin: '0', fontSize: '22px', fontWeight: '700', lineHeight: '1.25' });
839
+
840
+ const closeX = document.createElement('button');
841
+ closeX.type = 'button';
842
+ closeX.setAttribute('aria-label', 'Close');
843
+ closeX.textContent = '×';
844
+ closeX.className = 'oobee-stop-close';
845
+ Object.assign(closeX.style, {
846
+ border: 'none',
847
+ background: 'transparent',
848
+ fontSize: '28px',
849
+ lineHeight: '1',
850
+ cursor: 'pointer',
851
+ color: '#4b5563',
852
+ width: '36px',
853
+ height: '36px',
854
+ borderRadius: '12px',
855
+ display: 'grid',
856
+ placeItems: 'center'
857
+ });
858
+ head.appendChild(title);
859
+ head.appendChild(closeX);
860
+
861
+ const bodyWrap = document.createElement('div');
862
+ Object.assign(bodyWrap.style, {
863
+ padding: '12px 20px 20px 20px'
864
+ });
865
+
866
+ const form = document.createElement('form');
867
+ form.noValidate = true;
868
+ form.autocomplete = 'off';
869
+ Object.assign(form.style, {
870
+ display: 'grid',
871
+ gridTemplateColumns: '1fr',
872
+ rowGap: '12px'
873
+ });
874
+
875
+ const label = document.createElement('label');
876
+ label.setAttribute('for', 'oobee-stop-input');
877
+ label.textContent = 'Enter a name for this scan';
878
+ Object.assign(label.style, { fontSize: '15px', fontWeight: '600' });
879
+
880
+ const input = document.createElement('input');
881
+ input.id = 'oobeeStopInput';
882
+ input.type = 'text';
883
+ Object.assign(input.style, {
884
+ width: '100%',
885
+ borderRadius: '5px',
886
+ border: '1px solid #e5e7eb',
887
+ padding: '12px 14px',
888
+ fontSize: '14px',
889
+ outline: 'none',
890
+ boxSizing: 'border-box'
891
+ });
892
+ input.addEventListener('focus', () => {
893
+ input.style.borderColor = '#7b4dff';
894
+ input.style.boxShadow = '0 0 0 3px rgba(123,77,255,.25)';
895
+ });
896
+ input.addEventListener('blur', () => {
897
+ input.style.borderColor = '#e5e7eb';
898
+ input.style.boxShadow = 'none';
899
+ });
900
+
901
+ const actions = document.createElement('div');
902
+ Object.assign(actions.style, { display: 'grid', gap: '12px', marginTop: '4px' });
903
+
904
+ const primary = document.createElement('button');
905
+ primary.type = 'submit';
906
+ primary.textContent = 'Stop scan';
907
+ primary.className = 'oobee-stop-primary';
908
+ Object.assign(primary.style, {
909
+ border: 'none',
910
+ borderRadius: '999px',
911
+ padding: '12px 16px',
912
+ fontSize: '15px',
913
+ fontWeight: '600',
914
+ color: '#fff',
915
+ background: '#9021A6',
916
+ cursor: 'pointer'
917
+ });
918
+
919
+ const cancel = document.createElement('button');
920
+ cancel.type = 'button';
921
+ cancel.textContent = 'No, continue scan';
922
+ cancel.className = 'oobee-stop-cancel';
923
+ Object.assign(cancel.style, {
924
+ border: 'none',
925
+ background: 'transparent',
926
+ fontSize: '14px',
927
+ justifySelf: 'center',
928
+ cursor: 'pointer',
929
+ padding: '6px'
930
+ });
931
+
932
+ actions.appendChild(primary);
933
+ actions.appendChild(cancel);
934
+ const shouldHideInput = !!(vars?.opts && vars.opts.hideStopInput);
935
+ if (!shouldHideInput) {
936
+ form.appendChild(label);
937
+ form.appendChild(input);
938
+ }
939
+ form.appendChild(actions);
940
+ bodyWrap.appendChild(form);
941
+
942
+ stopDialog.appendChild(head);
943
+ stopDialog.appendChild(bodyWrap);
944
+ shadowRoot.appendChild(stopDialog);
945
+
946
+ let stopResolver: null | ((v: { confirmed: boolean; label: string }) => void) = null;
947
+ const hideStop = () => { try { stopDialog.close(); } catch {} stopResolver = null; };
948
+ const showStop = () => {
949
+ if (!shouldHideInput) input.value = '';
950
+ try { stopDialog.showModal(); } catch {}
951
+ if (!shouldHideInput) {
952
+ requestAnimationFrame(() => {
953
+ try { input.focus({ preventScroll: true }); input.select(); } catch {}
954
+ });
360
955
  }
956
+ };
957
+ form.addEventListener('submit', (e) => {
958
+ e.preventDefault();
959
+ const v = (input.value || '').trim();
960
+ if (stopResolver) stopResolver({ confirmed: true, label: v });
961
+ hideStop();
962
+ });
963
+ closeX.addEventListener('click', () => {
964
+ if (stopResolver) stopResolver({ confirmed: false, label: '' });
965
+ hideStop();
966
+ });
967
+ cancel.addEventListener('click', () => {
968
+ if (stopResolver) stopResolver({ confirmed: false, label: '' });
969
+ hideStop();
970
+ });
971
+ stopDialog.addEventListener('cancel', (e) => {
972
+ e.preventDefault();
973
+ if (stopResolver) stopResolver({ confirmed: false, label: '' });
974
+ hideStop();
975
+ });
976
+ (customWindow as Window).oobeeShowStopModal = () =>
977
+ new Promise<{ confirmed: boolean; label: string }>((resolve) => {
978
+ stopResolver = resolve;
979
+ showStop();
980
+ });
981
+ (customWindow as Window).oobeeHideStopModal = hideStop;
982
+
983
+ if (document.body) {
984
+ document.body.appendChild(shadowHost);
361
985
  } else if (document.head) {
362
986
  // The <head> element exists
363
987
  // Append the variable below the head
@@ -367,8 +991,10 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos) => {
367
991
  // Append the variable to the document
368
992
  document.documentElement.appendChild(shadowHost);
369
993
  }
994
+ positionMinimizeBtn();
995
+ setDraggableSidebarMenu();
370
996
  },
371
- { menuPos, MENU_POSITION, urlsCrawled },
997
+ { menuPos, MENU_POSITION, urlsCrawled, opts },
372
998
  )
373
999
  .then(() => {
374
1000
  log('Overlay menu: successfully added');
@@ -381,7 +1007,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos) => {
381
1007
  export const removeOverlayMenu = async page => {
382
1008
  await page
383
1009
  .evaluate(() => {
384
- const existingOverlay = document.querySelector('#oobee-shadow-host');
1010
+ const existingOverlay = document.querySelector('#oobeeShadowHost');
385
1011
  if (existingOverlay) {
386
1012
  existingOverlay.remove();
387
1013
  return true;
@@ -396,7 +1022,7 @@ export const removeOverlayMenu = async page => {
396
1022
  };
397
1023
 
398
1024
  export const initNewPage = async (page, pageClosePromises, processPageParams, pagesDict) => {
399
- let menuPos = MENU_POSITION.top;
1025
+ let menuPos = MENU_POSITION.right;
400
1026
 
401
1027
  // eslint-disable-next-line no-underscore-dangle
402
1028
  const pageId = page._guid;
@@ -413,42 +1039,106 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
413
1039
  pageClosePromises.push(pageClosePromise);
414
1040
 
415
1041
  if (!pagesDict[pageId]) {
416
- pagesDict[pageId] = { page };
1042
+ pagesDict[pageId] = {
1043
+ page,
1044
+ isScanning: false,
1045
+ collapsed: false,
1046
+ };
417
1047
  }
418
1048
 
419
1049
  type handleOnScanClickFunction = () => void;
420
1050
 
421
1051
  // Window functions exposed in browser
422
1052
  const handleOnScanClick: handleOnScanClickFunction = async () => {
1053
+ consoleLogger.info('Scan: click detected');
423
1054
  log('Scan: click detected');
424
1055
  try {
1056
+ pagesDict[pageId].isScanning = true;
425
1057
  await removeOverlayMenu(page);
426
1058
  await processPage(page, processPageParams);
427
1059
  log('Scan: success');
428
- await addOverlayMenu(page, processPageParams.urlsCrawled, menuPos);
429
-
430
- Object.keys(pagesDict)
431
- .filter(k => k !== pageId)
432
- .forEach(k => {
433
- updateMenu(pagesDict[k].page, processPageParams.urlsCrawled);
434
- });
1060
+ pagesDict[pageId].isScanning = false;
1061
+ await addOverlayMenu(page, processPageParams.urlsCrawled, menuPos, {
1062
+ inProgress: false,
1063
+ collapsed: !!pagesDict[pageId]?.collapsed,
1064
+ hideStopInput: !!processPageParams.customFlowLabel,
1065
+ });
435
1066
  } catch (error) {
436
1067
  log(`Scan failed ${error}`);
437
1068
  }
438
1069
  };
439
1070
 
440
- // Detection of new url within page
1071
+ const handleOnStopClick = async () => {
1072
+ const scannedCount = processPageParams?.urlsCrawled?.scanned?.length ?? 0;
1073
+ if (scannedCount === 0) {
1074
+ if (typeof processPageParams.stopAll === 'function') {
1075
+ try {
1076
+ await processPageParams.stopAll();
1077
+ } catch (e) {
1078
+ // ignore invalid; continue without label
1079
+ }
1080
+ }
1081
+ return;
1082
+ }
1083
+
1084
+ try {
1085
+ const inputValue = await page.evaluate(async () => {
1086
+ const win = window as Window;
1087
+ if (typeof win.oobeeShowStopModal === 'function') {
1088
+ return await win.oobeeShowStopModal();
1089
+ }
1090
+ const ok = window.confirm('Are you sure you want to stop this scan?');
1091
+ return { confirmed: ok, label: '' };
1092
+ });
1093
+
1094
+ if (!inputValue?.confirmed) {
1095
+ await page.evaluate(() => {
1096
+ const stopBtn = document.getElementById('oobeeBtnStop') as HTMLButtonElement | null;
1097
+ if (stopBtn) {
1098
+ stopBtn.disabled = false;
1099
+ stopBtn.textContent = 'Stop';
1100
+ }
1101
+ });
1102
+ return;
1103
+ }
1104
+
1105
+ const label = (inputValue.label || '').trim();
1106
+ try {
1107
+ const { isValid } = validateCustomFlowLabel(label);
1108
+ if (isValid && label) {
1109
+ processPageParams.customFlowLabel = label;
1110
+ }
1111
+ } catch {
1112
+ // ignore invalid; continue without label
1113
+ }
1114
+
1115
+ if (typeof processPageParams.stopAll === 'function') {
1116
+ try {
1117
+ await processPageParams.stopAll();
1118
+ } catch (e) {
1119
+ // any console log will be on user browser, do not need to log
1120
+ }
1121
+ }
1122
+ } catch (e) {
1123
+ // any console log will be on user browser, do not need to log
1124
+ }
1125
+ };
1126
+
441
1127
  page.on('domcontentloaded', async () => {
442
1128
  try {
443
1129
  const existingOverlay = await page.evaluate(() => {
444
- return document.querySelector('#oobee-shadow-host');
1130
+ return document.querySelector('#oobeeShadowHost');
445
1131
  });
446
1132
 
447
1133
  consoleLogger.info(`Overlay state: ${existingOverlay}`);
448
1134
 
449
1135
  if (!existingOverlay) {
450
1136
  consoleLogger.info(`Adding overlay menu to page: ${page.url()}`);
451
- await addOverlayMenu(page, processPageParams.urlsCrawled, menuPos);
1137
+ await addOverlayMenu(page, processPageParams.urlsCrawled, menuPos, {
1138
+ inProgress: !!pagesDict[pageId]?.isScanning,
1139
+ collapsed: !!pagesDict[pageId]?.collapsed,
1140
+ hideStopInput: !!processPageParams.customFlowLabel,
1141
+ });
452
1142
  }
453
1143
 
454
1144
  setTimeout(() => {
@@ -474,6 +1164,7 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
474
1164
  });
475
1165
 
476
1166
  await page.exposeFunction('handleOnScanClick', handleOnScanClick);
1167
+ await page.exposeFunction('handleOnStopClick', handleOnStopClick);
477
1168
 
478
1169
  type UpdateMenuPosFunction = (newPos: any) => void;
479
1170
 
@@ -481,7 +1172,6 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
481
1172
  const updateMenuPos: UpdateMenuPosFunction = newPos => {
482
1173
  const prevPos = menuPos;
483
1174
  if (prevPos !== newPos) {
484
- console.log(`Overlay menu: position updated from ${prevPos} to ${newPos}`);
485
1175
  menuPos = newPos;
486
1176
  }
487
1177
  };