@imsovikde/source-grab 1.0.1

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 (481) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +281 -0
  4. package/background/capture-registry.js +306 -0
  5. package/background/debugger-capture.js +171 -0
  6. package/background/download-engine.js +316 -0
  7. package/background/service-worker.js +426 -0
  8. package/content-scripts/auto-scroll.js +51 -0
  9. package/content-scripts/isolated-bridge.js +36 -0
  10. package/content-scripts/main-world-hook.js +278 -0
  11. package/design-tokens.css +67 -0
  12. package/icons/2-double-tap.svg +12 -0
  13. package/icons/2-scroll-left.svg +11 -0
  14. package/icons/2-scroll-right.svg +11 -0
  15. package/icons/2-scroll-up-1.svg +11 -0
  16. package/icons/2-scroll-up.svg +11 -0
  17. package/icons/2-tap-1.svg +15 -0
  18. package/icons/2-tap.svg +11 -0
  19. package/icons/2-touch-hold.svg +13 -0
  20. package/icons/3-scroll-down.svg +11 -0
  21. package/icons/3-scroll-up.svg +11 -0
  22. package/icons/3-tap-scroll.svg +13 -0
  23. package/icons/3-tap.svg +11 -0
  24. package/icons/analytics.svg +10 -0
  25. package/icons/anchor.svg +10 -0
  26. package/icons/apple.svg +11 -0
  27. package/icons/arrow-circle-down.svg +4 -0
  28. package/icons/arrow-circle-left.svg +4 -0
  29. package/icons/arrow-circle-right.svg +4 -0
  30. package/icons/arrow-circle-up.svg +4 -0
  31. package/icons/arrow-down-2.svg +3 -0
  32. package/icons/arrow-down.svg +11 -0
  33. package/icons/arrow-left.svg +3 -0
  34. package/icons/arrow-ne.svg +3 -0
  35. package/icons/arrow-nw.svg +3 -0
  36. package/icons/arrow-right.svg +3 -0
  37. package/icons/arrow-se.svg +3 -0
  38. package/icons/arrow-single-down.svg +3 -0
  39. package/icons/arrow-single-left.svg +3 -0
  40. package/icons/arrow-single-right.svg +3 -0
  41. package/icons/arrow-single-up.svg +3 -0
  42. package/icons/arrow-square-down.svg +4 -0
  43. package/icons/arrow-square-left.svg +4 -0
  44. package/icons/arrow-square-right.svg +4 -0
  45. package/icons/arrow-square-up.svg +4 -0
  46. package/icons/arrow-sw.svg +3 -0
  47. package/icons/arrow-up-2.svg +3 -0
  48. package/icons/arrow-up.svg +11 -0
  49. package/icons/arrow.svg +10 -0
  50. package/icons/at-the-rate.svg +10 -0
  51. package/icons/automation.svg +4 -0
  52. package/icons/backward.svg +11 -0
  53. package/icons/bag-2.svg +3 -0
  54. package/icons/bag.svg +3 -0
  55. package/icons/balloon-2.svg +10 -0
  56. package/icons/balloon.svg +10 -0
  57. package/icons/bank.svg +10 -0
  58. package/icons/basket.svg +6 -0
  59. package/icons/behance.svg +12 -0
  60. package/icons/bell-2.svg +10 -0
  61. package/icons/bell.svg +10 -0
  62. package/icons/bill.svg +19 -0
  63. package/icons/bisexual.svg +10 -0
  64. package/icons/blood-bag.svg +10 -0
  65. package/icons/blood.svg +12 -0
  66. package/icons/bluetooth.svg +10 -0
  67. package/icons/bookmark.svg +10 -0
  68. package/icons/bot.svg +6 -0
  69. package/icons/bottle.svg +15 -0
  70. package/icons/box-2.svg +6 -0
  71. package/icons/box.svg +3 -0
  72. package/icons/bug.svg +3 -0
  73. package/icons/bulb.svg +10 -0
  74. package/icons/burger.svg +16 -0
  75. package/icons/bus.svg +8 -0
  76. package/icons/cake.svg +10 -0
  77. package/icons/calculator.svg +15 -0
  78. package/icons/calendar.svg +15 -0
  79. package/icons/call.svg +12 -0
  80. package/icons/camera-2.svg +10 -0
  81. package/icons/camera.svg +12 -0
  82. package/icons/candy.svg +10 -0
  83. package/icons/car.svg +8 -0
  84. package/icons/card-2.svg +9 -0
  85. package/icons/card-3.svg +6 -0
  86. package/icons/card.svg +4 -0
  87. package/icons/cart-add.svg +4 -0
  88. package/icons/cart-delete.svg +4 -0
  89. package/icons/cart-remove.svg +4 -0
  90. package/icons/cash.svg +11 -0
  91. package/icons/caution.svg +12 -0
  92. package/icons/center-align-2.svg +10 -0
  93. package/icons/center-align.svg +13 -0
  94. package/icons/checklist.svg +4 -0
  95. package/icons/chevrons-down.svg +4 -0
  96. package/icons/chevrons-left.svg +4 -0
  97. package/icons/chevrons-right.svg +4 -0
  98. package/icons/chevrons-up.svg +4 -0
  99. package/icons/chip.svg +4 -0
  100. package/icons/clap.svg +14 -0
  101. package/icons/clock.svg +11 -0
  102. package/icons/cloud-down.svg +10 -0
  103. package/icons/cloud-up.svg +10 -0
  104. package/icons/cloud.svg +10 -0
  105. package/icons/cloudy-day.svg +15 -0
  106. package/icons/cloudy-night.svg +10 -0
  107. package/icons/codepen.svg +10 -0
  108. package/icons/coffee-cup-1.svg +7 -0
  109. package/icons/coffee-cup-2.svg +6 -0
  110. package/icons/coin-2.svg +11 -0
  111. package/icons/coin.svg +11 -0
  112. package/icons/confused-emoji.svg +8 -0
  113. package/icons/conveyor-belt.svg +8 -0
  114. package/icons/cookie.svg +14 -0
  115. package/icons/cool-emoji.svg +4 -0
  116. package/icons/copy.svg +10 -0
  117. package/icons/crop.svg +10 -0
  118. package/icons/cross.svg +10 -0
  119. package/icons/crown.svg +14 -0
  120. package/icons/crying-emoji.svg +4 -0
  121. package/icons/cut.svg +10 -0
  122. package/icons/cutlery.svg +11 -0
  123. package/icons/dashboard-2.svg +10 -0
  124. package/icons/dashboard-3.svg +11 -0
  125. package/icons/dashboard-4.svg +11 -0
  126. package/icons/dashboard.svg +11 -0
  127. package/icons/delete.svg +12 -0
  128. package/icons/diamond.svg +10 -0
  129. package/icons/dish.svg +10 -0
  130. package/icons/doc-add.svg +11 -0
  131. package/icons/doc-remove.svg +11 -0
  132. package/icons/doc.svg +15 -0
  133. package/icons/dollar-2.svg +11 -0
  134. package/icons/dollar.svg +10 -0
  135. package/icons/double-tap.svg +12 -0
  136. package/icons/download.svg +11 -0
  137. package/icons/drawer.svg +10 -0
  138. package/icons/dribbble.svg +10 -0
  139. package/icons/drink.svg +10 -0
  140. package/icons/dropbox.svg +10 -0
  141. package/icons/dropper.svg +10 -0
  142. package/icons/egg.svg +11 -0
  143. package/icons/eject.svg +12 -0
  144. package/icons/eraser.svg +10 -0
  145. package/icons/euro.svg +11 -0
  146. package/icons/facebook-2.svg +10 -0
  147. package/icons/facebook.svg +3 -0
  148. package/icons/factory.svg +7 -0
  149. package/icons/fast-forward.svg +10 -0
  150. package/icons/fast-rewind.svg +10 -0
  151. package/icons/fb-messenger.svg +11 -0
  152. package/icons/femaile.svg +10 -0
  153. package/icons/file-attachment.svg +10 -0
  154. package/icons/file-audio.svg +13 -0
  155. package/icons/file-code.svg +13 -0
  156. package/icons/file-contract.svg +15 -0
  157. package/icons/file-csv.svg +13 -0
  158. package/icons/file-figma.svg +14 -0
  159. package/icons/file-form.svg +15 -0
  160. package/icons/file-image-2.svg +12 -0
  161. package/icons/file-image.svg +12 -0
  162. package/icons/file-invoice.svg +15 -0
  163. package/icons/file-jpg.svg +13 -0
  164. package/icons/file-list.svg +17 -0
  165. package/icons/file-mov.svg +13 -0
  166. package/icons/file-mp4.svg +13 -0
  167. package/icons/file-notes.svg +12 -0
  168. package/icons/file-pdf.svg +13 -0
  169. package/icons/file-png.svg +13 -0
  170. package/icons/file-spreadsheet.svg +11 -0
  171. package/icons/file-svg.svg +13 -0
  172. package/icons/file-text.svg +14 -0
  173. package/icons/file-vector.svg +11 -0
  174. package/icons/file-zip.svg +10 -0
  175. package/icons/file.svg +10 -0
  176. package/icons/filter.svg +10 -0
  177. package/icons/fire.svg +5 -0
  178. package/icons/first-aid.svg +11 -0
  179. package/icons/firstaid-2.svg +12 -0
  180. package/icons/flag-2.svg +10 -0
  181. package/icons/flag.svg +10 -0
  182. package/icons/flashlight.svg +8 -0
  183. package/icons/flask-round.svg +10 -0
  184. package/icons/flask.svg +10 -0
  185. package/icons/flip.svg +12 -0
  186. package/icons/floppy.svg +11 -0
  187. package/icons/folded-hands.svg +12 -0
  188. package/icons/folder-add.svg +11 -0
  189. package/icons/folder-delete.svg +11 -0
  190. package/icons/folder-empty.svg +10 -0
  191. package/icons/folder-remove.svg +11 -0
  192. package/icons/folder.svg +10 -0
  193. package/icons/fork.svg +10 -0
  194. package/icons/forward.svg +11 -0
  195. package/icons/frame.svg +11 -0
  196. package/icons/franc.svg +11 -0
  197. package/icons/free-drag.svg +11 -0
  198. package/icons/gay.svg +10 -0
  199. package/icons/genderless.svg +10 -0
  200. package/icons/gift.svg +10 -0
  201. package/icons/globe.svg +10 -0
  202. package/icons/google.svg +10 -0
  203. package/icons/grid-2.svg +10 -0
  204. package/icons/grid.svg +10 -0
  205. package/icons/grinning-squinting-emoji.svg +6 -0
  206. package/icons/guitar.svg +10 -0
  207. package/icons/hand-truck.svg +5 -0
  208. package/icons/hand.svg +10 -0
  209. package/icons/happy-emoji.svg +8 -0
  210. package/icons/headphone.svg +10 -0
  211. package/icons/heart-beat.svg +10 -0
  212. package/icons/heart-eyes-emoji.svg +6 -0
  213. package/icons/heart.svg +10 -0
  214. package/icons/hetro.svg +10 -0
  215. package/icons/hide.svg +12 -0
  216. package/icons/home-1.svg +10 -0
  217. package/icons/home.svg +10 -0
  218. package/icons/horizontal-scroll.svg +12 -0
  219. package/icons/hot-air-balloon.svg +3 -0
  220. package/icons/ice-cream.svg +10 -0
  221. package/icons/icon.svg +12 -0
  222. package/icons/info.svg +5 -0
  223. package/icons/injection.svg +10 -0
  224. package/icons/instagram.svg +12 -0
  225. package/icons/intersex.svg +10 -0
  226. package/icons/key.svg +11 -0
  227. package/icons/laugh-emoji.svg +8 -0
  228. package/icons/layer.svg +10 -0
  229. package/icons/left-align-2.svg +12 -0
  230. package/icons/left-align.svg +13 -0
  231. package/icons/lesbian.svg +10 -0
  232. package/icons/link.svg +11 -0
  233. package/icons/linkedin.svg +13 -0
  234. package/icons/list.svg +15 -0
  235. package/icons/location-pin.svg +10 -0
  236. package/icons/location.svg +4 -0
  237. package/icons/lock.svg +11 -0
  238. package/icons/login.svg +11 -0
  239. package/icons/logout.svg +11 -0
  240. package/icons/lungs.svg +10 -0
  241. package/icons/magic-wand.svg +13 -0
  242. package/icons/mail-gift.svg +10 -0
  243. package/icons/mail-open.svg +10 -0
  244. package/icons/mail-surprise.svg +10 -0
  245. package/icons/mail.svg +10 -0
  246. package/icons/male.svg +10 -0
  247. package/icons/map.svg +13 -0
  248. package/icons/maximize.svg +13 -0
  249. package/icons/medical-logo.svg +10 -0
  250. package/icons/megaphone.svg +10 -0
  251. package/icons/menu-2.svg +12 -0
  252. package/icons/menu.svg +12 -0
  253. package/icons/message-2.svg +13 -0
  254. package/icons/message.svg +13 -0
  255. package/icons/mic-2.svg +11 -0
  256. package/icons/mic.svg +10 -0
  257. package/icons/microscope.svg +10 -0
  258. package/icons/minimize.svg +13 -0
  259. package/icons/money-plant.svg +11 -0
  260. package/icons/move.svg +10 -0
  261. package/icons/movie-clapper.svg +13 -0
  262. package/icons/multi-touch.svg +11 -0
  263. package/icons/music-2.svg +10 -0
  264. package/icons/music-3.svg +10 -0
  265. package/icons/music.svg +10 -0
  266. package/icons/mute.svg +11 -0
  267. package/icons/navigation-2.svg +10 -0
  268. package/icons/navigation.svg +10 -0
  269. package/icons/night.svg +10 -0
  270. package/icons/non-binary-transgender.svg +10 -0
  271. package/icons/note.svg +11 -0
  272. package/icons/offer.svg +9 -0
  273. package/icons/ok.svg +10 -0
  274. package/icons/other-gender.svg +10 -0
  275. package/icons/page-move.svg +11 -0
  276. package/icons/paint-brush-2.svg +10 -0
  277. package/icons/paint-brush.svg +10 -0
  278. package/icons/paint-bucket-2.svg +10 -0
  279. package/icons/paint-bucket.svg +10 -0
  280. package/icons/paint-roller.svg +10 -0
  281. package/icons/paper-clip-2.svg +10 -0
  282. package/icons/paper-clip.svg +10 -0
  283. package/icons/paste.svg +10 -0
  284. package/icons/pause.svg +11 -0
  285. package/icons/paypal.svg +10 -0
  286. package/icons/pen-tool.svg +10 -0
  287. package/icons/pen.svg +10 -0
  288. package/icons/pencil-2.svg +10 -0
  289. package/icons/pencil-3.svg +11 -0
  290. package/icons/pencil-ruler.svg +11 -0
  291. package/icons/pencil.svg +10 -0
  292. package/icons/phone-setting.svg +12 -0
  293. package/icons/phone.svg +11 -0
  294. package/icons/photo.svg +11 -0
  295. package/icons/pie.svg +11 -0
  296. package/icons/piggy-bank.svg +13 -0
  297. package/icons/pills.svg +10 -0
  298. package/icons/pin.svg +10 -0
  299. package/icons/pinterest.svg +10 -0
  300. package/icons/pizza-2.svg +10 -0
  301. package/icons/pizza.svg +33 -0
  302. package/icons/plane.svg +3 -0
  303. package/icons/play.svg +10 -0
  304. package/icons/png.svg +11 -0
  305. package/icons/point-down.svg +10 -0
  306. package/icons/point-left.svg +10 -0
  307. package/icons/point-right.svg +3 -0
  308. package/icons/point-up.svg +10 -0
  309. package/icons/popsicle.svg +10 -0
  310. package/icons/pound.svg +11 -0
  311. package/icons/product-hunt.svg +10 -0
  312. package/icons/puzzle-2.svg +10 -0
  313. package/icons/puzzle.svg +10 -0
  314. package/icons/qr.svg +21 -0
  315. package/icons/question-2.svg +11 -0
  316. package/icons/question.svg +11 -0
  317. package/icons/rain-heavy.svg +17 -0
  318. package/icons/rain-light.svg +13 -0
  319. package/icons/record.svg +11 -0
  320. package/icons/rectangle.svg +10 -0
  321. package/icons/right-align-2.svg +12 -0
  322. package/icons/right-align.svg +13 -0
  323. package/icons/rocket.svg +7 -0
  324. package/icons/rotate.svg +11 -0
  325. package/icons/rss.svg +13 -0
  326. package/icons/ruble.svg +11 -0
  327. package/icons/ruler.svg +10 -0
  328. package/icons/rupee.svg +11 -0
  329. package/icons/sad-emoji.svg +8 -0
  330. package/icons/safe.svg +13 -0
  331. package/icons/sale.svg +7 -0
  332. package/icons/satellite.svg +3 -0
  333. package/icons/saving.svg +15 -0
  334. package/icons/scan-2.svg +15 -0
  335. package/icons/scan-fingerprint.svg +20 -0
  336. package/icons/scan.svg +14 -0
  337. package/icons/screen-rotate.svg +12 -0
  338. package/icons/scroll-down.svg +11 -0
  339. package/icons/scroll-left-1.svg +11 -0
  340. package/icons/scroll-left.svg +11 -0
  341. package/icons/scroll-up.svg +11 -0
  342. package/icons/search.svg +10 -0
  343. package/icons/send-2.svg +10 -0
  344. package/icons/send-3.svg +11 -0
  345. package/icons/send.svg +10 -0
  346. package/icons/server-2.svg +15 -0
  347. package/icons/server.svg +3 -0
  348. package/icons/setting-2.svg +11 -0
  349. package/icons/setting-3.svg +10 -0
  350. package/icons/setting.svg +12 -0
  351. package/icons/shape.svg +10 -0
  352. package/icons/shield-2.svg +11 -0
  353. package/icons/shield.svg +11 -0
  354. package/icons/ship.svg +4 -0
  355. package/icons/shocked-emoji.svg +8 -0
  356. package/icons/shop.svg +4 -0
  357. package/icons/shopping-cart-2.svg +4 -0
  358. package/icons/shopping-cart-3.svg +4 -0
  359. package/icons/shopping-cart.svg +5 -0
  360. package/icons/shuffle.svg +10 -0
  361. package/icons/signal.svg +14 -0
  362. package/icons/skype.svg +11 -0
  363. package/icons/smiling-emoji.svg +6 -0
  364. package/icons/smiling-with-eyes-emoji.svg +8 -0
  365. package/icons/snapchat.svg +10 -0
  366. package/icons/snow.svg +22 -0
  367. package/icons/snowflake.svg +10 -0
  368. package/icons/snowman.svg +16 -0
  369. package/icons/sofa.svg +10 -0
  370. package/icons/speaker.svg +12 -0
  371. package/icons/spoon.svg +10 -0
  372. package/icons/spotify.svg +13 -0
  373. package/icons/square.svg +10 -0
  374. package/icons/star.svg +10 -0
  375. package/icons/stethoscope.svg +11 -0
  376. package/icons/stopwatch.svg +11 -0
  377. package/icons/suitcase.svg +10 -0
  378. package/icons/sun-2.svg +11 -0
  379. package/icons/sun-3.svg +18 -0
  380. package/icons/sun-4.svg +11 -0
  381. package/icons/sun.svg +18 -0
  382. package/icons/sunny.svg +18 -0
  383. package/icons/surprised-emoji.svg +8 -0
  384. package/icons/swipe-left.svg +11 -0
  385. package/icons/swipe-right.svg +11 -0
  386. package/icons/switch-1.svg +11 -0
  387. package/icons/switch.svg +10 -0
  388. package/icons/sync.svg +11 -0
  389. package/icons/tablet-2.svg +19 -0
  390. package/icons/tablet.svg +11 -0
  391. package/icons/tag-2.svg +3 -0
  392. package/icons/tag.svg +4 -0
  393. package/icons/tap-1.svg +15 -0
  394. package/icons/tap.svg +11 -0
  395. package/icons/target-2.svg +11 -0
  396. package/icons/target.svg +13 -0
  397. package/icons/test-tube.svg +10 -0
  398. package/icons/thumbs-down.svg +11 -0
  399. package/icons/thumbs-up-2.svg +11 -0
  400. package/icons/thumbs-up.svg +11 -0
  401. package/icons/thunderstorm.svg +11 -0
  402. package/icons/tick-2.svg +4 -0
  403. package/icons/tick.svg +3 -0
  404. package/icons/tik-tok.svg +10 -0
  405. package/icons/tooth.svg +12 -0
  406. package/icons/tornado.svg +25 -0
  407. package/icons/touch-hold.svg +13 -0
  408. package/icons/transform.svg +10 -0
  409. package/icons/transgender-2.svg +10 -0
  410. package/icons/transgender.svg +10 -0
  411. package/icons/tree-2.svg +10 -0
  412. package/icons/tree.svg +10 -0
  413. package/icons/trend-down-square.svg +11 -0
  414. package/icons/trend-down.svg +10 -0
  415. package/icons/trend-up-square.svg +11 -0
  416. package/icons/trend-up.svg +10 -0
  417. package/icons/trophy-2.svg +3 -0
  418. package/icons/trophy.svg +10 -0
  419. package/icons/truck.svg +4 -0
  420. package/icons/tumblr.svg +11 -0
  421. package/icons/tv.svg +11 -0
  422. package/icons/twitch.svg +13 -0
  423. package/icons/twitter.svg +10 -0
  424. package/icons/uber.svg +10 -0
  425. package/icons/unbox.svg +3 -0
  426. package/icons/unhide.svg +11 -0
  427. package/icons/unlink.svg +12 -0
  428. package/icons/upload.svg +11 -0
  429. package/icons/user-add.svg +12 -0
  430. package/icons/user-caution.svg +13 -0
  431. package/icons/user-delete.svg +12 -0
  432. package/icons/user-female.svg +11 -0
  433. package/icons/user-male.svg +11 -0
  434. package/icons/user-remove.svg +12 -0
  435. package/icons/user.svg +11 -0
  436. package/icons/v.svg +10 -0
  437. package/icons/vector.svg +10 -0
  438. package/icons/vibrate.svg +15 -0
  439. package/icons/video-camera.svg +10 -0
  440. package/icons/volume-down.svg +11 -0
  441. package/icons/volume-up.svg +12 -0
  442. package/icons/wallet.svg +11 -0
  443. package/icons/warehouse.svg +3 -0
  444. package/icons/water.svg +10 -0
  445. package/icons/wave-left.svg +11 -0
  446. package/icons/wave-right.svg +11 -0
  447. package/icons/webflow.svg +10 -0
  448. package/icons/whatsapp.svg +11 -0
  449. package/icons/wheelchair.svg +11 -0
  450. package/icons/wind.svg +14 -0
  451. package/icons/windows.svg +10 -0
  452. package/icons/wink-emoji.svg +6 -0
  453. package/icons/won.svg +11 -0
  454. package/icons/worried-emoji.svg +8 -0
  455. package/icons/y-combinator.svg +10 -0
  456. package/icons/yen.svg +11 -0
  457. package/icons/youtube.svg +11 -0
  458. package/icons/zap.svg +10 -0
  459. package/icons/zoom-in-2.svg +12 -0
  460. package/icons/zoom-in.svg +11 -0
  461. package/icons/zoom-out-1.svg +11 -0
  462. package/icons/zoom-out-2.svg +11 -0
  463. package/icons/zoom-out.svg +11 -0
  464. package/manifest.json +43 -0
  465. package/package.json +53 -0
  466. package/sidepanel/app.js +437 -0
  467. package/sidepanel/components/bottom-dock.js +148 -0
  468. package/sidepanel/components/empty-state.js +40 -0
  469. package/sidepanel/components/file-list-item.js +58 -0
  470. package/sidepanel/components/file-list.js +134 -0
  471. package/sidepanel/components/filter-bar.js +261 -0
  472. package/sidepanel/components/progress-overlay.js +79 -0
  473. package/sidepanel/components/recording-indicator.js +32 -0
  474. package/sidepanel/components/stats-header.js +37 -0
  475. package/sidepanel/icons/file-type-icons.svg +55 -0
  476. package/sidepanel/icons/ui-icons.svg +85 -0
  477. package/sidepanel/index.html +37 -0
  478. package/sidepanel/styles.css +934 -0
  479. package/sidepanel/utils/clipboard.js +33 -0
  480. package/sidepanel/utils/format.js +23 -0
  481. package/sidepanel/utils/virtual-scroll.js +107 -0
