@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to SourceGrab will be documented in this file.
4
+
5
+ ## [1.0.0] - 2026-07-24
6
+
7
+ ### Added
8
+ - Real-time resource capture via Chrome webRequest API
9
+ - Deep scan mode using Chrome DevTools Protocol (CDP)
10
+ - Category-based filtering (Images, Scripts, Stylesheets, Fonts, Media, SVG, PDF, Documents)
11
+ - Search by filename or URL
12
+ - Sort by load order, file size, file type, alphabetical, newest
13
+ - Size range filtering
14
+ - Select All / Deselect All
15
+ - Individual file download with exact filename matching
16
+ - Batch ZIP download with timestamped filenames
17
+ - Copy filenames to clipboard
18
+ - Auto-scroll page to discover lazy-loaded content
19
+ - Data persistence across tab switches and panel close/reopen
20
+ - Master clear button in header
21
+ - Custom dropdown menus (no native selects)
22
+ - Glassmorphism header and footer dock
23
+ - Spring physics button animations
24
+ - Recording indicator with pulse animation
25
+ - Empty state with contextual messages
26
+ - Toast notification system
27
+ - Responsive narrow-width layout
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Souvik Dey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ <div align="center">
2
+
3
+ # 🌐 SourceGrab
4
+
5
+ **Capture every file your browser loads — in real-time.**
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-E0977A.svg)](LICENSE)
8
+ [![Chrome MV3](https://img.shields.io/badge/Chrome-Extension%20MV3-4285F4.svg?logo=googlechrome&logoColor=white)]()
9
+ [![Release](https://img.shields.io/badge/Release-v1.0.0-8CBA80.svg)](https://github.com/imsovikde/source-grab/releases/latest)
10
+ [![GitHub Package](https://img.shields.io/badge/GitHub%20Package-v1.0.0-4285F4.svg?logo=github)](https://github.com/imsovikde/source-grab/pkgs/npm/source-grab)
11
+ [![CI Build](https://img.shields.io/badge/CI-Passing-brightgreen.svg)](https://github.com/imsovikde/source-grab/actions)
12
+ [![AI Agents Ready](https://img.shields.io/badge/AGENTS.md-Ready-C9C2E8.svg)](AGENTS.md)
13
+ [![Zero Dependencies](https://img.shields.io/badge/Dependencies-Zero-E8C766.svg)]()
14
+ [![Vanilla ES Modules](https://img.shields.io/badge/Code-Vanilla%20ES%20Modules-A8C8E0.svg)]()
15
+
16
+ A high-performance Manifest V3 Chrome Extension that continuously intercepts, catalogs, and downloads every network resource loaded by any active web tab using a **Three-Layer Interception Engine** powered by the **Chrome DevTools Protocol (CDP)**.
17
+
18
+ [⚡ Live Landing Page](https://source-grab.vercel.app/) • [📦 Download v1.0.0 Package](https://github.com/imsovikde/source-grab/releases/tag/v1.0.0) • [📖 Architecture Docs](ARCHITECTURE.md) • [💬 Support Center](SUPPORT.md)
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ ## 📖 Executive Summary & Vision
25
+
26
+ Extracting assets from modern dynamic websites often requires opening DevTools, repeatedly scrolling to trigger lazy-loaded images, right-clicking elements individually, or missing background Blobs and Web Worker streams entirely.
27
+
28
+ **SourceGrab** solves this by replicating the combined power of Chrome DevTools' **Network** and **Sources** panels inside a persistent side panel UI. It intercepts files as they travel across the browser pipeline, categorizes them automatically, allows copy-to-clipboard of file names, and enables bulk downloads individually or as compressed `.zip` archives while preserving original website path structures.
29
+
30
+ > [!NOTE]
31
+ > **SourceGrab operates 100% locally inside your browser.** No network data, URLs, or captured files leave your device.
32
+
33
+ ---
34
+
35
+ ## 🎯 Target Audiences & Key Use Cases
36
+
37
+ | User Persona | Core Problem Solved | Primary SourceGrab Feature |
38
+ |---|---|---|
39
+ | 💻 **Web Developers** | Asset auditing & network payload inspection | Real-time CDP capture & category filtering |
40
+ | 🎨 **UI/UX Designers** | Extracting reference SVGs, fonts, and high-res images | One-click SVG filter & filename copying |
41
+ | 🧪 **QA Engineers** | Verifying loaded network assets match build manifests | Size sorting & total count tracking |
42
+ | 📚 **Archivists & Researchers** | Capturing complete single-page offline asset snapshots | Timestamped ZIP bundle download |
43
+ | ⚡ **Content Scrapers** | Bulk saving media without manual right-click saving | Auto-scroll lazy-load capture engine |
44
+
45
+ ---
46
+
47
+ ## ✨ Comprehensive Feature Matrix
48
+
49
+ ### 1. 🔍 Three-Layer Interception Pipeline
50
+ - **Layer 1 (Passive `webRequest`):** Listens to `chrome.webRequest.onBeforeRequest`, `onResponseStarted`, and `onCompleted` events to index standard HTTP/HTTPS traffic.
51
+ - **Layer 2 (MAIN-World DOM & Fetch Hook):** Overrides `window.fetch` and `XMLHttpRequest`, attaches `MutationObserver` for dynamically injected DOM nodes, and hooks Shadow DOM elements.
52
+ - **Layer 3 (Chrome DevTools Protocol - CDP Deep Scan):** Connects `chrome.debugger` to capture raw response bodies, data URIs, Web Workers, inline Blobs, and stream buffers invisible to standard network listeners.
53
+
54
+ ### 2. 🏷️ Category Filtering & Real-Time Search
55
+ - Instant one-click filter buttons for **Images**, **SVG**, **PDF**, **Scripts**, **Stylesheets**, **Fonts**, **Media**, **Documents**, and **Other**.
56
+ - Live search bar supporting instant filtering by filename substring or full URL path.
57
+ - Sorting controls: **Load Order**, **File Size**, **File Type**, **Alphabetical**, and **Newest First**.
58
+
59
+ ### 3. 📦 Dual Download Engine & ZIP Export
60
+ - **Individual File Download:** Triggers Chrome native downloads while preserving website directory hierarchy (`SourceGrab/<hostname>/<path>/<filename>`).
61
+ - **Timestamped ZIP Packaging:** Generates compressed `.zip` archives directly in browser memory formatted as `SourceGrab-YYYY-MM-DD_HH-mm-ss.zip`.
62
+ - **Concurrency Control:** Queue manager caps active downloads at 5 concurrent streams with automatic 3-retry backoff.
63
+
64
+ ### 4. 📋 One-Click Copy Filenames
65
+ - Filter your view (e.g., all SVG files) and click **Copy** in the dock.
66
+ - Copies all visible filenames (with file extensions) to the system clipboard, separated by line breaks.
67
+
68
+ ### 5. 🔄 Auto-Scroll & Lazy Content Discovery
69
+ - Toggles automated incremental page scrolling to trigger `IntersectionObserver` lazy-loaded images, infinite-scroll JSON requests, and video stream segments.
70
+
71
+ ### 6. 💾 Multi-Tab Storage Isolation & Service Worker Safety
72
+ - Uses `chrome.storage.local` indexed by tab ID (`registry_${tabId}`) ensuring captured data survives sidepanel toggling, tab switching, and Chrome service worker restarts.
73
+
74
+ ### 7. 🎨 Premium Glassmorphic Side Panel UI
75
+ - Designed with **Space Grotesk** (display titles) and **Outfit** (UI body) Google Fonts.
76
+ - Squircle content cards (20px border radius) and pill controls (999px border radius).
77
+ - Smooth spring-physics animations without performance-heavy backdrop blurs.
78
+
79
+ ---
80
+
81
+ ## ⚡ Installation & Setup Guide
82
+
83
+ ### 1. Developer Sideload Guide (Unpacked Extension)
84
+
85
+ > [!IMPORTANT]
86
+ > SourceGrab is built using pure Vanilla ES Modules. No `npm install`, Node.js build, or Webpack bundling step is required to run the extension in Chrome!
87
+
88
+ 1. **Clone the Repository:**
89
+ ```bash
90
+ git clone https://github.com/imsovikde/source-grab.git
91
+ cd source-grab
92
+ ```
93
+
94
+ 2. **Open Chrome Extensions Page:**
95
+ Navigate to `chrome://extensions` in your Google Chrome browser.
96
+
97
+ 3. **Enable Developer Mode:**
98
+ Toggle the **Developer mode** switch in the top-right corner of the page.
99
+
100
+ 4. **Load Unpacked Extension:**
101
+ - Click the **Load unpacked** button in the top-left toolbar.
102
+ - Select the cloned `source-grab` root folder.
103
+
104
+ 5. **Open Side Panel:**
105
+ - Click the extension icon in your Chrome toolbar or open the Chrome Side Panel.
106
+ - Select **SourceGrab** from the panel dropdown.
107
+ - Click **Start** to begin capturing resources in real-time!
108
+
109
+ ---
110
+
111
+ ### 2. Installing `@imsovikde/source-grab` via npm
112
+
113
+ Because `@imsovikde/source-grab` is published to **GitHub Packages (`npm.pkg.github.com`)**, npm needs to know which registry handles the `@imsovikde` scope.
114
+
115
+ #### Step 1: Configure `.npmrc`
116
+ Create a `.npmrc` file in your project directory (or user directory `~/.npmrc`) with the following scope rule:
117
+
118
+ ```ini
119
+ @imsovikde:registry=https://npm.pkg.github.com
120
+ ```
121
+
122
+ #### Step 2: Run npm Install
123
+ ```bash
124
+ npm install @imsovikde/source-grab@1.0.0
125
+ ```
126
+
127
+ > [!TIP]
128
+ > If you have a GitHub Personal Access Token (`read:packages`), you can authenticate by adding `//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN` to your `.npmrc`.
129
+
130
+ ---
131
+
132
+ ## 🛠️ Project Directory Tree
133
+
134
+ ```
135
+ source-grab/
136
+ ├── manifest.json # Manifest V3 extension configuration
137
+ ├── package.json # Project metadata & packaging scripts
138
+ ├── design-tokens.css # Central CSS variables & theme tokens
139
+ ├── AGENTS.md # AI coding agent development guidelines
140
+ ├── ARCHITECTURE.md # System architecture & Mermaid flow diagrams
141
+ ├── SUPPORT.md # Troubleshooting & user support guide
142
+ ├── GOVERNANCE.md # Project maintainership & RFC standards
143
+ ├── LICENSE # Permissive MIT License
144
+ ├── README.md # Product page & main documentation
145
+ ├── CHANGELOG.md # Version history log
146
+ ├── CODE_OF_CONDUCT.md # Contributor Covenant v2.1
147
+ ├── CONTRIBUTING.md # Developer contribution guidelines
148
+ ├── SECURITY.md # Vulnerability disclosure policy
149
+ ├── PRD.md # Product Requirements Document
150
+ ├── TRD.md # Technical Requirements Document
151
+ ├── .github/ # GitHub workflow & community templates
152
+ │ ├── CODEOWNERS # Maintainer code mapping
153
+ │ ├── DEPENDABOT.yml # Weekly dependency security config
154
+ │ ├── FUNDING.yml # Maintainer sponsorship config
155
+ │ ├── PULL_REQUEST_TEMPLATE.md # PR review checklist
156
+ │ ├── ISSUE_TEMPLATE/ # Bug report & feature templates
157
+ │ └── workflows/ # CI & Release Actions
158
+ │ ├── ci.yml # Extension build verification
159
+ │ ├── release.yml # Release ZIP automation workflow
160
+ │ └── publish-package.yml # GitHub Packages npm publication
161
+ ├── background/ # Service Worker & Data Engine
162
+ │ ├── service-worker.js # Central event & messaging hub
163
+ │ ├── capture-registry.js # Local storage registry manager
164
+ │ ├── debugger-capture.js # CDP debugger deep-scan driver
165
+ │ └── download-engine.js # Downloader & ZIP builder
166
+ ├── content-scripts/ # DOM Hooks & Injectors
167
+ │ ├── main-world-hook.js # Fetch/XHR/DOM overrides
168
+ │ ├── isolated-bridge.js # Content script message bridge
169
+ │ └── auto-scroll.js # Automated page scroll driver
170
+ ├── sidepanel/ # User Interface Shell
171
+ │ ├── index.html # Main side panel markup
172
+ │ ├── app.js # Main UI controller & storage listeners
173
+ │ ├── styles.css # Glassmorphic component styles
174
+ │ ├── components/ # UI Component Modules
175
+ │ │ ├── bottom-dock.js # 8-button dock controller
176
+ │ │ ├── file-list.js # Virtual scroll file list
177
+ │ │ ├── file-list-item.js # Individual list item DOM renderer
178
+ │ │ ├── filter-bar.js # Category filter buttons
179
+ │ │ ├── stats-header.js # Resource counters
180
+ │ │ ├── recording-indicator.js # Animated capture status pill
181
+ │ │ ├── progress-overlay.js # Batch download overlay
182
+ │ │ └── empty-state.js # Contextual empty state renderer
183
+ │ └── utils/ # Side panel helper modules
184
+ │ ├── clipboard.js # Copy to clipboard helper
185
+ │ ├── format.js # Bytes & time formatters
186
+ │ └── virtual-scroll.js # High-performance 60fps list engine
187
+ ├── icons/ # 450+ Vector SVG Icon Set
188
+ └── docs/ # Vercel / Cloudflare Landing Page
189
+ ├── index.html # Responsive self-contained landing page
190
+ ├── robots.txt # Search engine crawling rules
191
+ └── sitemap.xml # SEO Sitemap index
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 📐 Architecture & Data Flow
197
+
198
+ ```mermaid
199
+ flowchart TD
200
+ subgraph Active Browser Tab
201
+ PAGE[Web Page DOM]
202
+ MWH[MAIN World Script Hook]
203
+ end
204
+
205
+ subgraph Service Worker Layer
206
+ SW[background/service-worker.js]
207
+ REG[capture-registry.js]
208
+ CDP[debugger-capture.js]
209
+ DL[download-engine.js]
210
+ end
211
+
212
+ subgraph Storage API
213
+ LOCAL[(chrome.storage.local)]
214
+ SESSION[(chrome.storage.session)]
215
+ end
216
+
217
+ subgraph Sidepanel UI
218
+ APP[sidepanel/app.js]
219
+ LIST[VirtualScroll Engine]
220
+ DOCK[Persistent Bottom Dock]
221
+ end
222
+
223
+ PAGE -->|webRequest events| SW
224
+ PAGE -->|Fetch/DOM Overrides| MWH
225
+ MWH -->|postMessage| SW
226
+ CDP -->|CDP Network events| SW
227
+ SW --> REG
228
+ REG -->|Persist Tab Registry| LOCAL
229
+ SW -->|Capture State| SESSION
230
+ LOCAL -->|storage.onChanged| APP
231
+ SESSION -->|storage.onChanged| APP
232
+ APP --> LIST
233
+ APP --> DOCK
234
+ DOCK -->|Action Dispatch| DL
235
+ ```
236
+
237
+ For complete technical specifications, see [ARCHITECTURE.md](ARCHITECTURE.md).
238
+
239
+ ---
240
+
241
+ ## ❓ Frequently Asked Questions (FAQ)
242
+
243
+ > [!TIP]
244
+ > Need help or found an issue? Check our [SUPPORT.md](SUPPORT.md) guide or submit a [Bug Report](https://github.com/imsovikde/source-grab/issues/new?template=bug_report.md).
245
+
246
+ ### Q: Why does `npm install` fail with 404 Not Found?
247
+ **A:** By default, `npm` queries `https://registry.npmjs.org`. Because `@imsovikde/source-grab` is hosted on **GitHub Packages (`https://npm.pkg.github.com`)**, you must configure `.npmrc` with `@imsovikde:registry=https://npm.pkg.github.com` before running `npm install`.
248
+
249
+ ### Q: Why does SourceGrab request Debugger permissions?
250
+ **A:** The Chrome Debugger permission enables **Chrome DevTools Protocol (CDP)** deep scanning. This allows SourceGrab to inspect data URLs, inline Blobs, Web Worker responses, and stream buffers that standard extension network listeners miss.
251
+
252
+ ### Q: Why doesn't SourceGrab capture files on `chrome://` pages?
253
+ **A:** Google Chrome security policies strictly restrict extensions from injecting scripts or attaching debuggers to internal system pages (`chrome://`, `chrome-extension://`, or the Chrome Web Store).
254
+
255
+ ### Q: Are my captured files or browsing data sent to an external server?
256
+ **A:** **No.** SourceGrab runs 100% locally within your Chrome browser. It contains zero analytics, tracking scripts, or external network requests.
257
+
258
+ ### Q: How does the ZIP download work?
259
+ **A:** SourceGrab compiles selected resource buffers into an uncompressed/STORE ZIP file in browser memory using a custom binary encoder, triggering a single `.zip` file save dialog.
260
+
261
+ ---
262
+
263
+ ## 🛡️ Native GitHub Community & Documentation Index
264
+
265
+ - 🤖 **[AGENTS.md](AGENTS.md)** — AI coding agent development guidelines.
266
+ - 📐 **[ARCHITECTURE.md](ARCHITECTURE.md)** — System architecture & data pipeline specs.
267
+ - 🤝 **[CONTRIBUTING.md](CONTRIBUTING.md)** — Contribution protocols & standards.
268
+ - ⚖️ **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)** — Community standards (Contributor Covenant v2.1).
269
+ - 🛡️ **[SECURITY.md](SECURITY.md)** — Security policies & vulnerability reporting.
270
+ - 🛠️ **[SUPPORT.md](SUPPORT.md)** — User help center & FAQs.
271
+ - 👥 **[GOVERNANCE.md](GOVERNANCE.md)** — Maintainer roles & RFC process.
272
+ - 📜 **[LICENSE](LICENSE)** — Permissive MIT License.
273
+ - 📋 **[CHANGELOG.md](CHANGELOG.md)** — Complete version release history.
274
+ - 📄 **[PRD.md](PRD.md)** — Product Requirements Document.
275
+ - 📄 **[TRD.md](TRD.md)** — Technical Requirements Document.
276
+
277
+ ---
278
+
279
+ <div align="center">
280
+ <sub>Built with ❤️ by <a href="https://github.com/imsovikde">Souvik Dey</a></sub>
281
+ </div>
@@ -0,0 +1,306 @@
1
+ // background/capture-registry.js
2
+ // v1.1 — Uses chrome.storage.local for persistent registry data
3
+
4
+ export class CaptureRegistry {
5
+ constructor() {
6
+ this.memoryCache = new Map(); // Map<tabId, { resources: Map<normalizedUrl, ResourceEntry>, nextIndex: number }>
7
+ this.writeTimeouts = new Map();
8
+ this.pendingFlush = new Set(); // Track tabs that need flushing
9
+ }
10
+
11
+ async init() {
12
+ try {
13
+ const data = await chrome.storage.local.get(null);
14
+ for (const [key, value] of Object.entries(data)) {
15
+ if (key.startsWith('registry_')) {
16
+ const tabId = parseInt(key.replace('registry_', ''), 10);
17
+ const resources = new Map();
18
+ if (value && value.resources) {
19
+ for (const [url, entry] of Object.entries(value.resources)) {
20
+ resources.set(url, entry);
21
+ }
22
+ }
23
+ this.memoryCache.set(tabId, { resources, nextIndex: value.nextIndex || 0 });
24
+ }
25
+ }
26
+ } catch (e) {
27
+ console.error('Failed to init CaptureRegistry:', e);
28
+ }
29
+ }
30
+
31
+ normalizeUrl(url) {
32
+ try {
33
+ // Skip data: and blob: URLs — just return as-is
34
+ if (url.startsWith('data:') || url.startsWith('blob:')) return url;
35
+
36
+ const parsed = new URL(url);
37
+ parsed.hash = '';
38
+
39
+ const ignoreParams = ['_t', '_ts', 'timestamp', 'v', 'ver', '_v', 'cb', 'cache', '_', '_=', 't', 'nocache', 'rand', 'random'];
40
+ const params = Array.from(parsed.searchParams.keys());
41
+ for (const p of params) {
42
+ if (ignoreParams.includes(p.toLowerCase())) {
43
+ parsed.searchParams.delete(p);
44
+ }
45
+ }
46
+
47
+ const sortedParams = Array.from(parsed.searchParams.entries()).sort((a, b) => a[0].localeCompare(b[0]));
48
+ parsed.search = new URLSearchParams(sortedParams).toString();
49
+ parsed.hostname = parsed.hostname.toLowerCase();
50
+
51
+ let result = parsed.toString();
52
+ if (result.endsWith('/') && parsed.pathname !== '/') {
53
+ result = result.slice(0, -1);
54
+ }
55
+ return result;
56
+ } catch (e) {
57
+ return url;
58
+ }
59
+ }
60
+
61
+ extractFilename(url) {
62
+ try {
63
+ // Handle data: URLs
64
+ if (url.startsWith('data:')) {
65
+ const mimeMatch = url.match(/^data:([^;,]+)/);
66
+ const mime = mimeMatch ? mimeMatch[1] : 'application/octet-stream';
67
+ const extMap = {
68
+ 'image/svg+xml': 'inline.svg', 'image/png': 'inline.png',
69
+ 'image/jpeg': 'inline.jpg', 'image/gif': 'inline.gif',
70
+ 'image/webp': 'inline.webp', 'application/pdf': 'inline.pdf',
71
+ 'text/css': 'inline.css', 'text/javascript': 'inline.js',
72
+ 'application/javascript': 'inline.js'
73
+ };
74
+ return extMap[mime] || 'inline-data';
75
+ }
76
+
77
+ const parsed = new URL(url);
78
+ let pathname = parsed.pathname;
79
+ if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
80
+ const parts = pathname.split('/');
81
+ let lastPart = parts[parts.length - 1];
82
+ if (!lastPart) return 'index';
83
+ return decodeURIComponent(lastPart);
84
+ } catch (e) {
85
+ return 'unknown';
86
+ }
87
+ }
88
+
89
+ extractExtension(filename) {
90
+ // Strip query string remnants from filename
91
+ const clean = filename.split('?')[0].split('#')[0];
92
+ const parts = clean.split('.');
93
+ if (parts.length > 1) {
94
+ return parts[parts.length - 1].toLowerCase();
95
+ }
96
+ return '';
97
+ }
98
+
99
+ categorize(url, mimeType, resourceType) {
100
+ const filename = this.extractFilename(url);
101
+ const ext = this.extractExtension(filename);
102
+
103
+ // Priority 1: Extension-based
104
+ if (ext === 'svg' || mimeType?.includes('image/svg+xml')) return 'svg';
105
+ if (ext === 'pdf' || mimeType?.includes('application/pdf')) return 'pdf';
106
+
107
+ const images = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'ico', 'bmp', 'tiff', 'tif', 'heic', 'heif', 'jxl'];
108
+ if (images.includes(ext) || (mimeType?.startsWith('image/') && !mimeType?.includes('svg'))) return 'images';
109
+
110
+ const scripts = ['js', 'mjs', 'cjs', 'jsx', 'ts', 'tsx'];
111
+ if (scripts.includes(ext) || mimeType?.includes('javascript') || mimeType?.includes('ecmascript')) return 'scripts';
112
+
113
+ if (ext === 'css' || mimeType?.includes('text/css')) return 'stylesheets';
114
+
115
+ const fonts = ['woff', 'woff2', 'ttf', 'otf', 'eot'];
116
+ if (fonts.includes(ext) || mimeType?.includes('font/') || mimeType?.includes('application/font')) return 'fonts';
117
+
118
+ const media = ['mp4', 'webm', 'ogg', 'ogv', 'mp3', 'wav', 'flac', 'aac', 'm4a', 'avi', 'mov', 'mkv', 'm3u8'];
119
+ if (media.includes(ext) || mimeType?.startsWith('video/') || mimeType?.startsWith('audio/')) return 'media';
120
+
121
+ const documents = ['html', 'htm', 'json', 'xml', 'xhtml', 'txt', 'csv', 'md', 'yaml', 'yml', 'toml'];
122
+ if (documents.includes(ext) || mimeType?.includes('text/html') || mimeType?.includes('application/json') || mimeType?.includes('text/xml') || mimeType?.includes('text/plain')) return 'documents';
123
+
124
+ // Priority 2: webRequest resourceType fallback
125
+ if (resourceType) {
126
+ if (resourceType === 'image') return 'images';
127
+ if (resourceType === 'script') return 'scripts';
128
+ if (resourceType === 'stylesheet') return 'stylesheets';
129
+ if (resourceType === 'font') return 'fonts';
130
+ if (resourceType === 'media') return 'media';
131
+ if (resourceType === 'main_frame' || resourceType === 'sub_frame') return 'documents';
132
+ }
133
+
134
+ return 'other';
135
+ }
136
+
137
+ async upsert(tabId, partialData) {
138
+ if (!partialData.url) return;
139
+
140
+ // Skip chrome-extension:// and chrome:// URLs
141
+ if (partialData.url.startsWith('chrome-extension://') || partialData.url.startsWith('chrome://')) return;
142
+
143
+ if (!this.memoryCache.has(tabId)) {
144
+ this.memoryCache.set(tabId, { resources: new Map(), nextIndex: 0 });
145
+ }
146
+
147
+ const tabState = this.memoryCache.get(tabId);
148
+ const normalizedUrl = this.normalizeUrl(partialData.url);
149
+
150
+ let entry = tabState.resources.get(normalizedUrl);
151
+ if (!entry) {
152
+ const filename = partialData.filename || this.extractFilename(partialData.url);
153
+ entry = {
154
+ url: normalizedUrl,
155
+ originalUrl: partialData.url,
156
+ filename,
157
+ extension: this.extractExtension(filename),
158
+ category: 'other',
159
+ mimeType: null,
160
+ size: null,
161
+ statusCode: null,
162
+ timestamp: Date.now(),
163
+ source: 'other',
164
+ domContext: null,
165
+ status: 'requested',
166
+ downloadState: 'none',
167
+ isNew: true,
168
+ index: tabState.nextIndex++,
169
+ requestId: null,
170
+ frameId: null
171
+ };
172
+ tabState.resources.set(normalizedUrl, entry);
173
+ }
174
+
175
+ // Merge partial data, preserving originalUrl
176
+ const oldOriginalUrl = entry.originalUrl;
177
+ Object.assign(entry, partialData, {
178
+ url: normalizedUrl,
179
+ originalUrl: oldOriginalUrl || partialData.url
180
+ });
181
+
182
+ // Re-derive filename and category after merge
183
+ if (!entry.filename || entry.filename === 'unknown') {
184
+ entry.filename = this.extractFilename(entry.originalUrl);
185
+ entry.extension = this.extractExtension(entry.filename);
186
+ }
187
+ entry.category = this.categorize(entry.originalUrl, entry.mimeType, entry.resourceType);
188
+
189
+ this.debouncedSave(tabId);
190
+ }
191
+
192
+ debouncedSave(tabId) {
193
+ this.pendingFlush.add(tabId);
194
+ if (this.writeTimeouts.has(tabId)) return;
195
+
196
+ this.writeTimeouts.set(tabId, setTimeout(async () => {
197
+ this.writeTimeouts.delete(tabId);
198
+ this.pendingFlush.delete(tabId);
199
+ await this.flushTab(tabId);
200
+ }, 100));
201
+ }
202
+
203
+ async flushTab(tabId) {
204
+ const tabState = this.memoryCache.get(tabId);
205
+ if (!tabState) return;
206
+ const obj = Object.fromEntries(tabState.resources);
207
+ const dataToSave = {
208
+ [`registry_${tabId}`]: { resources: obj, nextIndex: tabState.nextIndex }
209
+ };
210
+ try {
211
+ await chrome.storage.local.set(dataToSave);
212
+ } catch (e) {
213
+ console.error('CaptureRegistry save error:', e);
214
+ }
215
+ }
216
+
217
+ // Flush all pending writes immediately (called on service worker suspend)
218
+ async flushAll() {
219
+ for (const tabId of this.pendingFlush) {
220
+ if (this.writeTimeouts.has(tabId)) {
221
+ clearTimeout(this.writeTimeouts.get(tabId));
222
+ this.writeTimeouts.delete(tabId);
223
+ }
224
+ await this.flushTab(tabId);
225
+ }
226
+ this.pendingFlush.clear();
227
+ }
228
+
229
+ async getAll(tabId) {
230
+ const tabState = this.memoryCache.get(tabId);
231
+ if (!tabState) return [];
232
+ return Array.from(tabState.resources.values()).sort((a, b) => a.index - b.index);
233
+ }
234
+
235
+ async getStats(tabId) {
236
+ const entries = await this.getAll(tabId);
237
+ const stats = {
238
+ total: entries.length,
239
+ byCategory: { images: 0, svg: 0, pdf: 0, scripts: 0, stylesheets: 0, fonts: 0, media: 0, documents: 0, other: 0 },
240
+ newSinceLastBatch: 0
241
+ };
242
+
243
+ for (const entry of entries) {
244
+ if (stats.byCategory[entry.category] !== undefined) {
245
+ stats.byCategory[entry.category]++;
246
+ } else {
247
+ stats.byCategory.other++;
248
+ }
249
+ if (entry.isNew) {
250
+ stats.newSinceLastBatch++;
251
+ }
252
+ }
253
+
254
+ return stats;
255
+ }
256
+
257
+ async markBatchDownloaded(tabId, urls, batchId) {
258
+ const tabState = this.memoryCache.get(tabId);
259
+ if (!tabState) return;
260
+
261
+ for (const u of urls) {
262
+ const normalized = this.normalizeUrl(u);
263
+ const entry = tabState.resources.get(normalized);
264
+ if (entry) {
265
+ entry.downloadState = 'completed';
266
+ entry.isNew = false;
267
+ }
268
+ }
269
+ this.debouncedSave(tabId);
270
+ }
271
+
272
+ async clear(tabId) {
273
+ this.memoryCache.delete(tabId);
274
+ if (this.writeTimeouts.has(tabId)) {
275
+ clearTimeout(this.writeTimeouts.get(tabId));
276
+ this.writeTimeouts.delete(tabId);
277
+ }
278
+ this.pendingFlush.delete(tabId);
279
+ await chrome.storage.local.remove(`registry_${tabId}`);
280
+ }
281
+
282
+ // Clear ALL registries (browser close cleanup)
283
+ async clearAll() {
284
+ const data = await chrome.storage.local.get(null);
285
+ const keysToRemove = Object.keys(data).filter(k => k.startsWith('registry_'));
286
+ if (keysToRemove.length > 0) {
287
+ await chrome.storage.local.remove(keysToRemove);
288
+ }
289
+ this.memoryCache.clear();
290
+ for (const [tabId, timeout] of this.writeTimeouts) {
291
+ clearTimeout(timeout);
292
+ }
293
+ this.writeTimeouts.clear();
294
+ this.pendingFlush.clear();
295
+ }
296
+
297
+ async addNavigationMarker(tabId, url) {
298
+ await this.upsert(tabId, {
299
+ url: url,
300
+ mimeType: 'text/html',
301
+ source: 'webRequest',
302
+ status: 'completed',
303
+ filename: this.extractFilename(url) || 'index.html',
304
+ });
305
+ }
306
+ }