@@ -0,0 +1,426 @@
1
+ // background/service-worker.js
2
+ // v1.1 — Persistent data, onSuspend flush, deeper scan, stop-scroll, no auto-clear
3
+
4
+ import { CaptureRegistry } from './capture-registry.js';
5
+ import { DebuggerCapture } from './debugger-capture.js';
6
+ import { DownloadEngine } from './download-engine.js';
7
+
8
+ const registry = new CaptureRegistry();
9
+ const debuggerCapture = new DebuggerCapture(registry);
10
+ const downloadEngine = new DownloadEngine(registry, debuggerCapture);
11
+
12
+ // Track which tabs have active capture (in-memory, rehydrated from storage)
13
+ const activeTabs = new Set();
14
+ let keepAliveInterval = null;
15
+
16
+ function startKeepAlive() {
17
+ if (keepAliveInterval) return;
18
+ keepAliveInterval = setInterval(() => chrome.runtime.getPlatformInfo(() => {}), 25000);
19
+ }
20
+
21
+ function stopKeepAlive() {
22
+ if (keepAliveInterval) {
23
+ clearInterval(keepAliveInterval);
24
+ keepAliveInterval = null;
25
+ }
26
+ }
27
+
28
+ // ─── Synchronous top-level event registration (CRITICAL for MV3) ───
29
+
30
+ chrome.runtime.onInstalled.addListener(() => {
31
+ chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
32
+ });
33
+
34
+ chrome.runtime.onStartup.addListener(async () => {
35
+ await registry.init();
36
+ // Rehydrate active tab set from session storage (capture state is session-scoped)
37
+ const allData = await chrome.storage.session.get(null);
38
+ for (const [key, value] of Object.entries(allData)) {
39
+ if (key.startsWith('capture_state_') && value?.active) {
40
+ const tabId = parseInt(key.replace('capture_state_', ''), 10);
41
+ activeTabs.add(tabId);
42
+ }
43
+ }
44
+ if (activeTabs.size > 0) startKeepAlive();
45
+ });
46
+
47
+ // Eagerly init on first module load too (not just onStartup)
48
+ registry.init().then(() => {
49
+ chrome.storage.session.get(null).then(allData => {
50
+ for (const [key, value] of Object.entries(allData)) {
51
+ if (key.startsWith('capture_state_') && value?.active) {
52
+ const tabId = parseInt(key.replace('capture_state_', ''), 10);
53
+ activeTabs.add(tabId);
54
+ }
55
+ }
56
+ if (activeTabs.size > 0) startKeepAlive();
57
+ });
58
+ });
59
+
60
+ // ─── Service worker suspend — flush all pending data ───
61
+
62
+ chrome.runtime.onSuspend.addListener(() => {
63
+ registry.flushAll();
64
+ });
65
+
66
+ // ─── Tab lifecycle ───
67
+
68
+ chrome.tabs.onRemoved.addListener((tabId) => {
69
+ activeTabs.delete(tabId);
70
+ // DON'T clear registry on tab close — user must explicitly clear.
71
+ // Only clear the session-scoped capture state.
72
+ chrome.storage.session.remove(`capture_state_${tabId}`);
73
+ chrome.storage.session.remove(`download_progress_${tabId}`);
74
+ if (activeTabs.size === 0) stopKeepAlive();
75
+ });
76
+
77
+ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
78
+ if (changeInfo.url && activeTabs.has(tabId)) {
79
+ await registry.addNavigationMarker(tabId, changeInfo.url);
80
+
81
+ // Update capture state URL
82
+ const stateKey = `capture_state_${tabId}`;
83
+ const stateData = await chrome.storage.session.get(stateKey);
84
+ if (stateData[stateKey]) {
85
+ await chrome.storage.session.set({
86
+ [stateKey]: { ...stateData[stateKey], tabUrl: changeInfo.url }
87
+ });
88
+ }
89
+
90
+ // Re-inject content scripts on navigation
91
+ try {
92
+ await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, files: ['content-scripts/main-world-hook.js'], world: 'MAIN', injectImmediately: true });
93
+ await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, files: ['content-scripts/isolated-bridge.js'], world: 'ISOLATED', injectImmediately: true });
94
+ } catch(e) { /* Tab may not be ready yet */ }
95
+
96
+ // Re-scan resource tree after navigation if debugger is attached
97
+ const state = stateData[stateKey];
98
+ if (state?.deepScan) {
99
+ setTimeout(async () => {
100
+ try { await debuggerCapture.getResourceTree(tabId); } catch(e) {}
101
+ }, 1500); // Wait for page to load resources
102
+ }
103
+ }
104
+ });
105
+
106
+ // ─── webRequest listeners (fire only after host_permissions granted) ───
107
+
108
+ chrome.webRequest.onBeforeRequest.addListener(
109
+ (details) => {
110
+ if (details.tabId >= 0 && activeTabs.has(details.tabId)) {
111
+ registry.upsert(details.tabId, {
112
+ url: details.url,
113
+ source: 'webRequest',
114
+ status: 'requested',
115
+ resourceType: details.type
116
+ });
117
+ }
118
+ },
119
+ { urls: ['<all_urls>'] }
120
+ );
121
+
122
+ chrome.webRequest.onResponseStarted.addListener(
123
+ (details) => {
124
+ if (details.tabId >= 0 && activeTabs.has(details.tabId)) {
125
+ let size = null;
126
+ const cl = details.responseHeaders?.find(h => h.name.toLowerCase() === 'content-length');
127
+ if (cl) size = parseInt(cl.value, 10);
128
+ const ct = details.responseHeaders?.find(h => h.name.toLowerCase() === 'content-type');
129
+
130
+ registry.upsert(details.tabId, {
131
+ url: details.url,
132
+ statusCode: details.statusCode,
133
+ mimeType: ct ? ct.value.split(';')[0].trim() : null,
134
+ size: isNaN(size) ? null : size,
135
+ resourceType: details.type
136
+ });
137
+ }
138
+ },
139
+ { urls: ['<all_urls>'] },
140
+ ['responseHeaders']
141
+ );
142
+
143
+ chrome.webRequest.onCompleted.addListener(
144
+ (details) => {
145
+ if (details.tabId >= 0 && activeTabs.has(details.tabId)) {
146
+ registry.upsert(details.tabId, {
147
+ url: details.url,
148
+ status: 'completed'
149
+ });
150
+ }
151
+ },
152
+ { urls: ['<all_urls>'] }
153
+ );
154
+
155
+ chrome.webRequest.onErrorOccurred.addListener(
156
+ (details) => {
157
+ if (details.tabId >= 0 && activeTabs.has(details.tabId)) {
158
+ registry.upsert(details.tabId, {
159
+ url: details.url,
160
+ status: 'error'
161
+ });
162
+ }
163
+ },
164
+ { urls: ['<all_urls>'] }
165
+ );
166
+
167
+ // ─── Message router ───
168
+
169
+ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
170
+ // Content script resource detection
171
+ if (msg.type === 'RESOURCE_DETECTED') {
172
+ if (sender.tab && sender.tab.id && activeTabs.has(sender.tab.id)) {
173
+ const payload = msg.payload;
174
+ const tabId = sender.tab.id;
175
+
176
+ if (payload.type === 'RESOURCE_BLOB_DATA') {
177
+ return;
178
+ }
179
+
180
+ const entry = { url: payload.url, source: payload.source || 'domHook' };
181
+ if (payload.statusCode) entry.statusCode = payload.statusCode;
182
+ if (payload.mimeType) entry.mimeType = payload.mimeType;
183
+ if (payload.size) entry.size = payload.size;
184
+ if (payload.filename) entry.filename = payload.filename;
185
+ if (payload.type === 'FETCH_RESPONSE' || payload.type === 'XHR_RESPONSE') {
186
+ entry.status = 'completed';
187
+ }
188
+ if (payload.type === 'INLINE_SVG') {
189
+ entry.mimeType = 'image/svg+xml';
190
+ entry.status = 'completed';
191
+ entry.domContext = 'inline-svg';
192
+ }
193
+ if (payload.type === 'CSS_BACKGROUND') {
194
+ entry.domContext = 'css-background';
195
+ }
196
+ if (payload.type === 'DOM_RESOURCE') {
197
+ entry.domContext = payload.domContext || 'mutation';
198
+ }
199
+
200
+ registry.upsert(tabId, entry);
201
+ }
202
+ return;
203
+ }
204
+
205
+ // ─── Side Panel messages ───
206
+
207
+ if (msg.type === 'START_CAPTURE') {
208
+ (async () => {
209
+ try {
210
+ const tabId = msg.tabId;
211
+ activeTabs.add(tabId);
212
+
213
+ // Inject content scripts into the current tab
214
+ try {
215
+ await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, files: ['content-scripts/main-world-hook.js'], world: 'MAIN', injectImmediately: true });
216
+ } catch(e) { console.warn('Main hook inject:', e.message); }
217
+ try {
218
+ await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, files: ['content-scripts/isolated-bridge.js'], world: 'ISOLATED', injectImmediately: true });
219
+ } catch(e) { console.warn('Bridge inject:', e.message); }
220
+
221
+ // Register for future navigations (may already be registered, so try-catch)
222
+ try {
223
+ await chrome.scripting.registerContentScripts([
224
+ { id: 'sg-main-hook', js: ['content-scripts/main-world-hook.js'], matches: ['<all_urls>'], runAt: 'document_start', world: 'MAIN', allFrames: true },
225
+ { id: 'sg-bridge', js: ['content-scripts/isolated-bridge.js'], matches: ['<all_urls>'], runAt: 'document_start', world: 'ISOLATED', allFrames: true }
226
+ ]);
227
+ } catch (regErr) {
228
+ console.log('Content scripts already registered:', regErr.message);
229
+ }
230
+
231
+ // Attempt debugger attach (debugger is now a required permission)
232
+ let deepScan = false;
233
+ try {
234
+ await debuggerCapture.attach(tabId);
235
+ deepScan = true;
236
+
237
+ // Schedule a second resource tree scan after 3s for late-loading resources
238
+ setTimeout(async () => {
239
+ try { await debuggerCapture.getResourceTree(tabId); } catch(e) {}
240
+ }, 3000);
241
+ } catch (e) {
242
+ console.warn('Debugger attach failed:', e);
243
+ }
244
+
245
+ const tab = await chrome.tabs.get(tabId);
246
+ await chrome.storage.session.set({
247
+ [`capture_state_${tabId}`]: { active: true, deepScan, tabUrl: tab.url, startTime: Date.now() }
248
+ });
249
+
250
+ startKeepAlive();
251
+ sendResponse({ success: true });
252
+ } catch (e) {
253
+ sendResponse({ success: false, error: e.message });
254
+ }
255
+ })();
256
+ return true;
257
+ }
258
+
259
+ if (msg.type === 'STOP_CAPTURE') {
260
+ (async () => {
261
+ try {
262
+ const tabId = msg.tabId;
263
+ activeTabs.delete(tabId);
264
+
265
+ // Detach debugger
266
+ await debuggerCapture.detach(tabId);
267
+
268
+ // Unregister content scripts
269
+ try {
270
+ await chrome.scripting.unregisterContentScripts({ ids: ['sg-main-hook', 'sg-bridge'] });
271
+ } catch (e) { /* may not be registered */ }
272
+
273
+ // Update state — mark as inactive but DON'T clear registry
274
+ const stateKey = `capture_state_${tabId}`;
275
+ const data = await chrome.storage.session.get(stateKey);
276
+ if (data[stateKey]) {
277
+ await chrome.storage.session.set({
278
+ [stateKey]: { ...data[stateKey], active: false }
279
+ });
280
+ }
281
+
282
+ // Flush any pending registry writes to ensure data is saved
283
+ await registry.flushAll();
284
+
285
+ if (activeTabs.size === 0) stopKeepAlive();
286
+ sendResponse({ success: true });
287
+ } catch (e) {
288
+ sendResponse({ success: false, error: e.message });
289
+ }
290
+ })();
291
+ return true;
292
+ }
293
+
294
+ if (msg.type === 'TOGGLE_DEEP_SCAN') {
295
+ (async () => {
296
+ try {
297
+ const tabId = msg.tabId;
298
+ const enabled = msg.enabled;
299
+
300
+ if (enabled) {
301
+ try {
302
+ await debuggerCapture.attach(tabId);
303
+ // Immediate + delayed resource tree scan for thorough coverage
304
+ setTimeout(async () => {
305
+ try { await debuggerCapture.getResourceTree(tabId); } catch(e) {}
306
+ }, 2000);
307
+ } catch(e) {
308
+ console.warn('Debugger attach for deep scan:', e);
309
+ }
310
+ } else {
311
+ await debuggerCapture.detach(tabId);
312
+ }
313
+
314
+ // Update capture state
315
+ const stateKey = `capture_state_${tabId}`;
316
+ const data = await chrome.storage.session.get(stateKey);
317
+ if (data[stateKey]) {
318
+ await chrome.storage.session.set({
319
+ [stateKey]: { ...data[stateKey], deepScan: enabled }
320
+ });
321
+ }
322
+
323
+ sendResponse({ success: true });
324
+ } catch (e) {
325
+ sendResponse({ success: false, error: e.message });
326
+ }
327
+ })();
328
+ return true;
329
+ }
330
+
331
+ if (msg.type === 'DOWNLOAD_SELECTED') {
332
+ (async () => {
333
+ try {
334
+ const batchId = await downloadEngine.downloadBatch(msg.tabId, msg.urls);
335
+ sendResponse({ batchId });
336
+ } catch (e) {
337
+ sendResponse({ error: e.message });
338
+ }
339
+ })();
340
+ return true;
341
+ }
342
+
343
+ if (msg.type === 'DOWNLOAD_ZIP') {
344
+ (async () => {
345
+ try {
346
+ const res = await downloadEngine.downloadZip(msg.tabId, msg.urls, msg.zipFilename);
347
+ sendResponse(res);
348
+ } catch (e) {
349
+ sendResponse({ success: false, error: e.message });
350
+ }
351
+ })();
352
+ return true;
353
+ }
354
+
355
+ if (msg.type === 'AUTO_SCROLL') {
356
+ (async () => {
357
+ try {
358
+ await chrome.scripting.executeScript({ target: { tabId: msg.tabId }, files: ['content-scripts/auto-scroll.js'], world: 'MAIN' });
359
+ sendResponse({ success: true });
360
+ } catch (e) {
361
+ sendResponse({ success: false, error: e.message });
362
+ }
363
+ })();
364
+ return true;
365
+ }
366
+
367
+ if (msg.type === 'STOP_AUTO_SCROLL') {
368
+ (async () => {
369
+ try {
370
+ await chrome.scripting.executeScript({
371
+ target: { tabId: msg.tabId },
372
+ func: () => { window.__sgStopScroll = true; },
373
+ world: 'MAIN'
374
+ });
375
+ sendResponse({ success: true });
376
+ } catch (e) {
377
+ sendResponse({ success: false, error: e.message });
378
+ }
379
+ })();
380
+ return true;
381
+ }
382
+
383
+ if (msg.type === 'CLEAR_CAPTURE') {
384
+ (async () => {
385
+ try {
386
+ await registry.clear(msg.tabId);
387
+ sendResponse({ success: true });
388
+ } catch (e) {
389
+ sendResponse({ success: false, error: e.message });
390
+ }
391
+ })();
392
+ return true;
393
+ }
394
+
395
+ if (msg.type === 'RETRY_DOWNLOAD') {
396
+ (async () => {
397
+ try {
398
+ await downloadEngine.downloadBatch(msg.tabId, [msg.url]);
399
+ sendResponse({ success: true });
400
+ } catch (e) {
401
+ sendResponse({ success: false, error: e.message });
402
+ }
403
+ })();
404
+ return true;
405
+ }
406
+
407
+ // GET_REGISTRY — side panel requests full registry data for a tab
408
+ if (msg.type === 'GET_REGISTRY') {
409
+ (async () => {
410
+ try {
411
+ const tabId = msg.tabId;
412
+ const data = await chrome.storage.local.get(`registry_${tabId}`);
413
+ sendResponse({ registry: data[`registry_${tabId}`] || null });
414
+ } catch (e) {
415
+ sendResponse({ registry: null });
416
+ }
417
+ })();
418
+ return true;
419
+ }
420
+ });
421
+
422
+ // ─── Downloads tracker ───
423
+
424
+ chrome.downloads.onChanged.addListener((delta) => {
425
+ // DownloadEngine tracks its own downloads internally via chromeDownload()
426
+ });
@@ -0,0 +1,51 @@
1
+ // content-scripts/auto-scroll.js
2
+ // v1.1 — Stoppable auto-scroll via window.__sgStopScroll flag
3
+
4
+ (function() {
5
+ // Reset stop flag
6
+ window.__sgStopScroll = false;
7
+
8
+ const SCROLL_STEP = 400;
9
+ const SCROLL_INTERVAL = 600;
10
+ const IDLE_THRESHOLD = 3;
11
+ const MAX_SCROLLS = 500;
12
+
13
+ let scrollCount = 0;
14
+ let lastHeight = document.documentElement.scrollHeight;
15
+ let idleCount = 0;
16
+
17
+ const interval = setInterval(() => {
18
+ // Check stop flag
19
+ if (window.__sgStopScroll) {
20
+ clearInterval(interval);
21
+ window.__sgStopScroll = false;
22
+ return;
23
+ }
24
+
25
+ const currentHeight = document.documentElement.scrollHeight;
26
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
27
+ const clientHeight = window.innerHeight;
28
+
29
+ // At the bottom?
30
+ if (scrollTop + clientHeight >= currentHeight - 10) {
31
+ if (currentHeight === lastHeight) {
32
+ idleCount++;
33
+ } else {
34
+ idleCount = 0;
35
+ lastHeight = currentHeight;
36
+ }
37
+
38
+ if (idleCount >= IDLE_THRESHOLD) {
39
+ clearInterval(interval);
40
+ return;
41
+ }
42
+ }
43
+
44
+ window.scrollBy({ top: SCROLL_STEP, behavior: 'smooth' });
45
+ scrollCount++;
46
+
47
+ if (scrollCount >= MAX_SCROLLS) {
48
+ clearInterval(interval);
49
+ }
50
+ }, SCROLL_INTERVAL);
51
+ })();
@@ -0,0 +1,36 @@
1
+ // content-scripts/isolated-bridge.js
2
+ // Runs in ISOLATED world. Relays messages between MAIN world and service worker.
3
+
4
+ (function() {
5
+ if (window.__SOURCEGRAB_BRIDGE__) return;
6
+ window.__SOURCEGRAB_BRIDGE__ = true;
7
+
8
+ const CAPTURE_CHANNEL = '__SOURCEGRAB_CAPTURE__';
9
+ const CMD_CHANNEL = '__SOURCEGRAB_COMMAND__';
10
+
11
+ // MAIN world → Service Worker
12
+ window.addEventListener('message', (event) => {
13
+ if (event.source !== window) return;
14
+ if (event.data?.channel !== CAPTURE_CHANNEL) return;
15
+
16
+ try {
17
+ chrome.runtime.sendMessage({
18
+ type: 'RESOURCE_DETECTED',
19
+ payload: event.data.payload
20
+ });
21
+ } catch (e) {
22
+ // Extension context may be invalidated (e.g., extension reloaded)
23
+ }
24
+ });
25
+
26
+ // Service Worker → MAIN world
27
+ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
28
+ if (msg.type === 'COMMAND_TO_PAGE') {
29
+ window.postMessage({
30
+ channel: CMD_CHANNEL,
31
+ command: msg.command,
32
+ payload: msg.payload
33
+ }, '*');
34
+ }
35
+ });
36
+ })();